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是透過向上查找來決定的,所以可以套用原本直觀的父子關係!
  1. 先將原始function透過call綁定obj
  2. 裡頭的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