A function to tap into any methods chain - javascript

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 */
});

Related

JavaScript Object and Primitive

Please I want someone to explain the code below for me:
var f = new Number(44);
f.name = "Yusuf";
f.hello = function() {
console.log("Hello");
};
console.log(typeof f);
f.hello();
console.log(f.name);
console.log(f.toString() + "good");
console.log(Object.prototype.hasOwnProperty(name));
console.log(f.hasOwnProperty(hello));
console.log(f.length);
When I check the variable type. Object gets return and I am sure this is because of the Number object constructor call function. I added two properties, one a member and a method and when I call them , it work but when I used hasOwnProperty(), false is return for the member key and undefined for the method key.
Why is it so?
where are the methods going to if the hasOwnProperty doesn't work as usual when it is supposed to when I am actually checking the property on the containing object.?
I checked Number and object object and they all return false.
The hasOwnProperty method takes the property key as a string:
console.log(Number.prototype.hasOwnProperty("name"));
console.log(Object.prototype.hasOwnProperty.call(f, "name"));
console.log(f.hasOwnProperty("name"));
console.log(f.hasOwnProperty("hello"));
I recommend to always "use strict" mode so that you get exceptions when you try to use undeclared variables.

Javascript: silently fail on undefined assignment

Suppose I am trying to assign a variable in a way that causes an exception
I am going to access a non-existent key of a dictionary:
myObject.property = dictionary['NO_KEY'][0];
Now, because 'NO_KEY' does not exist on dictionary, my program will catch an exception when trying to subscript 0 of undefined - and crash. Is it possible to execute this line above as a no-op so that my script can continue running? I know there is try-catch syntex, but is there a more elegant syntax with ESMA6?
You can use an if condition and statement, Object.hasOwnProperty() or as suggested by #Ryan in operator
if (dictionary.hasOwnProperty("NO_KEY")) {
myObject.property = dictionary["NO_KEY"][0];
}
if ("NO_KEY" in dictionary) {
myObject.property = dictionary["NO_KEY"][0];
}
Object.defineProperty(Object.prototype,
'accessWithSilentFail', {
configurable: false,
enumerable: false,
writable: false,
value: function(key) {
return this[key] ? this[key] : {};
}});
myObject.property = dictionary
.accessWithSilentFail('NO_KEY')
.accessWithSilentFail(0);
That way you get an empty object if at any point the chain fails. You need to get an object so the chain doesn't fail halfway. You can call the function something shorter if you're going to use it a lot.
Although this works, it has many, many limitations, and it changes the Object prototype, which is usually frowned upon. You really should consider just checking for undefined, which is the idiomatic way to do it.
If you ever need to check if the access chain failed, you can use:
function chainFailed(result) {
return Object.keys(result).length === 0;
}
So you could do
myObject.property = dictionary
.accessWithSilentFail('NO_KEY')
.accessWithSilentFail(0);
if (!chainFailed(myObject.property)) {
//keep on
} else {
//handle failure
}
This works as long as your expected return isn't an empty object, on which case chainFailed will always return true. But I'm assuming you really want to fail silently, because if you wanted to handle errors you could just use an exception.
Use ternary operator
myObject.property = dictionary['NO_KEY'] ? dictionary['NO_KEY'][0] : null;
While I believe this is a bad idea that will come back to bite you later, here is a solution for modern browsers using proxies. Yes, you are still checking for the property existence, but it is hidden from your code accessing the dictionary keys.
var dictionary = {a: 42};
dictionary = new Proxy(dictionary, {
get: (target, property) => {
if (target.hasOwnProperty(property)) {
return target[property];
}
return {};
}
});
// Existing properties are passed through unchanged
console.log(dictionary.a);
// Missing properties result in an empty object
console.log(dictionary.b);
// Original test
var lost = dictionary['NO_KEY'][0];
console.log(lost);

How does Chai JS make function parentheses optional?

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.

How to chain methods after already returning a value?

The following does work:
//Returns an object with an "id" of 1, which is inside another object, "collection".
//Then, calls .move(), which sets collection's property "coordinates" to {x: 20, y: 20}
collection.get(1).move(20,20);
console.log(collection.get(1).get('coordinates')); //returns { x: 20, y: 20 }
This works because the collection .get() method executes return this;
Therefore, that method is chainable.
However, this does not work:
collection.get(1) //returns the object inside "collection" that has an "id" of 1
.get('coordinates') //returns that object's current "coordinates" { x: 20, y: 20 }
.move(20,20); //error
This is because the "object with id: 1" .get() method looks similar to this:
Thing.prototype = {
id: null,
get: function(prop) {
if (prop == 'id') {
return this.id;
} else {
return this.attributes[prop];
}
}
}
(The id is set when an object Thing is initialized, by the way.) As you can see, this individual .get() method already returns a value (a certain property of its object).
Therefore, given the way the code is set up now, the method isn't chainable.
To kind of wrap it all up a little bit, my "collections" hold an array of "things". Each collection has a .get() method, which returns a thing from the collection's array of things. In addition, each thing has a .get() method, which returns a value from a specific property in that thing.
The issue I'm having is that if possible, I'd like to make the indiviual thing .get() method chainable... but, I can't call both of these lines of code in the same block:
return this[prop];
return this;
I've looked at, for example, a code excerpt from one of John Resig's slides:
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
which shows an example of returning a couple different things through the use of callback functions (I think).
I've tried several different ways of going about this, which didn't work, including returning an object of another method with a callback that returns the object.
Any pushes in the right direction would be appreciated.
A function can only return one thing. If it's purpose is to return data about the object, it can't also return the object itself. You can only chain methods that are used for their side effects or to return related collections.
If you only care about performing operations on sub-objects, you could look to jQuery's addBack and end for inspiration. That said, I'm not sure it's very good API design; the usage is much more clear if you just put an intermediate result in a variable and use multiple statements, even if that's not so ‘slick’.
The bottom line is that your function must return the object itself if you wish to keep the chain going; this is one way to do that:
get: function(prop, callback) {
if (prop == 'id') {
callback(this.id);
} else {
callback(this.attributes[prop]);
}
return this;
}
This makes it chainable again because the side effect of the data fetch is now isolated, but as a result the logic may look a bit funny:
collection.get(1)
.get('coordinates', function(value) {
// whatever
})
.move(20,20);

JavaScript Object

I can easily create and object in a lot of different. For example like this:
var myObject = {
myFunction: function () {
return "";
}
};
So if I alert this object I get something this:
[object Object]
Now I'm if I alert the NaN object I get this:
NaN
And I can still call function from it:
alert(NaN.hasOwnProperty().toString());
So my question is how can I create a object like the NaN object?
If I alert it, it should return something like myObject
I should be able to create and call functions on it like myObject.myFunction();
function MyObject(){}
MyObject.prototype = {
toString: function(){
return "myObject";
},
myFunction: function(){
}
};
var myObject = new MyObject();
alert( myObject );
//myObject
Only objects that are created using a constructor function get their class name displayed using console.log(), as shown in #Esalijia's answer.
Even then, it'll be the class name that's displayed, not the name of whatever variable you happened to assign the object to.
Adding a toString() method to the object will however change the output of the alert() function, since that always looks for the presence of that method and use it if it exists.
So, writing:
var myObj = {
toString: function() { return "boo"; }
}
alert(myObj)
will show boo in the alert box, instead of [Object object].
alert(NaN.hasOwnProperty().toString()); // says false.
alert(typeof NaN); //says it is a number
So, I don't think NaN is an Object and It's datatype is a number.
For more info: NaN means Not-a-Number. Even datatype is number, it is not a number. When any mathematical calculation can't return number,it returns NaN. The isNaN() function determines whether a value is an illegal number (Not-a-Number).This function returns true if the value is NaN, and false if not.
For example,
var num=35;
var num2=num/'asg';
console.log(num2); //returns NaN
NaN is a number primitive, not an object. Like all primitives, it is converted to an object when you use the . operator to access a property or method that exists on the creator class's prototype.
To display properties available for primitives, either convert them to an object, or use console.dir()
console.dir(NaN);
console.log(Object(NaN));
All primitives behave this way, when you call console.log on any of the following, they exhibit the same behaviour:
console.log("Hello");
console.log(1);
console.log(true);
Yet, you can access properties on them like normal objects:
"Hello".length;
1 .toString();
true.valueOf();

Categories

Resources