Some JS stuff...
-
scope-chain, closure
-
"This"
Scope-chain & Closure
在ES6的var與let出現以前,切分變數有效範圍的最小單位是 "function"
此時b會先在自己的function scope那一層找 ->
找不到後再網外層找 ->
b成為global variable
var msg = "global."
function outer() {
var msg = "local."
function inner() {
return msg;
}
return inner;
}
var innerFunc = outer();
var result = innerFunc();
console.log( result ); // ?????????
Ans: local.
因為scope chain是在“定義”時決定的,function inner裡回傳的msg在定義時就已經往上查找到了outer宣告的msg也就是local.
就是內個取得「內部函式當時環境」變數值的用法,造就了閉包(closure)這樣的一個使用方式
Or even Arrow function
var counter = () => {
var count = 0;
return () => ++count;
}
What's this ??
- this 是 JavaScript 的一個關鍵字。(
廢話 - this 是 function 執行時,自動生成的一個內部物件。
(function() {
const self = this;
console.log(self);
})()
Q1
function func() {
console.log( this.a );
}
var obj = {
a: 2,
foo: func
};
obj.foo(); // ??
var func2 = obj.foo;
func2(); // ??
Q2
var bar = function() {
console.log( this.a );
};
var foo = function() {
var a = 123;
this.bar();
};
foo();
A: Error
B: Undefined
C: 123
Q3
var obj = {
func1: function(){
// true | false ?
console.log( this === obj );
var func2 = function(){
// true | false ?
console.log( this === obj );
};
func2();
}
};
obj.func1();
關鍵在於是"誰"在呼叫這個"function"
this 代表的是 function 執行時所屬的物件
1.
2.
1. 透過"obj"來呼叫綁定於obj.foo的 func函式
2. func2實際上是綁定到func函式,此時是由最外層的global object(browser: window, node: global)呼叫func2
而a並非global variable所以回吐undefined
window.func()
var bar = function() {
console.log( this.a );
};
var foo = function() {
var a = 123;
this.bar();
};
foo();
1. 透過global-object呼叫foo,此時this指向window
2. 在foo的scope中建立變數a,並用this (此時代表window)呼叫全域變數bar,此時裡頭的this依舊代表window
3. 執行bar裡的log,但a並未宣告於global,所以回傳undefined
this 以 function scope 為單位做切分,不同scope 中的 this 不會相互影響
不要被那個若有似無的父子關係給騙了!他們陌生到不行
var obj = {
func1: function(){
// true | false ?
console.log( this === obj );
var func2 = function(){
// true | false ?
console.log( this === obj );
};
func2();
}
};
obj.func1();
在沒有特別指名this,也不是透過特定object呼叫的情形下,this就是會預設綁定到global object
1. 透過obj呼叫func1,this指向obj => 1st log is true!
2. 宣告func2,不拐彎抹角直接呼叫func2,this指向window =>2nd log is false!
var obj = {
func1: function(){
// true | false ?
console.log( this === obj );
var func2 = function(){
// true | false ?
console.log( this === obj );
};
func2();
}
};
obj.func1();
題外話
為了解決那看起來很直觀卻是一個雷點的現象...
function test() {
console.log(this); // obj
setTimeout(function () {
console.log(this); // window
}, 10);
}
const obj = {
func: test,
};
obj.func();
強制綁定
function test() {
console.log(this); // obj
const self = this;
setTimeout(function () {
console.log(self); // obj
}, 10);
}
const obj = {
func: test,
};
obj.func();
bind()
function test() {
console.log(this); // obj
setTimeout(function () {
console.log(this); // obj
}.bind(this), 10);
}
const obj = {
func: test,
};
obj.func();
call() | apply()
function func( arg1, arg2, ... ){
// do something
}
func.call( context, arg1, arg2, ... );
func.apply( context, [ arg1, arg2, ... ]);
bind與call / apply差別就在於是否有呼叫function而已
用途上的差異就是:
前者在宣告時做綁定,使得未來呼叫時有固定的this
後者使用在 context 較常變動的場景,依照呼叫時的需要帶入不同的物件作為該 function 的 this
var obj = {};
function bindTest() { console.log(this) }
var testBinded = bindTest.bind(obj);
testBinded();
function callTest() { console.log(this) }
callTest.call(obj);
This in Arrow Function
箭頭函式並不擁有自己的 this 變數;使用的 this 值來自封閉的文本上下文,也就是說,箭頭函式遵循常規變量查找規則。因此,如果在當前範圍中搜索不到 this 變量時,他們最終會尋找其封閉範圍。
- this在宣告時對象就固定了,不再隨著呼叫場景的不同而有所改變
- 因為裡頭的this是透過向上查找來決定的,所以可以套用原本直觀的父子關係!
- 先將原始function透過call綁定obj
- 裡頭的arrow function會在宣告時向上查找,找到上層的this綁定對象為obj所以也綁obj
取而代之,無論是strict mode或是bind(), call(), apply()等function都無法改變arrow function裡this指向的對象
如果向上找沒找到function scope就會綁回global object
結論
兩種函式寫法都有他直觀與不直觀的應用場景,有需要使用到的話還是要搞懂它,以免發生一堆「你以為」的狀況~
REF
- https://pjchender.blogspot.com/2017/01/es6-arrow-function.html
- https://kuro.tw/posts/2017/10/12/What-is-THIS-in-JavaScript-%E4%B8%8A/
- https://ithelp.ithome.com.tw/articles/10193193
- MDN
- Chrome inspect
JS
By ian Lai
JS
- 345