I'm referring to the testing assertion library : http://chaijs.com/api/bdd/#false
You can write language chain assertions like the following:
expect(false).to.be.false;
expect() is obviously a global function, "to.be" looks like two properties, but how does the last part "false" work. I'm expecting that it would have to be a function call:
expect(false).to.be.false();
Is this 2015 ES syntax? I can't seem to find a reference to it in https://github.com/lukehoban/es6features
Stack Overflow says its not possible: How to implement optional parentheses during function call? (function overloading)
Can anyone shed some light on how something like this is implemented ?
Source Code: https://github.com/chaijs/chai/blob/master/lib/chai/core/assertions.js#L281
You can do this (and a lot of other stuff) with Object.defineProperty. Here's a basic example:
// our "constructor" takes some value we want to test
var Test = function (value) {
// create our object
var testObj = {};
// give it a property called "false"
Object.defineProperty(testObj, 'false', {
// the "get" function decides what is returned
// when the `false` property is retrieved
get: function () {
return !value;
}
});
// return our object
return testObj;
};
var f1 = Test(false);
console.log(f1.false); // true
var f2 = Test("other");
console.log(f2.false); // false
There's a lot more you can do with Object.defineProperty. You should check out the MDN docs for Object.defineProperty for detail.
Related
I'm currently learning and educating myself on javascript, i just found one simple code which is using this '()();' what is this called, didnt find much information about it, what is it and how it is used, here the code i found :
'use strict';
let obj, method;
obj = {
go: function() { alert(this); }
};
obj.go();
(obj.go)();
(method = obj.go)();
(obj.go || obj.stop)();
sory english is not my mother language if some mistake.
Used on their own, parenthesis are grouping operators. They group expressions to control the order or precedence of the evaluation. You can read MDN here about it.
// example // is the same as
(obj.go)(); obj.go();
(method = obj.go)(); method = obj.go; method();
(obj.go || obj.stop)(); // calling go or stop after assinging to a temp variable
That piece of code is demonstrating how this is bound within a function execution content (in this case in go). It shows that simply putting parentheses around a method does not alter that behaviour: this is still bound to obj.
As soon as the parentheses surround an expression involving operator(s) the situation changes, and the method that results from the expression is called without a specific this binding: the default applies (undefined).
Another variant is the following:
(0,obj.go)();
Here the comma-operator kicks in, and so we are in the expression case: this is no longer bound to obj in the method call.
It just controls the order of the execution. You could write everything like this too, to make it more clear:
// (obj.go)();
obj.go();
// (method = obj.go)();
method = obj.go;
method();
// (obj.go || obj.stop)();
var method = obj.go || obj.stop;
method();
I'm doing precourse work for a JavaScript-based programming school and I've run into a problem. The
assignment is to rewrite some underscore.js methods from scratch so we know how they work, rather than just relying on them blindly. My _.invoke will pass functions refs but not method names.
Here's the original problem:
// Calls the method named by functionOrKey on each value in the list.
// Note: you will nead to learn a bit about .apply to complete this.
_.invoke = function(collection, functionOrKey, args) {
};
My solution so far using _.map() I wrote previously (which passed its own tests):
_.invoke = function(collection, functionOrKey, args) {
return _.map(collection, function(value) {
return functionOrKey.apply(value, args);
});
};
My solution will support passing a function for functionOrKey. For example (from the Mocha Test Suite):
var reverse = function(){
return this.split('').reverse().join('');
};
var reversedStrings = _.invoke(['dog', 'cat'], reverse);
reseversedStrings = "['god', tac']; //YAY!!
However, when it comes to passing a method, such as toUpperCase, I get the error message: "TypeError: undefined is not a function". Any suggestions appreciated!
EDIT: Found the failing test:
var upperCasedStrings = _.invoke(['dog', 'cat'], 'toUpperCase');
expect(upperCasedStrings).to.eql(['DOG', 'CAT']);
My solution would be:
_.invoke = function(collection, functionOrKey, args) {
//invoke when provided a method name
if(typeof functionOrKey === 'string'){
return _.map(collection, function(val){
return val[functionOrKey](args);
});
}
//invoke when provided a function reference
return _.map(collection, function(val){
return functionOrKey.apply(val, args);
});
};
However, when it comes to passing a method, such as toUpperCase, I get the error message: "TypeError: undefined is not a function"
Well, you're not passing a "method". See #elclarns' answer for how to pass a bound function.
What you are passing is a property name, which is a string - and when functionOrKey is the string 'toUpperCase' it does not have an apply method. You will need to check the type of that parameter, and act accordingly. When it's a string, you will want to use
return value[key].apply(value, args);
in the map callback.
Btw, your args parameter shouldn't be an array, but should be built from the dynamic arguments object. If you want to "cheat" (or see the full solution), have a look at the annotated source code of Underscore.
I'm not sure I fully understand your problem, but maybe it can be simpler. If you want to call a method and invoke it for each item in the array, you could simply use the builtin map like this:
var invoke = Function.bind.bind(Function.call)
var reverse = function() {
return this.split('').reverse().join('')
}
var result = ['dog', 'cat'].map(invoke(reverse)) //=> ['god', 'tac']
You can also use it with builtin methods:
['dog', 'cat'].map(invoke(''.toUpperCase)) //=> ['DOG', 'CAT']
I think this solves your particular issue, but invoke doesn't forward any additional arguments to the function it calls, as per Underscore documentation. In that case you can try to use what you've got so far plus the above helper, and capture any additional arguments.
Since functions are first-class objects, and can be passed inside of another js object, how can I do an assert in my tests to be sure I'm getting back the right function?
I'm using Q for promises, and mocha/chai/chai-as-promised for testing. My method returns different functions based on the if/else (I need to either redirect or use a different route).
I'll return something like:
fulfill({next: function(res) {return res.render('single-pages/site-map');}});
and my test looks like:
return assert.becomes(
page.helpers.checkIfSinglePage('site-map', null),
{next: function(res) {return res.render('single-pages/site-map');}}
);
but it's telling me that the returned values are not the same.
AssertionError: expected { next: [Function] } to deeply equal { next: [Function] }
Functions are compared by reference in JavaScript.
(function(){}) === (function(){}); // false
In fact, this is because functions are objects. At the moment (until ES7) everything in JavaScript except primitive value types (number, string, null, undefined, bool) is a reference and compares with reference equality checks.
You technically can check the two functions as two strings (comparing the code) and (assuming no old versions of firefox) it will compare equal for the same function - but that's a poor indication since two functions can mean opposite things:
var foo = (function(){
x = alert;
return function foo(){ x(); } // first function
})();
var bar = (function(){
x = console.log.bind(console,"BAR");
return function foo(){ x(); } // first function
})();
foo.toString() === bar.toString(); // true, but an incorrect check.
So to conclude, there is no way to know in JavaScript if two do the same without having a reference to them.
Instead, you can call .next and check that the rendered valuer is the same.
Here is the situation: I have checking on existing class like:
('Promise' in window) // true/false`
And I wanna force return false or true on it, can I do it?
Yes, I can check it by some other way, like `
window.Promise = undefined;
window.Promise === undefined;
Or something like this, but can I somehow delete this object or simulate something for 'in' operator?
I check specification and v8 code, there is 'in' operator just call 'HasProperty' operator, which realization on c++.. I know 'hack' with faking toString/valueOf methods:
obj = {
toString: function(){ return 'myName'; }
},
obj2 = {};
obj2[obj] = 1; // Object {myName: 1}
May be I can use it in some way? But, as I send string 'Promise' I can't just fake it like this way.. may be exist some way to fake 'HasProperty'?
I like how Ruby's .tap method works. It lets you tap into any method chain without breaking the chain. I lets you operate an object then returns the object so that the methods chain can go on as normal. For example if you have foo = "foobar".upcase.reverse, you can do:
"foo = foobar".upcase.tap{|s| print s}.reverse
It will print the upcased (but not reversed) string and proceed with the reversing and assignment just like the original line.
I would like to have a similar function in JS that would serve a single purpose: log the object to console.
I tried this:
Object.prototype.foo = function() {
console.log(this);
return this;
};
Generally, it works (though it outputs Number objects for numbers rather than their numeric values).
But when i use some jQuery along with this, it breaks jQuery and stops all further code execution on the page.
Errors are like this:
Uncaught TypeError: Object foo has no method 'push'
Uncaught TypeError: Object function () { window.runnerWindow.proxyConsole.log( "foo" ); } has no method 'exec'
Here's a test case: http://jsbin.com/oFUvehAR/2/edit (uncomment the first line to see it break).
So i guess that it's not safe to mess with objects' prototypes.
Then, what is the correct way to do what i want? A function that logs current object to console and returns the object so that the chain can continue. For primitives, it should log their values rather than just objects.
You correctly figured out how a method can be safely added anywhere in a chain, but your adding it to the Object.prototype is intrusive and can break code easily. Looks like jQuery code is the one that breaks for you.
A much safer way is:
Object.defineProperty(Object.prototype, 'foo', {
value : function() { console.log( "foo" ); return this; },
enumerable : false
});
DEMO: http://jsbin.com/oFUvehAR/7/edit
Finally, something generic could look like this:
Object.defineProperty(Object.prototype, 'tap', {
value : function(intercept) {
intercept.call(this);
return this;
},
enumerable : false
});
// Usage:
var x = { a:1 };
x.tap(function(){ console.log(this); });
As for the primitives part of your question, that is a bit trickier. When you call the tap method on a primitive, an Object wrapper is created and the tap method is called on it. The primitive value is still available, via the valueOf() method of that Object wrapper, so you could log it. The tricky part is that you have no way of knowing if the "thing" that you wanted to call the tap method on was initially a primitive or an Object wrapper. Assuming you never want to work with Object wrappers (that is quite reasonable), you could do the better tap method posted below.
Object.defineProperty(Object.prototype, 'tap', {
value : function(intercept) {
var val = (this instanceof Number || this instanceof String || this instanceof Boolean) ? this.valueOf() : this;
intercept(val);
return val;
},
enumerable : false
});
var log = console.log.bind(console);
var x = { a : 1 };
x.tap(log);
2.0.tap(log);
Notice that while in the first version of the tap function, the function passed to it had the useful information in this, in the second version it is mandatory to pass it as a parameter.
If you want a specialized logger for it, you can do something like this:
Object.defineProperty(Object.prototype, 'log', {
value : function(){
return this.tap(console.log.bind(console));
},
enumerable : false,
writable : true /* You want to allow objects to overwrite the log method */
});