在原型继承中创建新对象时如何覆盖函数?
How to override a function when creating a new object in the prototypal inheritance?
从 this blog post 我们在 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
现在,我想知道 如何正确地 "override",例如 sayPlanet
函数?
我这样试过:
jane.sayPlanet = function(){
console.log("something different");
};
这有效。
不过,我也这样试过:
var jane = Object.create(female, {
name: {value: 'Jane'},
sayPlanet: function(){
console.log("something different");
}
});
但我收到类型错误。
我的问题是:
- 如何在
Object.create
中添加 sayPlanet
函数?
- 这是根本"a good way"还是有更好的(最佳实践)方法?
编辑:
我想出了一种方法可以在 Object.create
:
中添加 sayPlanet
sayPlanet: {
value: function(){
console.log("something different");
}
}
然而,还有第二个问题。另外,如果有人可以更深入地解释它,如果这是 "a good way" 像这样使用它,我将不胜感激。
编辑 #2: 正如 Mahavir 在下面指出的那样,这是一个糟糕的例子,因为事实证明你不能(请纠正我,如果我我错了) 更改 jane
的 name
一旦 Object.create
d.
编辑 #3:(伙计,伙计,这会让我进入某个人们穿着白大衣的设施)。正如@WhiteHat 在下面指出的那样,您确实可以将名称 属性 设置为可更新,如下所示:
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 的书,决定 "one way",看看我能走多远。
1-它应该像下面的代码块:
var jane = Object.create(Female.prototype, {
name: {value: 'Jane'},
sayPlanet:{ value : function(){ alert("something different");}}
});
2-这个解决方案很好,你可以看到这个reference
来自 MDN...
Object.create
的第二个参数调用 propertiesObject。
Object.defineProperties
的语法部分描述得最好,请参阅 props。
简而言之,传递给 Object.create
的第二个参数应该具有可枚举属性,每个属性具有一个或多个以下键...
configurable
enumerable
value
writable
get
set
您应该为您分配的每个 class 成员 解决这些问题。
取决于预期的功能...
根据您问题中的示例,为了在创建对象后更改值,只需添加 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();
我不确定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*/});
ES6标准中也提出了class .. extends
,但是你必须记住它是对javascript现有的基于原型的继承的语法糖并且仍然没有原生类 在 javascript 中。
实现继承的方式非常糟糕,我对这个例子真的不太满意。
如果您想更改某些值怎么办,请在创建对象后的某个时间说 '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 对象和继承。
Introduction to Object-Oriented JavaScript
Why is it necessary to set the prototype constructor?
使用 Object.create
当然是一种有效的方法,但在我看来,这个例子本身对 Object.create
的内部工作原理有点误导。博客 post 很好地总结了在 javascript 中创建对象的不同方法,但我认为 Object.create
的示例并没有很好地说明它是如何工作的,这比看起来更类似于 new/constructor
方法。
Object.create
允许创建基于 prototype
但没有 constructor
的对象。这意味着创建对象的 prototype chain
不依赖于 constructor
,(这就是为什么它可能更容易理解,这个通过构造函数链接的原型不是很直接或容易理解).但是 Object.create
仍然会创建一个 prototype chain
,就像 new
一样。
所以在你的例子中,当你在 human
中定义 name
例如这里:
var human = {
name: '',
然后当你创建 jane
:
var jane = Object.create(female, {
name: {value: 'Jane'}
您实际上并没有为在 human
中定义的 name property
赋值。您实际上是在向 jane 添加 属性。但是 human.name
仍然是 jane
的 prototype chain
中的 属性。它之所以有效,是因为 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(); //
不一定更好,但效果也不错,而且在我看来有一定的优势。
无论如何,正如其他人所说,似乎没有执行此操作的标准或默认方法。
据我所知,最佳做法是在对象原型级别定义写入权限。我从阅读 Addy Osmani 的 JavaScript 设计模式中学到了这项技术,它非常有名并且在线开源:http://addyosmani.com/resources/essentialjsdesignpatterns/book/
请查看下面示例中的 sayPlanet 属性。请记住,您不需要将所有其他属性的 "writeable" 设置为 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
从 this blog post 我们在 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
现在,我想知道 如何正确地 "override",例如 sayPlanet
函数?
我这样试过:
jane.sayPlanet = function(){
console.log("something different");
};
这有效。
不过,我也这样试过:
var jane = Object.create(female, {
name: {value: 'Jane'},
sayPlanet: function(){
console.log("something different");
}
});
但我收到类型错误。
我的问题是:
- 如何在
Object.create
中添加sayPlanet
函数? - 这是根本"a good way"还是有更好的(最佳实践)方法?
编辑:
我想出了一种方法可以在 Object.create
:
sayPlanet
sayPlanet: {
value: function(){
console.log("something different");
}
}
然而,还有第二个问题。另外,如果有人可以更深入地解释它,如果这是 "a good way" 像这样使用它,我将不胜感激。
编辑 #2: 正如 Mahavir 在下面指出的那样,这是一个糟糕的例子,因为事实证明你不能(请纠正我,如果我我错了) 更改 jane
的 name
一旦 Object.create
d.
编辑 #3:(伙计,伙计,这会让我进入某个人们穿着白大衣的设施)。正如@WhiteHat 在下面指出的那样,您确实可以将名称 属性 设置为可更新,如下所示:
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 的书,决定 "one way",看看我能走多远。
1-它应该像下面的代码块:
var jane = Object.create(Female.prototype, {
name: {value: 'Jane'},
sayPlanet:{ value : function(){ alert("something different");}}
});
2-这个解决方案很好,你可以看到这个reference
来自 MDN...
Object.create
的第二个参数调用 propertiesObject。
Object.defineProperties
的语法部分描述得最好,请参阅 props。
简而言之,传递给 Object.create
的第二个参数应该具有可枚举属性,每个属性具有一个或多个以下键...
configurable
enumerable
value
writable
get
set
您应该为您分配的每个 class 成员 解决这些问题。
取决于预期的功能...
根据您问题中的示例,为了在创建对象后更改值,只需添加 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();
我不确定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*/});
ES6标准中也提出了class .. extends
,但是你必须记住它是对javascript现有的基于原型的继承的语法糖并且仍然没有原生类 在 javascript 中。
实现继承的方式非常糟糕,我对这个例子真的不太满意。
如果您想更改某些值怎么办,请在创建对象后的某个时间说 '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 对象和继承。
Introduction to Object-Oriented JavaScript
Why is it necessary to set the prototype constructor?
使用 Object.create
当然是一种有效的方法,但在我看来,这个例子本身对 Object.create
的内部工作原理有点误导。博客 post 很好地总结了在 javascript 中创建对象的不同方法,但我认为 Object.create
的示例并没有很好地说明它是如何工作的,这比看起来更类似于 new/constructor
方法。
Object.create
允许创建基于 prototype
但没有 constructor
的对象。这意味着创建对象的 prototype chain
不依赖于 constructor
,(这就是为什么它可能更容易理解,这个通过构造函数链接的原型不是很直接或容易理解).但是 Object.create
仍然会创建一个 prototype chain
,就像 new
一样。
所以在你的例子中,当你在 human
中定义 name
例如这里:
var human = {
name: '',
然后当你创建 jane
:
var jane = Object.create(female, {
name: {value: 'Jane'}
您实际上并没有为在 human
中定义的 name property
赋值。您实际上是在向 jane 添加 属性。但是 human.name
仍然是 jane
的 prototype chain
中的 属性。它之所以有效,是因为 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(); //
不一定更好,但效果也不错,而且在我看来有一定的优势。
无论如何,正如其他人所说,似乎没有执行此操作的标准或默认方法。
据我所知,最佳做法是在对象原型级别定义写入权限。我从阅读 Addy Osmani 的 JavaScript 设计模式中学到了这项技术,它非常有名并且在线开源:http://addyosmani.com/resources/essentialjsdesignpatterns/book/
请查看下面示例中的 sayPlanet 属性。请记住,您不需要将所有其他属性的 "writeable" 设置为 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