-
Because it has a humble origin... Designed in 10 days... was proclaimed death several times, and it was planned to be replaced by many other more "powerful" languages
-
Because it has closures and lambdas, and the prototypal inheritance (the good parts)
-
Because it is weird, in a very nice sense... (some bad parts... maybe?)
From the video WAT (https://www.destroyallsoftware.com/talks/wat)
a. Adding two arrays
[] + []
//Expected: well, runtime error???
//Result: "" ( empty string)
//But Why????
//this is the same as calling [].toString() + [].toString() which for arrays is the same as calling
//[].join() which on an empty array returns ""
b. Adding two objects (Actually this is not accurate. You could think the first one is an object literal but it is not)
{} + {}
//Expected: again, runtime error???
//Result: NaN
//But Why????
// Well the first {} is evaluated as an "empty block" which does nothing
// and the +{} will try to execute the unary operator + on an object literal
// which happens to return NaN
c. Adding and array + object
[] + {}
//Expected: again... I will expect a runtime error.
//Result [object Object]
//But Why????
// the + operator will try to convert the values to primitives first,
// if it cannot it will execute the toString so for [] the valueOf
// will be the array itself, not a primitive, then it will try with
// toString, which for arrays it is the same as Array.prototype.join
// and in that case... will return an empty string. the second one
// will do the same and will end executing the toString method of the empty
// object which is... [object Object]
d. Adding and object to an array
{} + []
//Expected: runtime error?
//Result: 0
//But Why???
//The first is an empty block, then the rest will be evaluated as +[]
//+[] === +""
//+"" === 0
// so that's why it is 0
e. unary operator
+[]
//Expected: runtime error?
//Result: 0
//But Why??
//Same as above explanation
+{}
//Expected: runtime error?
//Result: NaN
//But Why??
//Unary of an empty Object is NaN
+{ valueOf : function () { return 3; }};
//Result: 3
+{ toString : function () { return 5; }};
//Result: 5
f. the type of NaN (Not a Number) is a Number :)
typeof NaN === 'number'
// lol! I kinda enjoy this one!
g. you can mess with it in a badly badly way:
var b = new Number(1);
(b).valueOf();
//Expected: 1
//Result: 1
b = new Number(-1);
(b).valueOf()
//Expected: -1
//Result: -1
NaN.valueOf()
//Expected: runtime error???
//Result: NaN (spec says it)
So far so good... now some interesting...
var oldProto = Number.prototype.valueOf;
Number.prototype.valueOf = function () {
return oldProto.apply(this, arguments) * 2;
};
b = new Number(20);
b.valueOf()
//20? not... 40!
Number.prototype.valueOf = function () { return 10; }
var b = new Number(1);
b.valueOf()
// 1? not... 10!
b.toString()
// 10?? not... 1!
// You can do the same with all the objects in javascript...
// so that's why you should not mess with the prototypes...
// As a rule of thumb you only modify the objects that you own...
// objects that you own are the ones you create.
// Others objects should be treated as not yours, so don't mess with them!
// Note: This only affects the instances of Objects
// created with the Number constructors... it does not affect primitives.
h. the instanceof is kinda broken (I almost never used it... well, maybe once...)
var Person = function () {};
var p = new Person();
p instanceof Person // true
p instanceof Person === (p.constructor === Person)
p instanceof Window // false as expected...
//but what if I do...
//WARNING: non standard __proto__ property!!! but almost all host environments have it!
Person.prototype.__proto__ = Window.prototype;
//now...
p instanceof Window === true
//Even when this might look wrong is actually ok
i. typeof is broken...
typeof null === //object
typeof [] === //object
//better use
({}).toString.call(null) === //[Object Null]
//or even better
Object.prototype.toString.call(null) === //[Object Null]
f. arrays are fun!
var arr = [];
arr[1] = 10;
arr.length === 2 // true! the array looks like : [undefined, 10]
Object.keys(arr) // ["1"], note that there is no length property in the keys, nor the 0 index
arr[-1] = 20 // runtime error? range error?? nope... silently creates a key "-1"
arr.length === 2 // still true! the array looks like : [undefined, 10] // where is my -1 property???
Object.keys(arr) // ["1", "-1"], not 0 not length... but there is the -1
arr["10"] = 23; // the array looks like [undefined, 10, undefined x 8, 23]. Note the index was a string...
arr[00] = 2; // setting arr[00] it is the same as arr[0], the array is now [2, 10, undefined x 8, 23]
arr["00"] = 12; // so another string again...
// will this set the 0 index?? nope... the array is now [2, 10, undefined x 8, 23]
Object.keys(arr) // ["0", "1", "10", "-1", "00"]
arr["00 "] = 12; // note the extra space at the end.
Object.keys(arr) // ["0", "1", "10", "-1", "00", "00 "]
g. arrays cannot be subclased easily
http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
Now they can... here is my attempt...
function SubArray() {
this.push.apply(this, arguments);
//Array.apply(this, arguments);
}
SubArray.prototype = Object.create(Array.prototype, {
constructor : {
value : SubArray
},
lastElement : {
value : function() {
return this[this.length - 1];
}
},
clone : {
value : function () {
var s = Object.create(SubArray.prototype);
// note the comma operator
return s.push.apply(s, this), s;
}
}
});
var sub = new SubArray(1, 2, 3);
sub instanceof SubArray; //true
sub instanceof Array; //true
sub.lastElement() //3
h. Many Ways to make inheritance ala "Class based languages..."
- http://www.crockford.com/javascript/inheritance.html
- http://dean.edwards.name/base/Base.js
- http://www.uselesspickles.com/class_library/
- http://ejohn.org/blog/simple-javascript-inheritance/
- http://alexsexton.com/blog/2013/04/understanding-javascript-inheritance/
- https://github.com/dilvie/stampit
- https://github.com/rauschma/proto-js
Some of them, very complicated, maybe because they will trying to emulate all the features of a class based language...
I guess I have a simpler one... My own implementation... done 1.5 years ago, in the file z-oop.js
Now with Object.create, everything it is lot simpler...
Take a look to the SubArray example above.
So as a conclusion...
Javascript is weird... well that's not true. It is not even fair to say it. It is weird because most of the time we don't read the spec... So here is the spec in case you feel curious http://www.ecma-international.org/ecma-262/5.1/.