JavaScript
Is Not
Type-Safe
(num|string) + (num|string); // awesome sauce (maybe)
[] + []; // ???
[] + {}; // ???
[] + []; // ""
[] + {}; // "[object Object]"
If input is primitive:
return it as is
Otherwise,
input is an object:
Call obj.valueOf()
If that result
is a primitive:
return it
Otherwise
call obj.toString()
If the result
is a primitive:
return it
Otherwise:
throw a TypeError
[] + []; ""
[] + {}; "[object Object]"
var array = [];
array.valueOf() === array; // true, same for object
toString([]); // ""
toString({}); // "[object Object]"
It Actually
Gets Weirder
From Here
var myWeight = '190'; // were only wishing made that so
myNum + 50; // more like it
Implicit
String
Conversion
if(peaceTreaty === 'undefined') {
launchZeeMissiles();
}
Actually Reads
Pretty Cleanly
if(typeof peaceTreaty === 'undefined') {
launchZeeMissiles();
}
Distinguish
Primitives
From Each Other
And Objects
isNaN(NaN); // true
var mySoCalledNum = Number("90s ftw"); // returns NaN
// but not itself NaN
isNaN(mySoCalledNum); // true -> implicit conversion
var myBool = 'false';
myBool === true; // ???
myBool === false; // ???
var myBool = 'false';
myBool === true; // false
myBool === false; // false
var myBool = 'false';
if(myBool === false) { // typeof myBool === 'string'
partyInTheUSA();
} else {
theEndIsNigh = true;
}
Conversions
Can Happen
Unexpectedly
What You Need
To Know About
this
Reference to
a Given
Context
var obj = {
doAllTheThings: function() {
console.log(this);
}
};
obj.doAllTheThings() === obj; // ???
I am an object |
My name is 'obj' |
My prototype is Object |
I have one method |
^^ is located at some_hex_value |
my return address is … |
|
|
I am a function |
My name is 'doAllTheThings' |
My prototype is … |
I take zero arguments |
`this` is the memory address of obj |
my return address is … |
|
|
var obj = {
doAllTheThings: function() {
console.log(this);
}
};
obj.doAllTheThings() === obj; // true
function funkyFunc() {
console.log(this);
}
funkyFunc(); // ???
function somewhatFunkyFunc() {
'use strict';
console.log(this);
}
somewhatFunkyFunc(); // ???
No Reference
i.e. unbound
Sloppy Mode
or
Quirks Mode
function funkyFunc() {
console.log(this);
}
funkyFunc(); // window
function somewhatFunkyFunc() {
'use strict';
console.log(this);
}
somewhatFunkyFunc(); // undefined
funkFunc |
this? |
window, I guess |
|
somewhatFunkyFunc |
this? |
undefined |
|
var thisHuh;
function MyObject() {
thisHuh = this;
}
var instance = new MyObject();
console.log(thisHuh === instance); // ???
function newOperator(Constr, arrayWithArgs) {
var thisValue = Object.create(Constr.prototype);
Constr.apply(thisValue, arrayWithArgs);
return thisValue;
}
h/t Dr. Axel Rauschmayer, again
new Constr |
this is ^^^ |
arrayWithArgs |
|
|
|
|
|
|
Objects … |
have properties |
house methods |
hold references |
can vary in memory footprint |
(i.e. Array.prototype.push() ) |
|
|
|
A Stack Frame
Stack Frame |
Stack Frame |
Stack Frame |
Stack Frame |
Stack Frame |
Stack Frame |
Stack Frame |
Stack Frame |
Stack Frame |
Stack Level Too Deep
Stack Overflow
Consider Please
the Following
var smarm = {
aString: 'theory',
aNum: 42,
aBool: true,
doFunc: function () {
var a = 'bob';
console.log(this); // smarm
(function () {
console.log(this); // window - 'this' is reset
console.log(a); // 'bob' - still in scope
}());
}
};
smarm.doFunc();
Your Object: smarm at abc123 |
String at 123 |
Num at 456 |
Bool at 789 |
Object at 000 |
|
|
|
|
wide-angle
123 String: 'theory' |
456 Num: 42.0 |
789 Bool: true |
000 Object: doFunc |
smarm (this) is at abc123 |
|
|
|
|
A bit closer in
All reference smarm
var smarm = {
aString: 'theory',
aNum: 42,
aBool: true,
doFunc: function () {
var a = 'bob';
console.log(this); // smarm
(function () {
console.log(this); // window - 'this' is reset
console.log(a); // 'bob' - still in scope
}());
}
};
smarm.doFunc();
Object doFunc |
String at eee |
Object at fff |
smarm is at abc123 |
|
|
|
|
|
Even Closer
Still referencing smarm
var smarm = {
aString: 'theory',
aNum: 42,
aBool: true,
doFunc: function () {
var a = 'bob';
console.log(this); // smarm
(function () {
console.log(this); // window - 'this' is reset
console.log(a); // 'bob' - still in scope
}());
}
};
smarm.doFunc();
Anon. function at fff |
return address doFunc 000 |
this? |
this was reset |
|
|
|
|
|
|
Elbow-Deep
No reference to smarm
Without
Reference,
We Know Where
this Goes
A Quick Word
On Prototypes
var Developer = function() {
this.back = 'bad';
this.posture = 'worse';
this.drinkCoffee = function() {
alert('I am on top of the world');
};
};
new Developer |
String: back |
String: posture |
Object: drinkCoffee |
|
|
|
|
|
|
Repetition of
Function
Definition
Everytime
var Developer = function() {
this.back = 'bad';
this.posture = 'worse';
};
Developer.prototype.drinkCoffee = function() {
alert('I believe I can fly');
};
new Developer |
String: back |
String: posture |
|
Developer.prototype |
drinkCoffee is at some_hex |
|
|
|
Function
Defined
Once
in the
Prototype
References to
Related Objects
It's All
Gray and White
Lines