在 Javascript 中扩展对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10430279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Extending an Object in Javascript
提问by Wituz
I am currently transforming from Java to Javascript, and it's a bit hard for me to figure out how to extend objects the way I want it to do.
我目前正在从 Java 转换为 Javascript,要弄清楚如何以我想要的方式扩展对象对我来说有点困难。
I've seen several people on the internet use a method called extend on object. The code will look like this:
我在互联网上看到有几个人使用一种称为扩展对象的方法。代码如下所示:
var Person = {
name : 'Blank',
age : 22
}
var Robot = Person.extend({
name : 'Robo',
age : 4
)}
var robot = new Robot();
alert(robot.name); //Should return 'Robo'
Does anyone know how to make this work? I've heard that you need to write
有谁知道如何使这项工作?我听说你需要写
Object.prototype.extend = function(...);
But I don't know how to make this system work. If it is not possible, please show me another alternative that extends an object.
但我不知道如何使这个系统工作。如果不可能,请告诉我另一个扩展对象的替代方案。
回答by osahyoun
You want to 'inherit' from Person's prototype object:
您想从 Person 的原型对象“继承”:
var Person = function (name) {
this.name = name;
this.type = 'human';
};
Person.prototype.info = function () {
console.log("Name:", this.name, "Type:", this.type);
};
var Robot = function (name) {
Person.apply(this, arguments);
this.type = 'robot';
};
Robot.prototype = Person.prototype; // Set prototype to Person's
Robot.prototype.constructor = Robot; // Set constructor back to Robot
person = new Person("Bob");
robot = new Robot("Boutros");
person.info();
// Name: Bob Type: human
robot.info();
// Name: Boutros Type: robot
回答by Calvintwr
World without the "new" keyword.
没有“new”关键字的世界。
And simpler "prose-like" syntax with Object.create().
使用 Object.create() 更简单的“散文式”语法。
Javascript is inherently a classless language, which can be written in a very simple and prose-like manner. In fact, the simple form I am about to illustrate below is the true prototypal naturethat exposes Javascript's special abilities. However, it has been overwhelming obscured by programmers who wrote it like a classical language, as though code needed to look complicated in order to work.
Javascript 本质上是一种无类语言,可以以非常简单和散文式的方式编写。事实上,我将在下面说明的简单形式是暴露 Javascript 特殊能力的真正原型性质。然而,它已经被像经典语言一样编写它的程序员压倒了,好像代码需要看起来很复杂才能工作。
TLDR;
TLDR;
const Person = { name: 'Anonymous' } // person has a name
const Hyman = Object.create(Person) // Hyman is a person
Hyman.name = 'Hyman' // and has a name 'Hyman'
No, you don't need constructors, no new
instantiation (read why you shouldn't use new
), no super
, no funny funny __construct
. You simply create Objects and then extend or morph them.
不,你不需要构造函数,不需要new
实例化(阅读为什么你不应该使用new
),不super
,没有有趣的滑稽__construct
。您只需创建对象,然后扩展或变形它们。
(If you know about getters and setters, see "Further reading" section to see how this pattern gives you free getters and setters in a way Javascript had originallyintended for, and how powerful they are.)
(如果您了解 getter 和 setter,请参阅“进一步阅读”部分,了解此模式如何以 Javascript最初打算的方式为您提供免费的 getter 和 setter ,以及它们的强大功能。)
Prose-like syntax: Base protoype
散文式语法:基本原型
const Person = {
//attributes
firstName : 'Anonymous',
lastName: 'Anonymous',
birthYear : 0,
type : 'human',
//methods
name() { return firstName + ' ' + lastName },
greet() {
console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' )
},
age() {
// age is a function of birth time.
}
}
const person = Object.create(Person). // that's it!
At a glance, it almost looks like filling up a paper application form.
乍一看,这几乎就像填写纸质申请表一样。
Extension, creating an instance of Person
扩展,创建一个实例 Person
const Skywalker = Object.create(Person)
Skywalker.lastName = 'Skywalker'
const anakin = Object.create(Skywalker)
anakin.firstName = 'Anakin'
anakin.birthYear = '442 BBY'
anakin.gender = 'male' // you can attach new properties.
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
Person.isPrototypeOf(Skywalker) // outputs true
Person.isPrototypeOf(anakin) // outputs true
Skywalker.isPrototypeOf(anakin) // outputs true
Compare to the "classical" equivalent:
与“经典”等价物相比:
function Person (firstName, lastName, birthYear, type) {
this.firstName = firstName
this.lastName = lastName
this.name = firstName + ' ' + lastName
this.birthYear = birthYear
this.type = type
}
Person.prototype.greet = function() { ... }
Person.prototype.age = function() { ... }
const Skywalker = new Person('', 'Skywalker', 0, 'human')
const anakin = new Skywalker('Anakin', 'Skywalker', 0, 'human')
Person.isPrototypeOf(Skywalker) // returns false!
For ages many people broke Javascript's prototype chainwithout realising. And they wrote many stackoverflow answers (just scroll down).
多年来,许多人在没有意识到的情况下破坏了 Javascript 的原型链。 他们写了许多 stackoverflow 答案(只需向下滚动)。
To salvage this, you need something even less readable:
为了解决这个问题,你需要一些更不可读的东西:
function Skywalker (firstName, birthYear, gender) {
Person.call(this, firstName, 'Skywalker', birthYear, 'human', gender) // yucks
this.gender = gender
}
Skywalker.prototype = Object.create(Person.prototype)
Why go through this trouble? In contrast with the prose-like syntax above.
为什么要经历这个麻烦?与上面类似散文的语法相反。
Now let's continue...
现在让我们继续...
Branching the base prototype
分支基本原型
// create a `Robot` prototype by extending the `Person` prototype:
const Robot = Object.create(Person)
Robot.type = 'robot'
Robot.variant = '' // add properties for Robot prototype
Attach methods unique to Robot
附加独特的方法 Robot
// Robots speak in binaries, so we need a different greet function:
Robot.machineGreet = function() { /*some function to convert strings to binary */ }
// morphing the `Robot` object doesn't affect `Person` prototypes
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
anakin.machineGreet() // error
Checking inheritance
检查继承
Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false
You have got everything you need already! No constructors, no instantiation. Clean, clear prose.
你已经拥有了你需要的一切!没有构造函数,没有实例化。干净,清晰的散文。
So why is this syntax not widely adopted? Well, people want to write Javascript like how they write other languages, and Javascript is compelled to look familiar. But Javascript really isn't like any language out there; syntax-wise it's far better.
那么为什么这种语法没有被广泛采用呢?好吧,人们希望像编写其他语言一样编写 Javascript,而 Javascript 被迫看起来很熟悉。但是 Javascript 真的不像任何语言;在语法上它要好得多。
Further reading
进一步阅读
Writability, Configurability and Free Getters and Setters!
可写性、可配置性和免费的 Getter 和 Setter!
For free getters and setters, or extra configuration, you can use Object.create()'s second argument a.k.a propertiesObject. It is also available in #Object.defineProperty, and #Object.defineProperties.
对于免费的 getter 和 setter,或额外的配置,您可以使用 Object.create() 的第二个参数,也就是 propertiesObject。它也可以在#Object.defineProperty和#Object.defineProperties 中使用。
To illustrate how powerful this is, suppose we want all Robot
to be strictly made of metal (via writable: false
), and standardise powerConsumption
values (via getters and setters).
为了说明这有多强大,假设我们希望所有东西都Robot
严格由金属制成(通过writable: false
),并标准化powerConsumption
值(通过 getter 和 setter)。
const Robot = Object.create(Person, {
// define your property attributes
madeOf: {
value: "metal",
writable: false,
configurable: false,
enumerable: true
},
// getters and setters, how javascript had (naturally) intended.
powerConsumption: {
get() { return this._powerConsumption },
set(value) {
if (value.indexOf('MWh')) return this._powerConsumption = value.replace('M', ',000k')
this._powerConsumption = value
throw new Error('Power consumption format not recognised.')
}
}
})
const newRobot = Object.create(Robot)
newRobot.powerConsumption = '5MWh'
console.log(newRobot.powerConsumption) // outputs 5,000kWh
And all prototypes of Robot
cannot be madeOf
something else because writable: false
.
并且所有的原型Robot
都不能是madeOf
别的东西,因为writable: false
.
const polymerRobot = Object.create(Robot)
polymerRobot.madeOf = 'polymer'
console.log(polymerRobot.madeOf) // outputs 'metal'
Mixins (using #Object.assign)
混合(使用#Object.assign)
const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped.
// You get the point by now.
Can you sense where this is going...?
你能感觉到这是要去哪里吗......?
Object.assign(darthVader, Robot)
Darth Vader gets the methods of Robot
:
达斯维达获得以下方法Robot
:
darthVader.greet() // inherited from `Person`, outputs "Hi, my name is Darth Vader..."
darthVader.machineGreet() // inherited from `Robot`, outputs 001010011010...
Along with other odd things:
以及其他奇怪的事情:
console.log(darthVader.type) // outputs robot.
Robot.isPrototypeOf(darthVader) // returns false.
Person.isPrototypeOf(darthVader) // returns true.
Well, whether Darth Vader is man or machine is indeed subjective:
好吧,Darth Vader 是人还是机器确实是主观的:
"He's more machine now than man, twisted and evil." - Obi-Wan Kenobi
"I know there is good in you." - Luke Skywalker
“他现在比人更像机器,扭曲而邪恶。” - 欧比旺·克诺比
“我知道你身上有优点。” - 卢克·天行者
Extra - Slightly shorter syntax with #Object.assign
额外 - #Object.assign 的语法略短
In all likelihood, this pattern shortens your syntax. But ES6 #Object.assign can shorten some more (For polyfill to use on older browsers, see MDN on ES6).
这种模式很可能会缩短您的语法。但是 ES6 #Object.assign 可以缩短一些(要在旧浏览器上使用 polyfill,请参阅ES6 上的 MDN)。
//instead of this
const Robot = Object.create(Person)
Robot.name = "Robot"
Robot.madeOf = "metal"
//you can do this
const Robot = Object.create(Person)
Object.assign(Robot, {
name: "Robot",
madeOf: "metal"
// for brevity, you can imagine a long list will save more code.
})
回答by tomilay
If you haven't yet figured out a way, use the associative property of JavaScript objects to add an extend function to the Object.prototype
as shown below.
如果你还没有想出办法,可以使用 JavaScript 对象的关联属性来添加一个扩展函数,Object.prototype
如下所示。
Object.prototype.extend = function(obj) {
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
this[i] = obj[i];
}
}
};
You can then use this function as shown below.
然后,您可以使用此功能,如下所示。
var o = { member: "some member" };
var x = { extension: "some extension" };
o.extend(x);
回答by Lior Elrom
Different approach: Object.create
不同的方法:Object.create
Per @osahyoun answer, I find the following as a better and efficient way to 'inherit' from Person's prototype object:
根据@osahyoun 的回答,我发现以下是从 Person 的原型对象“继承”的更好更有效的方法:
function Person(name){
this.name = name;
this.type = 'human';
}
Person.prototype.info = function(){
console.log("Name:", this.name, "Type:", this.type);
}
function Robot(name){
Person.call(this, name)
this.type = 'robot';
}
// Set Robot's prototype to Person's prototype by
// creating a new object that inherits from Person.prototype,
// and assigning it to Robot.prototype
Robot.prototype = Object.create(Person.prototype);
// Set constructor back to Robot
Robot.prototype.constructor = Robot;
Create new instances:
创建新实例:
var person = new Person("Bob");
var robot = new Robot("Boutros");
person.info(); // Name: Bob Type: human
robot.info(); // Name: Boutros Type: robot
Now, by using Object.create:
现在,通过使用Object.create:
Person.prototype.constructor !== Robot
Check also the MDNdocumentation.
另请查看MDN文档。
回答by KrIsHnA
In ES6, you may use spread operator like
在 ES6 中,您可以使用扩展运算符,例如
var mergedObj = { ...Obj1, ...Obj2 };
Note that Object.assign() triggers setters whereas spread syntax doesn't.
请注意, Object.assign() 会触发 setter,而 spread 语法则不会。
For more info see link, MDN -Spread Syntax
有关更多信息,请参阅链接,MDN -Spread Syntax
Old Answer :
旧答案:
In ES6, there is Object.assign
for copying property values. Use {}
as first param if you don't want to modify the target object (the first param passed).
在 ES6 中,有Object.assign
用于复制属性值。{}
如果您不想修改目标对象(传递的第一个参数),请用作第一个参数。
var mergedObj = Object.assign({}, Obj1, Obj2);
For more details see link, MDN - Object.assign()
有关更多详细信息,请参阅链接,MDN - Object.assign()
In case if you need is a Polyfill for ES5, the link offers it too. :)
如果您需要ES5的Polyfill,该链接也提供了它。:)
回答by Harold
And another year later, I can tell you there is another nice answer.
又一年后,我可以告诉你还有另一个很好的答案。
If you don't like the way prototyping works in order to extend on objects/classes, take alook at this: https://github.com/haroldiedema/joii
如果您不喜欢原型设计以扩展对象/类的方式,请查看以下内容:https: //github.com/haroldiedema/joii
Quick example code of possibilities (and many more):
可能性的快速示例代码(以及更多):
var Person = Class({
username: 'John',
role: 'Employee',
__construct: function(name, role) {
this.username = name;
this.role = role;
},
getNameAndRole: function() {
return this.username + ' - ' + this.role;
}
});
var Manager = Class({ extends: Person }, {
__construct: function(name)
{
this.super('__construct', name, 'Manager');
}
});
var m = new Manager('John');
console.log(m.getNameAndRole()); // Prints: "John - Manager"
回答by Ali Shahbaz
People who are still struggling for the simple and best approach, you can use Spread Syntax
for extending object.
仍在为简单和最佳方法而苦苦挣扎的人,可以使用Spread Syntax
扩展对象。
var person1 = {
name: "Blank",
age: 22
};
var person2 = {
name: "Robo",
age: 4,
height: '6 feet'
};
// spread syntax
let newObj = { ...person1, ...person2 };
console.log(newObj.height);
Note:Remember that, the property is farthest to the right will have the priority. In this example, person2
is at right side, so newObj
will have name Roboin it.
注意:请记住,最右边的属性将具有优先权。在本例中,person2
位于右侧,因此其中newObj
将包含名称Robo。
回答by 250R
You might want to consider using helper library like underscore.js, which has it's own implementation of extend()
.
您可能需要考虑使用像underscore.js这样的辅助库,它有自己的extend()
.
And it's also a good way to learn by looking at it's source code. The annotated source code pageis quite useful.
而且通过查看它的源代码也是一种学习的好方法。带注释的源代码页非常有用。
回答by Niek Vandael
Mozilla 'announces' object extending from ECMAScript 6.0:
Mozilla '宣布' 从 ECMAScript 6.0 扩展的对象:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends
NOTE: This is an experimental technology, part of the ECMAScript 6 (Harmony) proposal.
注意:这是一项实验性技术,是 ECMAScript 6 (Harmony) 提案的一部分。
class Square extends Polygon {
constructor(length) {
// Here, it calls the parent class' constructor with lengths
// provided for the Polygon's width and height
super(length, length);
// Note: In derived classes, super() must be called before you
// can use 'this'. Leaving this out will cause a reference error.
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
set area(value) {
this.area = value; }
}
This technology is available in Gecko (Google Chrome / Firefox) - 03/2015 nightly builds.
该技术可在 Gecko (Google Chrome / Firefox) - 03/2015 nightly builds 中使用。
回答by Cezary Daniel Nowak
In the majority of project there are some implementation of object extending: underscore, jquery, lodash: extend.
在大多数项目中有一些对象扩展的实现:下划线、jquery、lodash: extend。
There is also pure javascript implementation, that is a part of ECMAscript 6: Object.assign: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
还有纯 javascript 实现,这是 ECMAscript 6 的一部分:Object.assign:https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign