在學習 JS
,往往都會對 this
的值很模糊,也讓人困擾!以為認為 this
是指向某個對象,但往往不是你想那樣。正確來說,誰調用函數,那麼this
就指向調用者。但是要特別注意如果使用嚴謹模式 this
會轉變成 undefined
。
function fn() {
'use strict'
console.log(this) // undefined
}
this 的值取決於函數呼叫方式。
function Cat(name) {
this.name = name;
this.sayName = function () {
console.log(`Meow! My name is ${this.name}`);
};
}
const bailey = new Cat('Bailey');
什麼時候 this 被賦值
- 很多人誤解,以為
this
是指向對象。 - 正確來說:當前函數的
this
是在函數被調用執行的時候才確定。
const dog = {
bark: function () {
console.log('Woof!');
},
barkTwice: function () {
this.bark();
this.bark();
}
};
this
的值實際上沒有分配給任何東西,直到object
呼叫方法。this
的值是根據object
調用方法時,this
被確定了。
What Does this Get Set To?
當函數被調用時,變數this被設成特定的值。根據函數的調用方式,this
指向對象會截然不同。
有四種方式去 call function
,在每個設定 this
方式不同
- 使用關鍵字
new
來調用構造函數將會設定this
指向新的object
。 - 在
object
調用方法 (method)this
指向obj
本身。- 例子:
dog.barkTwice()
會訪問dog
本身
- 例子:
- 呼叫原生函數,
this
會指向window
(browser
環境) - 呼叫原生函數允許我們去設定
this
指向
總結
函數,對象和 this
是有互相關聯的。當我們使用 new
來調用構造函數,變數 this
會被設為新的 object
。當我們在 object
調用方法 (method) ,this
會被設為 object
本身。當我們調用原生函數 (在 browser
環境) this
會被設為 window
。
設定this的指向
如果我們想要設成自己 this
的值,JavaScript
有提供幾種來設定/改變 this
的值的方法(call()
, apply()
, bind()
)來實現。
call()
,apply()
會根據參數形式。bind()
是返回新的函數。
call()
call()
是一個直接調用在函數上的方法
function.call(函數內部指定 this 的值, 函數的參數)
以下面例子函數裡 this
是指向 window
,3和4是函數的參數。
function multiply(n1, n2) {
return n1 * n2;
}
multiply(3, 4); // 12
multiply.call(window, 3, 4); // 12
以下面例子,在 object
的方法(method) 使用 call()
- 使用
call()
來向object
借方法來執行 - 當我執行
mockingbird.describe
方法時,裡面this
是指向 object(mockingbird) 本身。 - 執行
mockingbird.describe.call(pride);
第一個參數是來指定this
的值,所以this
是指向pride
對象
const mockingbird = {
title: 'To Kill a Mockingbird',
describe: function () {
console.log(`${this.title} is a classic novel`);
}
};
mockingbird.describe(); // 'To Kill a Mockingbird is a classic novel'
const pride = {
title: 'Pride and Prejudice'
};
mockingbird.describe.call(pride); // 'Pride and Prejudice is a classic novel'
apply()
apply()
是一個直接調用在函數上的方法call()
和apply()
很相似,apply() 差別在於函數的參數是陣列形式
function.apply(函數內部指定this的值, [函數的參數])
以下面例子函數裡 this
是指向 window
,3和4是函數的參數。
function multiply(n1, n2) {
return n1 * n2;
}
multiply.apply(window, [3, 4]); // 12
以下面例子使用 apply
來調用 object 方法(method)
apply()
和call()
使用方式是一樣。- 結果也和
call()
是一樣
const mockingbird = {
title: 'To Kill a Mockingbird',
describe: function () {
console.log(`${this.title} is a classic novel`);
}
};
mockingbird.describe(); // 'To Kill a Mockingbird is a classic novel'
const pride = {
title: 'Pride and Prejudice'
};
mockingbird.describe.apply(pride); // 'Pride and Prejudice is a classic novel'
選擇哪個方法呢?
call()
可能有限,如果你不知道 function
需要多少參數,可以使用apply()
來代替是最佳選擇!
callback and this
function invokeTwice(cb) {
cb();
cb();
}
const dog = {
age: 5,
growOneYear: function () {
this.age += 1;
}
};
const cat = {
age: 5,
growOneYear: function () {
this.age += 1;
}
};
dog.growOneYear();
dog.age; // 6
invokeTwice(dog.growOneYear);
dog.age; // 6
為什麼 age
還是保留 6
。因為 invokeTwice
調用 dog.growOneYear
,但是是當作 function
來調用而不是 method
,所以 this
是指向 global object
而不是 dog
。
bind()
bind
可以用來指定 this
,會 return
新的函數
bind()
是一個直接調用在函數上的方法- 返回該函數副本,具有特定
this
值 - 向
bind
中傳入一個參數是 - 在
bind
函數被調用時作為目標函數的this
參數
// 根據上面程式例子
const grow = dog.growOneYear.bind(dog)
invokeTwice(grow)
dog.age // 8
// dog.growOneYear 是目標函數,
// 我們不希望this是指向 global object,
// 但我希望 this 是指向 dog
const grow = dog.growOneYear.bind(cat)
invokeTwice(grow) // cat.age = 7
// dog.growOneYear 裡面的 this 綁定到 cat (指定object),
// 會複製一份函數存在 grow 變數。
// 呼叫 grow() cat 的 age 會是8
// 理解成 cat.growOneYear()
剪頭函數?
關於剪頭函數有沒有 this
?
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
剪頭函數本身是沒 this
!
總結
總結來說,this
的值是誰調用函數 this
指向誰 (this 指向是函數調用者)。但我們可以強制改變 this
的指向,可以使用 (call
, apply
, bind
)