箭頭函數,除了幫助我們簡寫之外,最大的好處就是this不再被綁定在調用者,而是繼承與環境對象,在哪個環境定義,this就是指的哪個環境對象。
在編寫構造函數或者函數時,this的指向經常會變化,導致this.屬性無法在各個函數體面使用
案例代碼:
function Counter() {
console.log('我是構造體'+this);
this.number = 0;
//返回一個 ID(數字),可以將這個ID傳遞給 clearTimeout() 來取消執行。
this.timer = window.setTimeout(function () {
this.number++;
//此時的this的意思是window,window是沒有number屬性的
console.log(this.number);
},1000)
}
var b = new Counter();
console.log(b.number);
結果 0
NaN
爲了解決this指向問題,我們可以使用bind來改變,不能使用call,apply
function Counter() {
this.number = 0;
var fn = function () {
this.number++
console.log(this.number);
}
// 不能用call、applay,這兩種方法都是立即執行,並且不會循環執行
// var timer = window.setTimeout(fn.call(this),3000)
//var timer = window.setInterval(fn.apply(this),3000)
var timer = window.setInterval(fn.bind(this),3000)
}
var b = new Counter();
console.log(b.number);
從上可以看出,想改變this的指向非常困難
有了箭頭函數之後,一切都變得簡單,不在綁定this的指向了
function Counter() {
this.number = 0;
var timer = setInterval(() => {
console.log(this);
this.number++;
console.log(this.number);
},2000)
}
var b = new Counter();
原理分析:
普通函數,this的概念是:this是JavaScript的一個關鍵字,他是指函數執行過程中,自動生成的一個內部對象,是指當前的對象,只在當前函數內部使用。(this對象是在運行時基於函數的執行環境綁定的:在全局函數中,this指向的是window;當函數被作爲某個對象的方法調用時,this就等於那個對象)。
函數中this對象的取值,是在函數被調用執行的時候確定的。此時會創建執行上下文環境。
對於箭頭函數,它沒有自己的this,不能用作構造函數。
箭頭函數中的this對象是定義時所在的對象,不是調用時所在的對象。
案例1
var testObject = {
name:'123',
sayHi:function () {
console.log(this);
// return function () {
// console.log(this);
// }
//箭頭函數的this,不再是誰調用就是誰,而是指當前環境的this是誰,
//一般是指父級,因爲箭頭函數沒有this對象,都是從父級繼承過來的
return ()=>{
console.log(this);
}
}
}
var hh = testObject.sayHi();
window.hh();
案例2
function foo() {
setTimeout(function() {
console.log('setTimeout普通函數的this='+this);
console.log("id: ",this.id);
}, 100);
}
function fooa() {
setTimeout(()=>{
console.log('setTimeout箭頭函數的this='+this);
console.log("id: ",this.id);
}, 100);
}
var id=21;
//兩個函數的結果都是一樣的:window,21
//因爲可以理解爲都是由window調用的window.foo() 和 window.fooa()
foo(); //this指向window對象, 21
fooa(); //this指向window對象, 21
=========================================
//setTimeout(普通函數體)的this指向的調用時對象,那麼這個對象如果不改變的話永遠都是window.
foo.call({id:42}); //this指向window對象, 21
//setTimeout(箭頭函數體)本身沒有this,定義時對象,也就是哪個對象調用了,this就是哪個對象的
fooa.call({id:42}); //this指向object對象, 42
案例3-返回值是函數的,函數體裏面的this指向的是window,和普通函數裏面的this一樣
const test = {
name: 'test object',
createAnonFunction: function() {
return function() {
//function 當做了普通函數,this的指向是window
console.log(this.name);
console.log(arguments);
};
},
createArrowFunction: function() {
return () => {
//箭頭函數本身沒有this對象,this是在定義是去找,本身沒有的話,就會去父級尋找
//當test對象去調用方法createArrowFunction,this就獲得了test對象
console.log(this.name);
console.log(arguments);
};
}
};
//結果
JavaScript 代碼:
> const anon = test.createAnonFunction('hello', 'world');
> const arrow = test.createArrowFunction('hello', 'world');
> anon();
undefined
{}
> arrow();
test object
{ '0': 'hello', '1': 'world' }
案例4-函數作爲參數的時候,函數體裏面的this指向的是window,和普通函數裏面的this一樣
Array.prototype.myFind = function (index,callback) {
//this是對象數組
console.log(this)
for (var i = 0; i < this.length; i++) {
if(i === index){
callback(this[i])
}
}
}
var arr = ['hello', 'WORLD', 'Whatever'];
arr.myFind(1,function (item) {
//this是window
console.log(this);
console.log(item);
})
特殊的案例
Array.prototype.myFind = function (index, callback) {
for (var i = 0; i < this.length; i++) {
if (i === index) {
callback.call(this,this[i])
//callback(this[i])
}
}
}
var arr = ['hello', 'WORLD', 'Whatever'];
arr.myFind(1, function (item) {
//array數組
console.log(this);
})
arr.myFind(1, item => {
//window對象
console.log(this);
})