#Weird Part Of JS
-
this
is global object if there does not have receiver. If so, it also imply it is awindow
object,document === window.document
. -
JS engine create
this
execution context(window object) in bottom of execution stack and run other execution context which is in top of it for you. -
When you declare a
var
or define as a function(function expression or function statement), you can find it throughwindow.func()
orfunc()
in global environment. -
Hoisting:
var
andfunc
are all firstly initialize but not assigned its value yet until the later code assign it. Check code example below.// case1 b(); console.log(a); // prints undefined. // var a has been initialized but not assigned yet. // the later code var a = 5; will assign its value. var a = 5; function b(){ console.log("func b"); } // above code is equivalent to: // Code is parsed line by line. // Hoisting first => set up memory space for var and func // all var initially set to undefined. // case 2 function b(){ console.log("func b"); } var a; // start execute the code. b(); // undefined console.log(a); a = 5; // Hoisting only load function code to memory, // so k() does not need to know b() exist or not, just load it. // It will print out "b" k(); function k(){ b(); } function b(){ console.log("b"); }
-
Global execution context will load at first, once create phase has done and start execution phase. so the execution stack will clear once all code runs in global has finished.
-
var
environment will seperately stay with each execution context in execution stack. -
Scope: outer execution context is usually global. If a fucntion define in global, it will set
this
to global which implies thatthis
point to a function's outer execution context. -
Scope chain: The outer reference of execution context will based on where the function code defined in lexical env. it will go all way up until to global execution context. If the outer execution context cannot be not found, then thorw error.
//case 1 function b(){ console.log(myVar); console.log(this); } function a(){ var myVar = 2; b(); j.myVar(); } var myVar = 5; var j = { myVar: function(){ console.log(myVar); } }; a(); // case 2 function a(){ // set b's lexical env to a function b(){ console.log(myVar); // closure will set this to window obj. console.log(this); } var myVar = 2; // without above code, myVar will be found in global (5) b(); } var myVar = 5; a();
-
Only for function,
this
keyword refer to its receiver. if it does not have receiver, thenthis
refers to global object(window) in default mode. if no receiver in strict mode, thenthis
refers toundefined
. -
For object, when a function is called as a method of an object,
this
sets to object instead global. Based on its receiver decidethis
refers to. -
For value property in object and declare in global,
this
sets to global in default by scope chain to findthis
var.function Zs(){ // this.t is undefined because this.a refers to global console.log("t is " + this.t); console.log(this); console.log(this.a);
}
var p = {
x: function(){
var v = {
a: 5,
t: this,
s: Zs
};
console.log(v.t);
// this bind to object p
// Object { x: function() }, x is function,
// so this is set to p obj.
v.s();
// this bind to v obj
// return function(){ console.log(this) };
// return anonymous function declaration, not execute yet;
/*
var d = function(){ console.log(this) };
return d();
// window obj
return (function(){
console.log(this);
})();
// window obj
closure or anonymous function cannot access outer this,
so bind to global window object in default mode.
*/
}
};
var t = 99;
var a = 100;
p.x();
```
-
this
inside the anonymous function cannot access the outer function’s this, so it is bound to the global window object, when strict mode is not being used. -
If a function have
this
, check where the function actually called. If it lost the receiver(avar
assign to it also lost the receiver if we call that var instead directly call the function), then we have to usebind
,apply
, or setthis
tovar
from outside and pass it in. -
If an inner function is called inside another function, then
this
will refer to that another function's invoked object. -
let
is another declaration for lazy loading(only be able to use untillet
is run), and use block scoping(valid in curly braces, not just function scope). - ECMA6 -
Execution stack; event q: event q get event, once eecution stack empty, then it will load event from event q. This is called event loop.
-
Create namespace by creatiing object property.
var a = {}; a.b.c = 5 // not work, a.b is undefined. // use b as namespace var a = { b: {} }; a.b.c = 5;
-
Object literal !== JSON exactly, JSON's property must be all string and value can only be list or primitive value.
-
An object consist of primitive property, object properties, and methods.
-
JSON.parse(json_string)
,JSON.stringify(object_literal);
-
Function is just a special type of object.
function k(){}; k.greet = "greeting"; console.log(k.greet);
-
Js treat function as first class object. A function statement is a function code that not return value, a function expression is that assign the function(treat as result) to a
var
. -
Expression is a piece of code that result in a value
console.log(b);
var b = function(){};
// Function expression result an function object to var b.
// Also, b have not hoisted to function(), until later code assigned.
-
If we passing a function B as argument of function C, then
this
of B will be lost and set towindow
object due to thethis
rule of closure or anonymous function, we can useapply
orbind
to pull itself in.var b = { x: function(){ console.log(this); }, d: function(){ this.x(); }, t: function(func) { func(); }, v: function(func) { } }; b.d(); // b obj
b.t(b.x); // window obj b.v(b.x()); // b.x() call first => b obj
var b = {
x: function(){ console.log(this); },
d: function(){ x(); }
};
b.d();
// cannot find x() in window global object.
// because we does not prefix this.
```
-
Spread parameter replace arguments
function(a, b...)
: access b as spread param. - ECMA6 -
A function statment always have name:
// function expression var s = function(){ console.log("ss"); }();
-
Always end with semicolon, dont let parser do it for you:
function z(){ return { a: 5 }
} // Result undefined cuz parser add semicolon before line break. // Syntax parser ignore white space.
29. **IIFE**: imediately invode function expression.
```js
(function(name){ console.log(name) })('name');
// or
(function(name){ console.log(name) }('name'));
// use iife for multiple js loading to avoid data polluting.
(function(name){ console.log(name); console.log(window); }('name'));
```
30. Closure knows its outer variable inside that outer function, this achieve by scope chain in lexical env.
```js
function zz(what){
return function(boom){
console.log(what + " " + boom);
};
}
var v = zz("what");
v("boom");
```
31. The outer function's execution context pop out exec stack, but still reside in memory. the inner function will still have reference of vars for that outer function. Then the gc will not clean outer function cuz some code sitll have reference on it. **It might cause memory leak or exhaust it out if too much inner function**. Closing in all vars of that is supposed to access to: **closure**.
32. Closure has lazy loading propery, so closure try get i which already increment to 3 in below example.
```js
function hh(){
var ary = [];
for (var i = 0; i < 3; i++) {
ary.push(function(){
// this is ary obejct
console.log(i);
});
};
return ary;
}
var t = hh();
t[0](); // i all 3
t[1](); // i all 3
t[2](); // i all 3
```
33. To solve above case, use socpe variable `let` will bind the value to its first execution and that scope context in mean time so j can be 0, 1, 2 seperately.
```js
function hh(){
var ary = [];
for (var i = 0; i < 3; i++) {
let j = i;
ary.push(function(){
// this is ary obejct
console.log(j);
});
};
return ary;
}
```
34. Or create an IIFE which pass `i` to it and return. **The only way to preserve the outer `var i` in each loop is to create another execution context instead(IIFE)**.
```js
function hh_rev(){
var ary = [];
for (var i = 0; i < 3; i++) {
ary.push((function(j){
return function(){
console.log(j);
}
})(i));
};
return ary;
}
var t = hh_rev();
t[0]();
t[1]();
t[2]();
```
35. Trick of `this`: [http://www.quirksmode.org/js/this.html](http://www.quirksmode.org/js/this.html)
36. [https://software.intel.com/zh-cn/blogs/2013/10/09/javascript-this](https://software.intel.com/zh-cn/blogs/2013/10/09/javascript-this)
> What `this` is depends on how the callee to call a function.
> If there has no receiver for the func, then `this` point to global in default mode.
> If there has receiver, then it points to callee object.
> **Two patterns for it:**
>> ```js
>> obj.func();
>> // this for func point to obj.
>> func();
>> // this for func point to global object in default mode.
>> ```
37. Examples:
```js
// case 1
var x = 10;
var obj = {
x: 20,
f: function(){
console.log(this.x);
var foo = function(){ console.log(this.x); }
foo(); // 10, this point to global
}
};
obj.f(); // 20
// case 2
// To pass in obj as this instead, we can use apply, or set tmp var.
var x = 10;
var obj = {
x: 20,
f: function(){
console.log(this.x);
var that = this;
var foo = function(){ console.log(that.x); }
foo(); // 20, that point to global
}
};
obj.f(); // 20
// case 3
var x = 10;
var obj = {
x: 20,
f: function(){ console.log(this.x); }
};
obj.f();
// 20
// Should be very careful when you assign an func to a var.
// That means this will check whether that var has calllee or not,
// apply rule.
var fOut = obj.f;
fOut();
// the fOut does not have callee, so this point to global => 10
var obj2 = {
x: 30,
f: obj.f
}
obj2.f();
// callee is obj2 => 30
```
38. In some js plugins, the callback function usually followed the pattern with `apply` or `call` method to set this to callee object.
```js
var f = function(innerf){
innerf(arg1, arg2, arg3, ...);
// this of innerf will be global obj in default mode.
}
// The common pattern
var f = function(innerf){
innerf.call(this, arg1, arg2, arg3, ...);
}
obj.f(function(){ // this of callback set to obj. });
```
39. Approach to work around `this`:
[http://code.mforever78.com/translation/2015/05/19/understand-javascripts-this-with-clarity-and-master-it/](http://code.mforever78.com/translation/2015/05/19/understand-javascripts-this-with-clarity-and-master-it/)
[http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/](http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/)
40. **Function factories**: a function return function, each time we run the factory, it returns a new function object. (not singular).
41. Clousre and Callback:
```js
function sayHiLater(){
var g = "greeting";
// The anonymous function; callback will execute after 3 sec.
// It will execute callback, and back by scope chain to
// find g's reference. This is closure feature.
// You can think it just back to setTimeout code and go out to find g.
setTimeout(function(){
console.log(g);
console.log(this);
}, 3000);
}
var obj = {
p: sayHiLater,
pp: function() { sayHiLater(); },
ppp: function() {
var g = "greeting";
var self = this;
setTimeout(function(){
console.log(g);
console.log(self);
}, 3000);
}
};
obj.p(); // this is window object => property not method.
obj.pp(); // this is window Object => inner function.
obj.ppp(); // this is obj Object now => object method
```
42. Callback: a functino call when another function is done.
```js
function whenDone(callback){
var a = 5;
var j = 6;
callback();
}
whenDone(function{
console.log("I am callback");
});
```
43. `bind`, `apply`, and `call`: `bind` replace `this` and return a new copy of that function. `call` and `apply` accept arguments.
> bind(thisObj, args) => args prepend to argument list
> call(thisObj, ... arguments)
> apply(thisObj, argument_list = [] = null)
44. Use `apply` and `call` we can implement the function currying feature. create function copy with preset params.
```js
function bbb(a, b, c) {
console.log(arguments);
console.log(b);
};
var a = bbb.bind("thisObj", 1);
a(2, 3); // [1,2,3] b = 2
var a = bbb.bind("thisObj", 1, 5);
a(3, 4, 5); // [1,5,3,4,5] b = 5
```
45. Functional programming:
```js
var fu = function(limit, item){
//console.log(this); => window object
return item > limit;
}
function map(arr, fn){
var new_ary = [];
for (var i = 0; i < arr.length; i++) {
new_ary.push(fn(arr[i]));
}
return new_ary;
}
var result = map([1, 3, 4, 5], fu.bind(this, 3.5));
// we can rewrite fu by wrapper
var fu_wrapper = function(limit){
return function(limit, item){
return item > limit;
}.bind(this, limit);
}
var result_ver = map([1,3,4,5], fu_wrapper(1));
```
46. **Prototype chain is for object property/method, scope chain is for function to find access data/var/function/object**.
47. Dont ever do this below, performance issue.
```js
__proto__
// NON-STANDARD PROPERTY, THOUGH WIDELY SUPPORT
dan.__proto__ = person;
```
48. Every object eventually proto type to Object object.
49. All `Object` object's prototype has `toString()` method.
50. All `Function` object's prototype is **Empty(anonymous) functio**n, its has `bind`, `call`, and `apply` methods.
51. `for-in` loop **go through every member/method in an object and its prototype**. Be sure to use it only for its object.
```js
var john = { a: 4, b:5 }
for(var prop in john){
if (john.hasOwnProperty(prop)){
console.log("This is " + prop + " " + john[prop]);
}
}
```
52. `extend` in underscore.js: `_.extend(subclass, ... superclass object)`. It means to extend the property, but may overwrite or not.
53. Avoid using prototype to inherit property/methods, use `for-in` loop to add property/method, check below example instead.
```js
var extend = createAssigner(allKeys);
function allKeys(obj){
if (!obj.isPrototypeOf(Object)) return [];
var keys = [];
for(var x in obj){
keys.push(x);
}
return keys;
}
// initiate, undefinedOnly will only create empty object.
var createAssigner = function(keysFunc, undefinedOnly){
return function(obj){
var len = arguments.length
if (len < 2 || obj == null) return obj;
for (var i = 1; i < len; i++) {
var source = arguments[i],
keys = KeysFunc(source),
l = keys.length;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if(obj[key] === void(0) ||
!undefinedOnly) obj[key] = source[key];
};
};
return obj;
};
}
```
54. Function constructor use `this` to point to new object. new : create object, `new Person() === new(function(){ something same as person });`.
55. Function constructor usually only set properties, the object method will set in its prototpye for **gloal use**. Put method to prototype so **only one copy of that method need to initialize**. Otherwise it will copy that function to every new objects, unless you want to customized that function for each created object.
56. What `new` operator internally doing:
```js
function newObject(fn_constrcutor){
var params = Array.prototype.slice.call(arguments, 1);
var obj = Object.create(fn_constructor.prototype);
fn_constructor.apply(obj, params);
return obj;
}
var empty = newObject(function(){});
var c = newObject(function(a, b){
this.a = a;
this.b = b;
}, 1, 2);
```
57. Only **`Function` and `Object` has prototype, instance not**. prototype is a property to help that function object to build an object via new.
[http://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript](http://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript)
58. Sometimes you might forget a function is a constructor then call it without a `new` keyword. By convention we named that function with **first capitalize letter to distinquish this**.
59. JS engine will try convert String prmitive to String object for you if you access some `String.prototype.method`. ex: `"John".length`. **JS engine looks `String` object for you instead just primitive string**.
60. This is because **JS engine will automatically coeirce from primitive to object for you then `"John".__proto__` is point to `String.prototype`**.
61. Besides String, **JS engine will not coerce for primitive Number to Number object for you. But you can add paranthesis to work around(let JS coerce for you)**.
```js
typeof "1234";
// string
typeof new String("1245");
// object
Number.prototype.getSelf = function(){
return (typeof this);
};
(3).getSelf();
// object
(3)
// 3, is a primitive => js not coerce yet.
(3) === 3
// true
3.getSelf();
// error
```
62. **Caveat:** caution to use built-in function constructor(new String, Number) for primitives.
63. Array Object: index as key, value is array[index]. So avoid to use `for-in` loop for iterating the Array.
64. Though `__proto__` is not an standard but popular, you can use `Object.getprototypeOf(obj)` to know its `__proto__`.
65. **Polyfill**: add features that an engine may lack.
66. what `Object.create` do is set the prototype to its first param.
```js
if (!Object.create) {
Object.create = function(proto){
if (arguments.lenght > 1)
return "Object.create only accepts one argument."
// return will be Func {} ...
function Func(){ /* construcotr */ };
// set prototype.constructor and prototype.
Func.prototype = proto;
Func.prototype.constructor = Func;
return new Func();
}
}
// then instead use apply, manually assign it.
var obj = function Obj(f, l){
this.firstName = f;
this.lastname = l;
};
var new_obj = Object.create(obj.prototype);
new_obj.firstName = "First name";
new_obj.lastName = "last name";
// or obj.apply(new_obj, ["First name","last name"]);
```
67. Function name maybe duplicate but not the same if use function expression instead function assignment.
```js
var x = function Func(){
console.log("Hi-X");
};
// function assignment
function Func(){
console.log("Hi-Func");
};
x.prototype === Func.prototype
// false, even though name are the same,
// but actually Func() is a method create in Window object,
// which key is also named Func().
// x is a property which point to another Func() object.
```
68. In **ECMA6**, `class` is a constructor keyword.
```js
// class still an object, not class in JAVA or C or Ruby
class Person {
constructor(f, l){
this.firstName = f;
this.LastName = l;
}
method() {
return "Hi " + firstName;
}
}
var new_pl = new Person("First", "last");
// use extends to set prototype
class Baby extends Person {
constructor(f, l){
// if you need use prototype's constructor to set property
super(f, l)
}
}
```
69. `var p = new (function P(){});` is just function expression, will not registered in current this object.
70. `function.prototype` is an object. `typeof` is possible `(__proto__)` in its prototype chain, `instanceof`: just father `__proto__`. Better approach: use `Object.prototpye.toString.call(obj);` to list all prototypes in chain.
71. table of `typeof`:
```js
typeof null // bug, forever... === object
typeof undefined === undefined
// typeof possible return string value: string, number, boolean,
// undefined, (any object)object, function, symbol.
// dont use below...
typeof new Number(1) // object
typeof Number(1) // number
var n = Number(1)
typeof n // number
// so be careful, not use typeof if it is too ambiuity
```
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof)
72. **Conclusion of `typeof`**: `typeof` only good for knowing the target is `undefined` or has been set. **Better use `Object.prototype.toString.call(obj)` to know obj's superclass**.
```js
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(2) // "[object Number]"
```
73. `instanceof` only useful when compare a **customed type obejct**. O.w. useless as `typeof`.
```js
3 instanceof Number // false, 3 is primitive
new Numbwe(3) instanceof Number // true
[] instanceof Array // true
```
74. If `document` object is not the same, then use the `instanceof` to compare other object will not have the correct result due to different constructor object.(see below `Func()` vs `var x` example)
```js
var x = function Func(){
console.log("Hi-X");
};
// function assignment
function Func(){
console.log("Hi-Func");
};
x.prototype === Func.prototype
```
75. If `'use strict'` is only put inside a function, then **it will switch to strict mode only in that scope**.
76. Jquery is an mutation of Array object, it returns an array, but then overwrite its prototype point to `Jquery.fn` which support JQuery api.
77. We can provide return a type of object for function constructor, which will create that object extend constructor customed property/methods.
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new)
> The object returned by the constructor function becomes the result of the whole new expression. If the constructor function doesn't explicitly return an object, the object created in step 1 is used instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.)
```js
// JQuery like array object, with merge
function F(){
var b = [];
this.x = 100;
this.j = 1000;
for (var p in this){
Object.defineProperty(b, p, {
value: this[p]
});
}
return b;
}
var d = new F();
d.x === 100;
d.push(5)
d.length === 1
```