關於javascript的this講解的文章已經多如牛毛了,我本人在開發過程中也用到過很多次。這次趁有空,整理一下關於this的一系列問題:包括this的指向,嚴格模式和非嚴格模式下的區別,什麼情況下可以改變this的指向以及在ES6下箭頭函數中this的指向。
一.嚴格模式下this的指向
嚴格模式下:
function aa(){
'use strict';
console.log(this) //undefined
}
aa();
非嚴格模式下:
function aa(){
console.log(this) //window
}
aa();
二.this的指向
MDN中對this的解釋如下:
在絕大多數情況下,函數的調用方式決定了this的值。this不能在執行期間被賦值,並且在每次函數被調用時this的值也可能會不同。
簡單點來說,this的指向在聲明時是未定義的,只有在它被調用時纔會被賦值,要搞清楚它到底指向什麼,只要搞清楚到底誰調用它即可。
下面有幾種情況可以幫助我們加快理解this的指向問題:
1.第一種情況:
function aa(){
console.log(this) //window
}
aa();
此時是window對象調用了aa()函數,所以this指向window。
2.第二種情況
var test = {
aa:"hello",
bb:function(){
console.log(this.aa) //"hello"
}
}
test.bb();
此時是test對象調用了this,所以this此時指向的bb對象中的aa;
var test = {
aa:"hello",
bb:function(){
console.log(this) //test (object)
}
}
test.bb();
此時就很清晰的能看到this指向的test對象。
3.第三種情況
var test = {
aa:"hello",
bb:{
aa:'hello2',
cc:function(){
console.log(this.aa) //"hello2"
}
}
}
test.bb.cc();
此時調用cc中this的對象是bb,而不是test,所以this指向的是bb。
以上三個例子已經比較清晰的解釋了this的指向問題:
誰最後調用了this,誰就是this指向的對象。
三.什麼情況下this的指向會被改變
這些情況大致可以分爲兩類:
1.關鍵字類。主要有new,return
2.函數類。 主要有call,apply,bind
第一類.關鍵字類
我們先看new的例子:
function aa(){
this.name = "Tom"
}
var bb = aa(); //bb is undefined;
var bb = new aa();
console.log(bb.name) //"Tom"
new關鍵字的關鍵在於實例化了構造函數aa()。使它的函數能夠順利的拿到裏面的name。
return的例子:
function aa(){
this.name = "Tom";
return {};
}
var bb = new aa();
console.log(bb.name) //undefined
當return的內容是一個對象時,則this會指向這個對象。
function aa(){
this.name = "Tom";
return 'aaa';
}
var bb = new aa();
console.log(bb.name) //"Tom"
當return的內容是一個非對象時,則this的指向不會發生改變。
第二類.函數類
1.call()函數
老規矩,先關門放MDN。
call() 方法調用一個函數,其具有一個指定的this值和分別地提供的參數(參數的列表)。
簡單來說就是可以通過call()函數來指定你想調用的this所造的環境,同時還可以追加多個參數。
以下是MDN上的官方示例:
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
console.log(new Food('cheese', 5).name);
// expected output: "cheese"
如果沒有調用call()函數,最後打印的應該是food,調用後纔會改成cheese。如果覺得上面的例子還不夠清晰,我們可以再看一個,如果覺得ok了,請跳過。
var aa = {
name:"tom",
bb:function(x, y){
console.log(this.name); //"tom"
console.log(x,y); // 1,2
}
}
var b = aa.bb
b.call(aa);
b.call(aa,1,2)
2.apply函數
apply() 方法調用一個具有給定this值的函數,以及作爲一個數組(或類似數組對象)提供的參數。
該方法的作用和 apply()方法類似,只有一個區別,就是call()方法接受的是若干個參數的列表,而apply()方法接受的是一個包含多個參數的數組。
var aa = {
name:"tom",
bb:function(x, y){
console.log(this.name); //"tom"
console.log(x,y); // 1,2
}
}
var b = aa.bb
b.apply(aa,[1,2])
3.bind函數
bind()方法創建一個新的函數, 當這個新函數被調用時其this置爲提供的值,其參數列表前幾項置爲創建時指定的參數序列。
var module = {
x: 42,
getX: function() {
return this.x;
}
}
var unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined
var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
需要注意的是,bind函數與apply和call都有所不同,不同之處在於bind不是立即執行的。
bind() 函數會創建一個新綁定函數,綁定函數與被調函數具有相同的函數體(在 ECMAScript 5 中)。
我們看一個例子:
var aa = {
name:"tom",
bb:function(){
console.log(this.name)
}
}
var cc = aa.bb;
var dd = cc.bind(aa);
console.log(dd); //type function aa()
dd(); // "tom"
三.ES6箭頭函數中的this
箭頭函數與其他函數區別很大,我們直接看MDN:
箭頭函數表達式的語法比函數表達式更短,並且沒有自己的this,arguments,super或 new.target。
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| 正確地指向person 對象
}, 1000);
}
var p = new Person();
當然,通過相關函數也不能改變=>中的this指向。
通過 call 或 apply 調用
由於 箭頭函數沒有自己的this指針,通過 call() 或 apply() 方法調用一個函數時,只能傳遞參數(不能綁定this—譯者注),他們的第一個參數會被忽略。(這種現象對於bind方法同樣成立—譯者注)
總結一下,在箭頭函數中,是沒有它自己的this的,它只會從自己的作用域鏈的上一層繼承this。
我們來看例子:
var obj = {
fn:()=>{
console.log(this) //window
}
};
obj.fn();
再看一個:
function obj(){
let aa = ()=>{
console.log(this);
}
aa();
}
obj() // window
var bb = new obj(); //obj type object
第二個例子中,new obj()後之所以打印出了obj(),是因爲new關鍵字綁定了this到返回的新的對象object中。