概述
這是我看typescript的時候看引用資源看到的,原文在這里:Understanding JavaScript Function Invocation and "this",我簡單地總結一下記下來供以后開發時參考,相信對其他人也有用。
機制
js中的函數調用機制是這樣的:
- 建立一個表argList,從索引1開始塞入函數的參數。
- 表的索引0的值是thisValue。
- 把this賦給thisValue,然后調用func.call(argList)。
說這么多,其實就是想說明,函數調用f(x,y)其實就是f.call(this, x, y)的語法糖。內部是通過f.call(this, x, y)來調用的。
可以看到,雖然f(x,y)里面沒有this,但是f.call(this, x, y)里面有this,所以非常好理解為什么函數調用中會有this了。
那么this是從哪里來的呢?當f(x,y)沒有調用者的時候,this自動被賦值為window;當f(x,y)有調用者的時候,this被賦值為指向調用者。
例子
舉幾個實例感受一下:
//例子1
function hello(thing) {
console.log(this + " says hello " + thing);
}
hello.call("Yehuda", "world"); //輸出Yehuda says hello world
//例子2
function hello(thing) {
console.log(this + " says hello " + thing);
}
//相當於hello.call(window, "world");
hello("world"); // 輸出[object Window] says hello world
//例子3
function hello(thing) {
console.log(this + " says hello " + thing);
}
let person = { name: "Brendan Eich" };
person.hello = hello;
//相當於hello.call(person, "world");
person.hello("world"); //輸出[object Object] says hello world
注意:我們這里不是strict mode。
更優雅的寫法
有時候,我們希望函數沒有調用者,但是this卻不指向window對象。有以下2種優雅的解決方案:
箭頭函數
es6里面定義了箭頭函數,不只是為了簡化函數的寫法,而且還有這么一條有用的規定:箭頭函數的this不會隨調用者的不同而變化,它的this永遠是被定義的函數域中的this。
//不用箭頭函數
let person = {
hello: function(thing) {
return function() {
console.log(this + " says hello " + thing);
}
}
}
let helloTest = person.hello('world');
helloTest(); //輸出[object Window] says hello world
//使用箭頭函數
let person = {
hello: function(thing) {
return () => {
console.log(this + " says hello " + thing);
}
}
}
let helloTest = person.hello('world');
helloTest(); //輸出[object Object] says hello world
需要注意的是,需要用一個function()把箭頭函數包裹起來,因為如果不這樣的話,它被定義的函數域是window。
bind
用箭頭函數有點麻煩,我們可以這么寫一個bind函數達到效果。
let person = {
hello: function(thing) {
console.log(this + " says hello " + thing);
}
}
let bind = function(func, thisValue) {
return function() {
return func.apply(thisValue, arguments)
}
}
let boundHello = bind(person.hello, person);
boundHello('world'); //輸出[object Object] says hello world
es5給所有Function封裝了上面的bind方法,所以我們只需要這么寫:
let person = {
hello: function(thing) {
console.log(this + " says hello " + thing);
}
}
let boundHello = person.hello.bind(person);
boundHello('world'); //輸出[object Object] says hello world
這就是bing()方法的來歷0.0
