Knockout可观察属性在JS原型继承中出现值覆盖问题如何解决?
问题结论
这不是Knockout的bug,是JavaScript原型链的原生特性导致的问题。
原因分析
- 普通字符串属性版本运行正常的原因:你在
Orange、Apple构造函数中执行this.fruitName = "xxx"时,会直接在当前实例对象上新增自有属性fruitName,覆盖原型链上的同名属性,每个实例的属性完全独立,因此输出符合预期。 - 替换为Knockout observable后输出异常的原因:你将
fruitName定义为Fruit.prototype上的属性,而observable本质是引用类型的函数对象,所有继承自Fruit的实例默认共享原型上的同一个observable。你在子类构造函数中执行this.fruitName("xxx")时,并没有给实例新增自有属性,只是调用了原型上共享的observable的赋值方法,后执行的Apple实例赋值会覆盖之前Orange实例的赋值,最终两个实例调用都返回最后设置的I am an apple。 - 原有代码额外隐患:调用父类构造函数时写的
Fruit.call()没有传入当前实例this,会导致父类构造函数内的this指向错误;另外Apple.prototype.constructor被错误赋值为Orange,属于笔误。
解决方法
不要把observable属性定义在父类原型上,改为在父类构造函数内部初始化,保证每个实例都拥有独立的observable:
function Fruit(){ // 每个实例单独创建observable,不会共享 this.fruitName = ko.observable("I am a fruit"); this.who = function(){ console.dir(this.fruitName()); } } function Orange(){ // 调用父类构造函数时传入当前实例this Fruit.call(this); this.fruitName("I am an orange"); } Orange.prototype = new Fruit(); Orange.prototype.constructor = Orange; function Apple(){ Fruit.call(this); this.fruitName("I am an apple"); } Apple.prototype = new Fruit(); // 修正constructor指向的笔误 Apple.prototype.constructor = Apple; var orange = new Orange(); var apple = new Apple(); orange.who(); // 输出 I am an orange apple.who(); // 输出 I am an apple
内容的提问来源于stack exchange,提问作者GETah
相关产品推荐
相关产品推荐

