The Cross Language Ambiguity
In JavaScript, the in operator checks if a property is present where the property is the name rather than its value.
"name" in {name:"WebReflection"}; // true
However, I bet at least once in our JS programming life we have done something like this, expecting a true rather than false.
4 in [3, 4, 5]; // false
// Array [3, 4, 5] has no *index* 4
The Python Way
In Python, as example, last operation is perfectly valid!The behavior of Python in operator is indeed more friendly in certain situations.
"b" in ("a", "b", "c") #True
4 in [3, 4, 5] # True
value in VS value.in()
What if we pollute the Object.prototype with an in method that is not enumerable and sealed? No for/in loops problems, neither cross browsers issues since if it's possible in our target environment, we do it, otherwise we don't do it ... a fair compromise?Rules
- if the target object is an Array, check if value is contained in the Array ( equivalent of -1 < target.indexOf(value) )
- if the target object is an Object, check if value is contained in one of its properties ( equivalent of for/in { if(object[key] === value) return true; } ).
I don't think nested objects should be checked as well and right now these are not ( same as native Array#indexOf ... if it's an array of arrays internal arrays values are ignored and that's how it should be for consistency reason ) - if the target object is a typeof "string", check if value is a subset of the target ( equivalent of -1 < target.indexOf(value) ).
The Python logic on empty string is preserved since in JavaScript "whateverStringEvenEmpty".indexOf("") is always 0 - if the target property is a typeof "number", check if value is a divisor of the target ( as example, (3).in(15) === true since 15 can be divided by 3)
Compatibility
The "curious fact" is that in ES5 there is no restrictions on an IdentifierName.This is indeed different from an Identifier, where in latter ReservedWord is not allowed.
"... bla, bla bla ..." ... right, the human friendly version of what I've just said is that obj.in, obj.class, obj.for etc etc are all accepted identifiers names.
Accordingly, to understand if the current browser is ES5 specs compliant we could do something like this:
Back in topic, IE9 and updated Chrome, Firefox, Webkit, or Safari are all compatible with this syntax.
try {
var ES5 = !!Function("[].try");
} catch(ES5) {
ES5 = !ES5;
}
alert(ES5); // true or false
New Possibilities
Do you like Ruby syntax?
// Ruby like instances creation (safe version)
Function.prototype.new = function (
anonymous, // recycled function
instance, // created instance
result // did you know if you
// use "new function"
// and "function" returns
// an object the created
// instance is lost
// and RAM/CPU polluted
// for no reason?
// don't "new" if not necessary!
) {
return function factory() {
// assign prototype
anonymous.prototype = this.prototype;
// create the instance inheriting prototype
instance = new anonymous;
// call the constructor
result = this.apply(instance, arguments);
// if constructor returned an object
return typeof result == "object" ?
// return it or return instance
// if result is null
result || instance
:
// return instance
// in all other cases
instance
;
};
}(function(){});
// example
function Person(name) {
this.name = name;
}
var me = Person.new("WebReflection");
alert([
me instanceof Person, // true
me.name === "WebReflection" // true
].join("\n"));
Details on bad usage of new a part, this is actually how I would use this method to boost up performances.
// Ruby like instances creation (fast version)
Function.prototype.new = function (anonymous, instance) {
return function factory() {
anonymous.prototype = this.prototype;
instance = new anonymous;
this.apply(instance, arguments);
return instance;
};
}(function(){});
cool?
Do Not Pollute Native Prototypes
It does not matter how cool and "how much it makes sense", it is always considered a bad practices to pollute global, native, constructors prototypes.However, since in ES5 we have new possibilities, I would say that if everybody agrees on some specific case ... why not?
for/in loops can now be safe and some ReservedWord can be the most semantic name to represent a procedure as demonstrated in this post.
Another quick example?
// after Function.prototype.new
// after Person function declaration
Object.defineProperty(Object.prototype, "class", {
enumerable: !1,
configurable: !1,
get: function () {
return this.__proto__.constructor;
}
});
var me = Person.new("WebReflection");
// create instance out of an instance
var you = me.class.new("Developer");
alert([
you instanceof Person, // true
you.name === "Developer" // true
].join("\n"));
I can already see Prototype3000 farmework coming out with all these magic tricks in place :D
Have fun with ES5 ;)
I really miss that Python's functionality in JS :)
ReplyDeleteI really wish you'd take a loot at your layout in Chrome. I hate that I need to open up another browser just to read this blog.
ReplyDeleteScreenshot: http://i5.aijaa.com/b/00627/8541731.jpg
I don't have any problem but styles are from the old version of blogger, I did not tough them
ReplyDeleteGood ole Works On My Computer -syndrome.
ReplyDeleteIf it helps, for some reason everything is scaled down by 50%. I've been seeing this for quite a while now, and I'm quite sure I've also seen someone else mention this. I'm using the latest Chrome (not Chromium).
ReplyDeleteare you sure is not that you pressed by mistake zoom out ? Is everything ok with zoom 100% ? ...
ReplyDeleteOh, that was embarrassing. Sorry for the trouble. As usual, it was just another case of PEBKAC.
ReplyDeleteBut in my defense, never before have I seen a (browser) zoom state persist like this.