使用扩展运算符克隆类实例时方法上下文绑定问题如何修复
问题根因
- 你在构造函数内为实例赋值的
setName是箭头函数,箭头函数的this是词法静态绑定,在构造函数执行、实例a创建时就已经绑定到a,后续无法被修改。 - 扩展运算符
...对类实例执行的是自身可枚举属性的浅拷贝,相当于直接把a上的name属性、已经绑定到a的setName方法原封不动复制到普通对象b上,因此调用b.setName时修改的仍然是a的name属性。
修复方案
方案1:将类方法移到原型上(推荐)
原型方法的this是动态绑定,调用时的执行上下文就是方法前的调用对象,不会被提前绑死:
class Person { constructor(name) { this.name = name; } // 不要在构造函数内写箭头函数方法,移到类原型上 setName(name) { this.name = name; } }
同时克隆类实例时不要直接用扩展运算符生成普通对象,正确的克隆方式是新建同类实例再拷贝属性:
const a = new Person('John'); // 方式1:传参构造 const b = new Person(a.name); // 方式2:属性多的情况下用Object.assign批量拷贝 const c = Object.assign(new Person(), a); b.setName('Paul'); console.log(b.name); // Paul console.log(a.name); // John 符合预期
方案2:保留箭头函数写法,新增类克隆方法
如果因为业务需要必须保留构造函数内的箭头函数(比如要避免方法作为回调传递时丢失上下文),可以在类内新增专属克隆方法,每次克隆都生成新的绑定了正确上下文的方法:
class Person { constructor(name) { this.name = name; this.setName = name => { this.name = name; }; } clone() { // 新建实例会自动生成绑定到新实例的setName方法 return new Person(this.name); } } const a = new Person('John'); const b = a.clone(); b.setName('Paul'); console.log(b.name); // Paul console.log(a.name); // John 符合预期
方案3:必须用扩展运算符克隆时重写方法
如果一定要用扩展运算符生成普通对象而非类实例,可以在拷贝时手动重写对应方法,覆盖旧的绑定了原始实例的方法:
const a = new Person('John'); const b = { ...a, // 重写setName,上下文绑定到新对象b setName(name) { this.name = name; } }; b.setName('Paul'); console.log(b.name); // Paul console.log(a.name); // John 符合预期
内容的提问来源于stack exchange,提问作者Juanjo
相关产品推荐
相关产品推荐

