为何使用Object.assign无法将类实例方法赋值给任意对象?
var foo = {}; class bar { fiddle() { alert('boo'); } } Object.assign(foo, new bar()); foo.fiddle();
这段代码运行时报错:foo.fiddle is not a function,为什么会这样?
实际场景中,我需要把类的fiddle方法复制到另一类的已有实例中,试过类似otherInstance['fiddle'] = bar['fiddle']的写法,但不可行。
我注意到普通对象的方法可以通过Object.assign正常复制:
let x = {a: y => y * 2} let z = Object.assign({}, x) console.log(z.a(5)); // 10
简化版:
let calculator = {double: y => y * 2} let clone = Object.assign({}, calculator) alert(clone.double(5)); // 10
修改版:
let calculator = {double: y => y * 2} let clone = {} Object.assign(clone, calculator) alert(clone.double(5)); // 10
推测问题和原型机制有关,但不确定具体原因。
更新1
目前我把类改成普通对象暂时解决了问题:
class foo { foodle() { console.log('1') } } const bar = { fiddle: () => { console.log('2'); } } var test = new foo(); var test2 = Object.create(bar); Object.assign(test, bar); test.foodle(); // 1 test.fiddle(); // 2
但我更希望全程用类开发组件,有没有恢复类写法的方案?
更新2
我发现这种写法是可行的:
var foo = {} class bar { fiddle() { alert('boo'); } } var barInstance = new bar(); foo['fiddle'] = barInstance['fiddle']; foo.fiddle(); // boo
核心原因:类方法存储在原型链上,而非实例自身
ES6类中定义的方法(比如bar里的fiddle)默认挂载在类的原型对象(bar.prototype)上,并不是直接存在于类实例(new bar())自身的属性中。
而Object.assign()仅复制源对象自身的可枚举属性,不会遍历原型链上的属性。所以当你用Object.assign(foo, new bar())时,new bar()实例自身没有fiddle属性,自然不会复制到foo里,也就出现了foo.fiddle is not a function的报错。
对比普通对象的情况:普通对象里定义的方法是直接作为对象自身属性存在的,所以Object.assign可以正常复制。
针对类场景的解决方案
方案1:直接从原型上获取方法赋值
既然方法在原型上,你可以直接从类的原型对象或者实例的原型链上拿到方法,再赋值给目标对象:
var foo = {}; class bar { fiddle() { alert('boo'); } } // 方式1:从类的原型上取 foo.fiddle = bar.prototype.fiddle; foo.fiddle(); // 正常弹出'boo' // 方式2:从实例的原型链上取 const barInstance = new bar(); foo.fiddle = barInstance.__proto__.fiddle; foo.fiddle(); // 同样有效
方案2:批量复制原型上的方法
如果需要批量复制类原型上的所有方法,可以遍历原型对象的属性手动赋值:
var foo = {}; class bar { fiddle() { alert('boo'); } anotherMethod() { console.log('another'); } } // 遍历bar原型上的所有自有属性,跳过constructor Object.getOwnPropertyNames(bar.prototype).forEach(key => { if (key !== 'constructor') { foo[key] = bar.prototype[key]; } }); foo.fiddle(); // 'boo' foo.anotherMethod(); // 'another'
方案3:修改目标对象的原型链(适合组件继承场景)
如果你的需求是让目标实例拥有类的所有方法,还可以直接修改目标对象的原型链,实现原型继承:
class foo { foodle() { console.log('1'); } } class bar { fiddle() { console.log('2'); } } const test = new foo(); // 让test的原型继承bar的原型,同时保留原foo的原型 Object.setPrototypeOf(test, Object.create(bar.prototype)); Object.setPrototypeOf(test.__proto__, foo.prototype); test.foodle(); // 1 test.fiddle(); // 2
这种方式符合原型继承逻辑,不会重复复制方法,适合组件复用场景。
不推荐方案:将方法定义为实例自身属性
如果一定要让Object.assign直接复制,可以把方法定义在类的构造函数里,让方法成为实例自身属性:
class bar { constructor() { this.fiddle = () => { alert('boo'); } } } var foo = {}; Object.assign(foo, new bar()); foo.fiddle(); // 正常运行
但这种方式会让每个实例都创建一份方法副本,浪费内存,违背类的原型复用设计,不推荐在组件开发中使用。
内容的提问来源于stack exchange,提问作者BLAZORLOVER

