问题 如何在原型继承中创建新对象时覆盖函数? [关闭]
从 这篇博文 我们在JavaScript中有一个原型继承的例子:
var human = {
name: '',
gender: '',
planetOfBirth: 'Earth',
sayGender: function () {
alert(this.name + ' says my gender is ' + this.gender);
},
sayPlanet: function () {
alert(this.name + ' was born on ' + this.planetOfBirth);
}
};
var male = Object.create(human, {
gender: {value: 'Male'}
});
var female = Object.create(human, {
gender: {value: 'Female'}
});
var david = Object.create(male, {
name: {value: 'David'},
planetOfBirth: {value: 'Mars'}
});
var jane = Object.create(female, {
name: {value: 'Jane'}
});
david.sayGender(); // David says my gender is Male
david.sayPlanet(); // David was born on Mars
jane.sayGender(); // Jane says my gender is Female
jane.sayPlanet(); // Jane was born on Earth
现在,我想知道的是一个人 正确 “覆盖”,例如, sayPlanet
功能?
我试过这样的:
jane.sayPlanet = function(){
console.log("something different");
};
这很有效。
但是,我也尝试过这样:
var jane = Object.create(female, {
name: {value: 'Jane'},
sayPlanet: function(){
console.log("something different");
}
});
但是我得到了一个类型错误。
我的问题是:
- 我怎么能添加
sayPlanet
里面的功能 Object.create
?
- 这是“一个好方法”还是有更好的(最佳实践)方式?
编辑:
我想办法如何添加 sayPlanet
在 - 的里面 Object.create
:
sayPlanet: {
value: function(){
console.log("something different");
}
}
但是,第二个问题仍然存在。另外,如果有人能够在更深层次上解释它,我会很感激,如果这是一个像这样使用它的“好方法”。
编辑#2: 正如马哈维尔所指出的那样,这是一个可怕的例子,因为事实证明你不能(如果我错了,请纠正我)改变 name
的 jane
一旦它过去了 Object.create
d。
编辑#3: (伙计哦,伙计,这会让我进入某个人们穿白大褂的设施)。正如@WhiteHat指出的那样,确实可以将name属性设置为可更新,如下所示:
var jane = Object.create(female, {
name: {
value: 'Jane',
writable: true
}
});
然后你就可以做到 jane.name="Jane v2.0";
。
我在这里说实话的人 - 我对于看似有这么多选择的方向不了解。就在今天,我读到Eric Elliot https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3 现在我不知道该怎么想,因为他继续争辩说ES6上的人并没有做得很好:O。嗯,我想我将不得不再次重新阅读Crockfords的书,决定“单程”,看看它需要多远。
1036
2017-09-22 11:04
起源
答案:
运用 Object.create
肯定是一种有效的方法,但这个例子本身在我看来有点误导了它的内部运作 Object.create
。博客文章非常好地总结了在javascript中创建对象的不同方法,但我不认为这个例子是 Object.create
很好地了解它是如何工作的,这更类似于 new/constructor
比看起来更接近的方法。
Object.create
允许基于a创建对象 prototype
,但没有 constructor
。这意味着 prototype chain
创建的对象不依赖于a constructor
,(这就是为什么它可能更容易遵循,通过构造函数链接的原型不是非常简单或易于遵循)。但 Object.create
仍然创造了一个 prototype chain
,以同样的方式 new
确实。
所以在你的例子中,当你定义时 name
在 human
例如这里:
var human = {
name: '',
然后当你创造 jane
:
var jane = Object.create(female, {
name: {value: 'Jane'}
你并没有真正为它分配一个值 name property
你定义的 human
。你实际上是在给jane添加一个属性。但 human.name
仍然是一个财产 prototype chain
的 jane
。它的工作原理是因为javascript将遵循原型链来查找第一个匹配的属性,但是 human.name
仍然以某种方式与之相关联 jane
。
看这里: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
如果使用构造函数,也会发生同样的事情:
var Person = function(gender, name) {
this.name = name;
}
var newPerson = new Person();
同样的道理 sayPlanet
功能。
这是一种有效的方法,但可能导致奇怪的行为。例如,您可以决定修改 sayPlanet
通过这种方式分配给所有人类:
human.sayPlanet = function(){console.log('new sayPlanet')}
这将适用于所有人 humans
,除了您给出的那些 sayPlanet
他们自己的财产。在您的情况下,这可能是预期的结果。但是,你还是要看看 sayPlanet
真的应该是人类的财产。
同 gender
,它适用于 human
,并在 male
和 female
。如此变化 human.gender
对任何一个都不起作用。但它仍然属于 human
,当你想使用这些对象时,这有点令人困惑。您基本上拥有一个已定义的属性,该属性是可写的,但更改后的属性根本没有效果。它主要表示您需要将哪个属性添加到人类实例或原型链中的某个位置。同样,它似乎被大量使用,但当用这种例子解释时,它以某种方式给人的印象是 Object.create
只是结合了属性,但它不是它的功能。
最后,您需要选择是否要使用 prototypes
或不。如果没有,那么功能继承可能是最好的方法。然后每个对象都是不同的,并且有自己的一组属性,您可以初始化,而不必担心 prototypes
。
如果你想使用 prototypes
,那你就可以用了 new/constructor
要么 Object.create
做法。但 Object.create
将以相同的方式创建原型链 new
它,它只是摆脱了构造函数。
如何做的一个小例子 Object.create
和 new
分享一些行为:
var human = {
name: '',
gender: '',
planetOfBirth: 'Earth',
sayGender: function () {
console.log(this.name + ' says my gender is ' + this.gender);
},
sayPlanet: function () {
console.log(this.name + ' was born on ' + this.planetOfBirth);
}
};
var male = Object.create(human, {
gender: {value: 'Male'}
});
var female = Object.create(human, {
gender: {value: 'Female'}
});
var david = Object.create(male, {
name: {value: 'David'},
planetOfBirth: {value: 'Mars', configurable: true}
});
var jane = Object.create(female, {
name: {value: 'Jane'},
sayPlanet: {value: function(){
console.log("something different");
}, writable: true, enumerable: true, configurable: true
}
});
var Male = function(name){ // in this case the real constructor is female or male. Name is the only property that 'needs' to be initialized
this.name = name;
this.planetOfBirth = 'Jupiter';
}
Male.prototype = Object.create(male);
var john = new Male('John')
david.sayGender(); // David says my gender is Male
david.sayPlanet(); // David was born on Mars
jane.sayGender(); // Jane says my gender is Female
jane.sayPlanet(); // Jane was born on Earth
john.sayGender(); // John says my gender is Female
john.sayPlanet(); // John was born on Earth
delete david.planetOfBirth; //just to show how human properties will still be in the chain even if you delete them
delete john.name; // It's true also if you use new.
delete jane.sayPlanet;
console.log('\n','----after deleting properties----');
david.sayPlanet();
jane.sayPlanet();
john.sayGender();
human.planetOfBirth = 'Venus'; // This will apply to all humans, even the ones already created
console.log('\n','----after assigning planetOfBirth on human----');
david.sayPlanet();
jane.sayPlanet();
john.sayPlanet(); // John still has planetOfBirth as its own property, since it wasn't deleted
delete john.planetOfBirth;
console.log('\n','----after deleting john planetOfBirth----');
john.sayPlanet(); // But it's still there
事实上(只是为了更加混乱),有些人结合起来 Object.create
同 new/constructor
或者使用功能继承。像这样的东西例如:
https://john-dugan.com/object-oriented-javascript-pattern-comparison/#oloo-pattern
应用于您的示例,它将提供如下内容:
var human = {
planetOfBirth: 'Earth',
sayGender: function () {
console.log(this.name + ' says my gender is ' + this.gender);
},
sayPlanet: function () {
console.log(this.name + ' was born on ' + this.planetOfBirth);
},
init: function(name){
this.name = name;
}
};
var male = Object.create(human, {
gender: {value: 'Male'} // This is part of male/female prototype and can't be written, which seems logical
});
var female = Object.create(human, {
gender: {value: 'Female'}
});
var david = Object.create(male).init('David');
david.planetOfBirth = 'Mars';
var jane = Object.create(female).init('Jane')
jane.sayPlanet = function(){console.log('something different')};
var john = Object.create(male).init('John');
john.planetOfBirth = 'Jupiter';
david.sayGender(); // David says my gender is Male
david.sayPlanet(); // David was born on Mars
jane.sayGender(); // Jane says my gender is Female
jane.sayPlanet(); // Jane was born on Earth
john.sayGender(); // John says my gender is Female
john.sayPlanet(); // John was born on Earth
delete david.planetOfBirth; // Overridden properties will still exists after delete, but not the others.
delete john.name;
delete jane.sayPlanet;
console.log('\n','----after deleting properties----');
david.sayPlanet();
jane.sayPlanet();
john.sayPlanet();
human.planetOfBirth = 'Venus'; // This will apply to all humans, even the ones already created.
// But not for humans woth overridden planetOfBirth.
console.log('\n','----after assigning planetOfBirth on human----');
david.sayPlanet();
jane.sayPlanet();
john.sayPlanet(); // John's name is now undefinded
delete john.planetOfBirth;
console.log('\n','----after deleting john planetOfBirth----');
john.sayPlanet(); //
不一定更好,但它也有效,在我看来有一定的优势。
无论如何,正如其他人所说,似乎没有标准或默认的方式来做到这一点。
4
2017-10-03 12:02
我学到的最佳实践是在Object原型级别定义写权限。我从阅读Addy Osmani的JavaScript设计模式中学到了这种技术,它在网上非常有信誉和开源: http://addyosmani.com/resources/essentialjsdesignpatterns/book/
请查看以下示例中的sayPlanet属性。请记住,您不需要将所有其他属性“可写”设置为false,我只在我的示例代码中进行了说明。与其他仍然有效的方法相比,这种方法提供了更大的灵活性和可重用性。在这里,您可能会注意到这是 Object.defineProperties
原型中的语法。
var human = {
name: {
value: '',
writable: false //only to illustrate default behavior not needed
},
gender: {
value: '',
writable: false //only to illustrate default behavior not needed
},
planetOfBirth: {
value: "Earth",
configurable: false //only to illustrate default behavior not needed
}
};
//here we define function properties at prototype level
Object.defineProperty(human, 'sayGender', {
value: function() {
alert(this.name + ' says my gender is ' + this.gender);
},
writable: false
});
Object.defineProperty(human, 'sayPlanet', {
value: function() {
alert(this.name + ' was born on ' + this.planetOfBirth);
},
writable: true
});
//end definition of function properties
var male = Object.create(human, {
gender: {
value: 'Male'
}
});
var female = Object.create(human, {
gender: {
value: 'Female'
}
});
var david = Object.create(male, {
name: {
value: 'David'
},
planetOfBirth: {
value: 'Mars'
}
});
var jane = Object.create(female, {
name: {
value: 'Jane'
}
});
//define the writable sayPlanet function for Jane
jane.sayPlanet = function() {
alert("something different");
};
//test cases
//call say gender before attempting to ovverride
david.sayGender(); // David says my gender is Male
//attempt to override the sayGender function for david
david.sayGender = function() {
alert("I overrode me!")
};
//I tried and failed to change an unwritable fucntion
david.sayGender(); //David maintains that my gender is Male
david.sayPlanet(); // David was born on Mars
jane.sayGender(); // Jane says my gender is Female
jane.sayPlanet(); // something different
6
2017-10-04 22:53
来自MDN ......
第二个论点是 Object.create
要求一个 propertiesObject。
的语法部分 Object.defineProperties
最好地描述,见 道具。
简而言之,第二个论点传递给了 Object.create
应该具有可枚举的属性,每个属性都包含以下一个或多个键...
配置
枚举
值
写
得到
组
你应该为每一个解决这些问题 班级成员 你分配。
取决于预期的功能......
从您的问题中的示例中提取,为了在创建对象后更改值,只需添加 writable: true
。
var human = {
name: '',
gender: '',
planetOfBirth: 'Earth',
sayGender: function () {
console.log(this.name + ' says my gender is ' + this.gender);
},
sayPlanet: function () {
console.log(this.name + ' was born on ' + this.planetOfBirth);
}
};
var male = Object.create(human, {
gender: {
value: 'Male',
writable: true
}
});
var david = Object.create(male, {
name: {
value: 'David',
writable: true
},
planetOfBirth: {
value: 'Mars',
writable: false //only born once
}
});
david.sayGender();
david.sayPlanet();
david.gender = 'Female';
david.name = 'Caitlyn';
david.planetOfBirth = 'Venus'; //(will not work, not writable)
david.sayGender();
david.sayPlanet();
3
2017-09-29 20:20
实现继承的方法很糟糕,我对这个例子感觉不太好。
如果您想要更改某些值,比如在创建对象后的某个时间更改“planetOfBirth / gender”,该怎么办?
- 由于对象属性是默认不可枚举的,因此不是
可配置,不可写。
最佳实践总是取决于您的模型,结构和最终目标。
以下分析是一种方式。
//Following the same example: lets understand this like a real life problem
//Creates human objects
var human = {
name: '',
gender: '',
planetOfBirth: 'Earth',
sayGender: function () {
console.log(this.name + ' says my gender is ' + this.gender);
},
sayPlanet: function () {
console.log(this.name + ' was born on ' + this.planetOfBirth);
}
};
//Creates Person constructor to use later to create Male/Female objects like David/Jane
var Person = function(gender, name) {
this.gender = gender;
this.name = name;
};
//Assigning the human as the prototype of Person constructor
Person.prototype = Object.create(human);
//Setter function to change the planet of birth of a person
Person.prototype.setPlanetOfBirth = function(planetOfBirth){
this.planetOfBirth = planetOfBirth;
};
//Creating david object using Person constructor
var david = new Person('Male', 'David');
//change the planet of birth for David as Mars
david.setPlanetOfBirth('Mars');
//Creating jane object using Person constructor
var jane = new Person('Female', 'Jane');
//A object specific function the will only available to Jane to use
jane.sayPlanet = function(){
console.log("something different");
};
david.sayGender(); // David says my gender is Male
david.sayPlanet(); // David was born on Mars
jane.sayGender(); // Jane says my gender is Female
jane.sayPlanet(); // something different
即使经过这么多天,我也很困惑并且更多地深入研究JavaScript对象的概念。
我相信,没有这样的官方文件。下面的一些文档可能会帮助您理解JavaScript对象和继承。
面向对象的JavaScript简介
的Object.create()
为什么有必要设置原型构造函数?
原型继承
关于JavaScript中继承的常见误解
搜索: 面向对象的JavaScript.pdf
2
2017-10-01 06:10
1 - 它应该像下面的代码块:
var jane = Object.create(Female.prototype, {
name: {value: 'Jane'},
sayPlanet:{ value : function(){ alert("something different");}}
});
2 - 这个解决方案很好,你可以看到这个 参考
0
2017-09-22 11:51
我不确定javascript中是否存在原型继承的最佳实践,但将原型分配给对象的经典方法是创建构造函数:
var human = {
sayPlanet:function(){
// old sayPlanet
}
}
function Person(name, sayPlanet)
{
this.name = name;
this.sayPlanet = sayPlanet;
}
Person.prototype = human;
// use your constructor function
var david = new Person('david', function(){/*new sayPlanet*/});
还有 class .. extends
在ES6标准中提出,但你必须记住它是javascript现有的基于原型的继承的语法糖,并且在javascript中仍然没有本机类。
0
2017-10-01 05:14