javascript開發:ES5與ES6的“this”深入分析
ES6中新增了箭頭函式這種語法,箭頭函式以其簡潔性和方便獲取this的特性,俘獲了大批粉絲兒
它也可能是面試中的寵兒, 我們關鍵要搞清楚 箭頭函式和普通函式中的this
一針見血式總結:
普通函式中的this:
1. this總是代表它的直接呼叫者, 例如 obj.func ,那麼func中的this就是obj
2.在預設情況(非嚴格模式下,未使用 'use strict'),沒找到直接呼叫者,則this指的是 window
3.在嚴格模式下,沒有直接呼叫者的函式中的this是 undefined
4.使用call,apply,bind(ES5新增)繫結的,this指的是 繫結的物件
箭頭函式中的this
預設指向在定義它時,它所處的物件(宿主物件),而不是執行時的物件, 定義它的時候,可能環境是window
下面通過一些例子來研究一下 this的一些使用場景[ 使用最新版 chrome測試 ]
要整明白這些, 我們需要首先了解一下作用域鏈:
當在函式中使用一個變數的時候,首先在本函式內部查詢該變數,如果找不到則找其父級函式,
最後直到window,全域性變數預設掛載在window物件下
1.全域性變數預設掛載在window物件下
<script>
var aa = 2;
alert(window.aa);
(function () {
aa = 3;
})();
alert(window.aa);
</script>
我們僅僅聲明瞭一個全域性變數aa,但是打印出window.aa卻和aa保持一致,為什麼呢?
眼見為實, 我們使用 console.dir(window) 列印 window物件看看
我們可以看到在window屬性中,看到aa屬性了;此外,函式也適用於此情況,全域性函式也會掛在在window物件下
我們常見的window的屬性和方法有: alert, location,document,parseInt,setTimeout,setInterval等,window的屬性預設可以省略window字首!
2.在普通函式中,this指向它的直接呼叫者;如果找不到直接呼叫者,則是window
我們來看一些例子
示例1:
<script>
function test() {
console.log(this);
}
test();
</script>
結果是: window
原因: test()是一個全域性函式,也就是說是掛在window物件下的,所以 test()等價於 window.test() ,所以此時的this是window
示例2:
<script>
var obj = {
say: function () {
setTimeout(function () {
console.log(this)
});
}
}
obj.say();
</script>
結果是: window
匿名函式,定時器中的函式,由於沒有預設的宿主物件,所以預設this指向window
問題: 如果想要在setTimeout/setInterval中使用這個物件的this引用呢?
用一個 變數提前把正確的 this引用儲存 起來, 我們通常使用that = this, 或者 _this = this來儲存我們需要的this指標!
<script>
var obj = {
func: function() {},
say: function () {
var that = this; //此時的this就是obj物件
setTimeout(function () {
console.log(this)
that.func()
});
}
}
obj.say();
</script>
我們也可以使用 func.bind(this) 給回撥函式直接繫結宿主物件, bind繫結宿主物件後依然返回這個函式, 這是更優雅的做法
- <span style="font-family:Times New Roman;"><script>
- var obj = {
- func: function() {},
- say: function () {
- // 此時的this就是obj物件
- setTimeout(function () {
- console.log(this)
- this.func()
- }.bind(this));
- }
- }
- obj.say(); // obj
- </script></span>
示例3(改變自360面試題):
window.val = 1;
var obj = {
val: 2,
dbl: function () {
this.val *= 2;
val *= 2;
console.log(val);
console.log(this.val);
}
};
// 說出下面的輸出結果
obj.dbl();
var func = obj.dbl;
func();
結果是: 2 4 8 8
<1> 12行程式碼呼叫
val變數在沒有指定物件字首,預設從函式中找,找不到則從window中找全域性變數
即 val *=2 就是 window.val *= 2
this.val預設指的是 obj.val ;因為 dbl()第一次被obj直接呼叫
<2>14行程式碼呼叫
func() 沒有任何字首,類似於全域性函式,即 window.func呼叫,所以
第二次呼叫的時候, this指的是window, val指的是window.val
第二次的結果受第一次的影響
3.在嚴格模式下的this
<script>
function test() {
'use strict';
console.log(this);
}
test();
</script>
結果是: undefined
4.箭頭函式中的 this
<script>
var obj = {
say: function () {
setTimeout(() => {
console.log(this)
});
}
}
obj.say(); // obj
</script>
此時的 this指的是定義它的物件, obj,而不是 window!
示例(多層巢狀的箭頭函式):
<script>
var obj = {
say: function () {
var f1 = () => {
console.log(this); // obj
setTimeout(() => {
console.log(this); // obj
})
}
f1();
}
}
obj.say()
</script>
因為f1定義時所處的函式 中的 this是指的 obj, 所以不管有多層巢狀,都是 obj
示例(複雜情況: 普通函式和箭頭函式混雜巢狀)
<script>
var obj = {
say: function () {
var f1 = function () {
console.log(this); // window, f1呼叫時,沒有宿主物件,預設是window
setTimeout(() => {
console.log(this); // window
})
};
f1();
}
}
obj.say()
</script>
示例(嚴格模式下的混雜巢狀)
<script>
var obj = {
say: function () {
'use strict';
var f1 = function () {
console.log(this); // undefined
setTimeout(() => {
console.log(this); // undefined
})
};
f1();
}
}
obj.say()
</script>
結果都是undefined
說明: 嚴格模式下,沒有宿主呼叫的函式中的this是undefined!!!所以箭頭函式中的也是undefined!