this指向问题
箭头函数本身没有this,而是继承了父级作用域中的this。而且箭头函数一旦定下来,this指向不会根据任何情况变化。
普通函数包括全局函数,其this的指向情况可概括为4种:
- 函数调用,当一个函数不是一个对象的属性时,直接作为函数来调用时,
this指向全局对象。
// 宏任务中之所以要用箭头函数,是因为宏任务方法是定义在全局函数环境,
// 如果不用箭头函数this会指向全局对象,用箭头函数的this代表当前函数作用域的父级作用域的this。
setInterval(() => {
this.arg = xxx;
}, interval);
-
方法调用,如果一个函数作为一个对象的方法来调用时,
this指向这个对象。如item.func(arg) //func中的this此时指向item -
构造函数调用,
this指向这个用new新创建的对象。
const Func = function(arg){
this.arg = arg;
}
let instance = new Func(arg);
- 第四种是
apply 、call 和 bind调用模式,这三个方法都可以显式地指定调用函数的this指向。apply接收参数的是数组,call接受参数列表,bind方法通过传入一个对象,返回一个this绑定了传入对象的新函数。这个函数的this指向除了使用new时会被改变,其他情况下都不会改变。如:
const Func = function(){
this.arg1 = xx;
this.arg2 = xxx;
}
Func.apply(obj,[arg1,arg2]) //此时this 指向obj
不该使用箭头函数的情况
1. 通过构造函数调用
const createItem = (itemName, numOfItem) => {
this.itemName = itemName;
this.num = numOfItem;
this.getItemName = ()=>{return this.itemName};
this.getNumOfItem = ()=>{return this.num};
};
// TypeError: Foo is not a constructor
2. 需要使用原型prototype继承,仅限ES5继承写法 由于JavaScript中的继承底层本质上都是基于原型prototype的,因此涉及到继承或者原型方法的调用,都不该使用箭头函数。
const Func = ()=>{};
console.log(Func.prototype);
// undefined
3. 需要使用supper继承 箭头函数没有supper,如果用ES6 class创建类,其中的函数方法采用箭头函数,如果出现基类方法为箭头函数,子类方法为普通函数,当我们想访问子类的方法时,却访问到了父类的方法。造成混乱
class TypeA{
constructor(typeName="typeA"){
this.typeName = typeName;
}
getTypeName=()=>{
console.log("father");
return this.typeName;
}
}
class TypeB extends TypeA{
constructor(typeName="typeB"){
super(typeName);
}
getTypeName(){
console.log("child");
return this.typeName;
}
}
let typeB = new TypeB();
console.log(typeB.getTypeName());
// ouput: father '\n' typeB
原因在于JavaScript访问属性变量和函数的方式,是先查找当前作用域中是否存在该变量或者函数,如果找不到,就从prototype上找,以此在原型链上找,这里由于基类TypeA的getTypeName是箭头函数,实际上class中涉及=都是Field Declaration(域声明),子类TypeB的方法是在原型prototype上的,由于TypeB继承了TypeA的方法,但是当我们访问函数方法时,却优先访问TypeA的方法了,因此随便混用箭头函数容易造成混乱,建议还是将函数方法存放在prototype上。
4. 需要使用生成器
MDN上描述只有"使用 function*来定义一个生成器"的定义,也没有使用箭头函数定义的方式。