现象
在我们日常开发当中,我们写一个组件,经常遇到需要传递一个方法给点击事件等等,那么我们经常是怎么做的呢?
class Item extends Component {jump = () => {this.setState({ xxx: true});
};
render() {
return (<div onClick={this.jump}> 跳转啰 </div>
);
}
}
如上代码,因为点击回调使用了 this,为了绑定 this,使用了箭头函数。或者不使用箭头函数了,用 bind 进行绑定。
class Item extends Component {jump() {this.setState({ xxx: true});
}
render() {
// 在 render 里进行 bind
return (<div onClick={::this.jump}> 跳转啰 </div>
);
}
}
亦或者是这样
class Item extends Component {constructor() {
// 在初始化就绑定
this.jump = this.jump.bind(this);
}
jump() {this.setState({ xxx: true});
}
render() {
return (<div onClick={this.jump}> 跳转啰 </div>
);
}
}
是不是!是不是!我相信以上 3 种绑定方式起码有 90% 的占有率!!!
这样的绑定坏处是什么
我们一个一个情况来分析。
箭头函数绑定
问题 1:
我们知道使用箭头函数的时候,他实际上该方法是放在了实例上面,而不是 prototype 上面,举个例子。
class A {
a = '1'
func1() {}
func2 = () => {};
}
const a = new A();
大家可以试试在控制台看一下 a 究竟长什么样子,如无意外他应该是介个样子的。
{
a: '1',
func2: () => {},
__proto__: {func1: function() {}}
}
那么我们想想,假设我们在一个列表 item 组件上使用箭头函数,实际上他的实例是怎么样的。
就是每一个实例上的箭头函数都不是在 prototype 公用的而是自己创建一个函数!那么类似在商品列表种的商品 item,你们现在还觉得使用箭头函数绑定 this 吗?
问题 2
相信大家都使用过了修饰器了,那么大家想想,修饰器 能修饰箭头函数吗?
答案是不能!
修饰器的输入是什么
function boundMethod(target, key, descriptor) {
return {// 新的 descriptor};
}
其中 target 是实例本身,key 是被修饰的 key,descriptor 是该属性的描述。
我们来看一下修饰 prototype 上的方法跟修饰箭头函数有什么不同。
const testDes = function() {return function(target, name, descriptor) {
return {
...descriptor,
value: function() {console.log('descaaaa')
descriptor.value();}
};
};
};
class A {@testDes(1)
func1() {}
@testDes(1)
func2 = () => {}
}
var a = new A();
a.func1();
a.func2();
console.log(a)
以上例子,我们发现只有 a.func1()=
才会有打印修饰的字符,修饰器是不能修饰 prototype 上不存在的属性的。
问题 3
大家想一想,如果一个类中使用了箭头函数,那么这个类可以被继承吗?
答案就是不可以了。
因为箭头函数并不声明在 prototype 上,所以该方法不能在子类上被使用,导致一种奇怪的错觉。
在 render 里 bind 绑定
其实这个问题比较明显,我们都知道 bind 方法是返回一个新的方法,就是在每次 render 的时候都会创建新的函数,销毁上一次的函数。在 render 触发比较频繁的时候,就会有非常多没有必要的创建销毁开销。
在 constructor 里 bind 绑定
这个方式其实跟箭头函数有相似之处,就是在实例本身再次赋值一个通过 bind 的方法,也就是多个实例之间各自都有一个方法,并且该类 prototype 上的方法就冗余了。
怎么解决
既然以上的绑定 this 都或多或少有不好的地方,但是实际使用我们经常需要绑定 this,那么怎么绑定 this 才是最好的呢?
答案就是使用修饰器进行绑定。
大家可以看一下 autobind-decorator
这个库,非常简单,使用修饰器进行绑定,就是直接在 prototype 上进行更改,多个实例共享没问题,继承也没毛病,该方法依旧可以被再次修饰。完美了!
看到这篇文章的你,知道怎么绑定 this 了吗?