Javascript 使用 Object.assign 和 Object.create 进行继承
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33692912/
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
Using Object.assign and Object.create for inheritance
提问by user5325596
I typically implement inheritance along the following lines.
我通常按照以下方式实现继承。
function Animal () { this.x = 0; this.y = 0;}
Animal.prototype.locate = function() {
console.log(this.x, this.y);
return this;
};
Animal.prototype.move = function(x, y) {
this.x = this.x + x;
this.y = this.y + y;
return this;
}
function Duck () {
Animal.call(this);
}
Duck.prototype = new Animal();
Duck.prototype.constructor = Duck;
Duck.prototype.speak = function () {
console.log("quack");
return this;
}
var daffy = new Duck();
daffy.move(6, 7).locate().speak();
I've read this post by Eric Elliottand if I understand correctly I can use Object.create
and Object.assign
instead? Is it really that simple?
我已经阅读了 Eric Elliott 的这篇文章,如果我理解正确,我可以使用Object.create
而Object.assign
不是?真的那么简单吗?
var animal = {
x : 0,
y : 0,
locate : function () {
console.log(this.x, this.y);
return this;
},
move : function (x, y) {
this.x = this.x + x;
this.y = this.y + y;
return this;
}
}
var duck = function () {
return Object.assign(Object.create(animal), {
speak : function () {
console.log("quack");
return this;
}
});
}
var daffy = duck();
daffy.move(6, 7).locate().speak();
As an aside, by convention constructor functions are capitalized, should object literals that act as constructors also be capitalized?
顺便说一句,按照惯例构造函数是大写的,作为构造函数的对象文字也应该大写吗?
I realise there are many questions here discussing new
versus Object.create
, but they typically seem to relate to Duck.prototype = new Animal();
versus Duck.prototype = Object.create(Animal.prototype);
我意识到这里有很多关于new
vs 的问题Object.create
,但它们通常似乎与Duck.prototype = new Animal();
vsDuck.prototype = Object.create(Animal.prototype);
采纳答案by nils
Yes, it is that simple. In your example with Object.create/Object.assign
, you are using a factory function to create new instances of duck
(similar to the way jQuery creates new instances if you select an element with var body = $('body')
). An advantage of this code style is, that it doesn't force you to call a constructor of animal
when you want to create a new duck
instance (as opposed to ES2015 Classes).
是的,就是这么简单。在您使用 的示例中Object.create/Object.assign
,您正在使用工厂函数来创建 的新实例duck
(类似于 jQuery 创建新实例的方式,如果您选择带有 的元素var body = $('body')
)。这种代码风格的一个优点是,它不会强迫您animal
在想要创建新duck
实例时调用构造函数(与 ES2015 类相反)。
Differences in initialization
初始化的差异
Maybe one interesting tidbit that works slightly differently than if you were to use a constructor (or any other initialization function):
也许一个有趣的花絮与您使用构造函数(或任何其他初始化函数)的工作方式略有不同:
When you create a duck
instace, all the properties of animal
are in the [[Prototype]]
slot of the duck
instance.
创建duck
实例时,所有的属性animal
都在实例的[[Prototype]]
槽中duck
。
var daffy = duck();
console.log(daffy); // Object { speak: function() }
So daffy
does not have any own x
and y
properties yet. However, when you call the following, they will be added:
所以daffy
还没有任何自己的x
和y
财产。但是,当您调用以下内容时,它们将被添加:
daffy.move(6, 7);
console.log(daffy); // Object { speak: function(), x: 6, y: 7 }
Why? In the function-body of animal.move
, we have the following statement:
为什么?在 的函数体中animal.move
,我们有以下语句:
this.x = this.x + x;
So when you call this with daffy.move
, this
refers to daffy
. So it will try to assign this.x + x
to this.x
. Since this.x
is not yet defined, the [[Prototype]]
chain of daffy
is traversed down to animal
, where animal.x
is defined.
所以当你用 调用它时daffy.move
,this
指的是daffy
. 所以它会尝试分配this.x + x
给this.x
. 由于this.x
尚未定义, 的[[Prototype]]
链daffy
向下遍历到animal
,其中animal.x
定义。
Thus in the first call, the this.x
on the right side of the assignment refers to animal.x
, because daffy.x
is not defined. The second time daffy.move(1,2)
is called, this.x
on the right side will be daffy.x
.
因此在第一次调用中,this.x
赋值右侧的 指的是animal.x
,因为daffy.x
没有定义。第二次daffy.move(1,2)
被调用,this.x
右侧将是daffy.x
。
Alternative Syntax
替代语法
Alternatively, you could also use Object.setPrototypeOf
instead of Object.create/Object.assign
(OLOO Style):
或者,您也可以使用Object.setPrototypeOf
代替Object.create/Object.assign
(OLOO Style):
var duck = function () {
var duckObject = {
speak : function () {
console.log("quack");
return this;
}
};
return Object.setPrototypeOf(duckObject, animal);
}
Naming Conventions
命名约定
I'm not aware of any established conventions. Kyle Simpson uses uppercase letters in OLOO, Eric Elliot seems to use lowercase. Personally I would stick with lower-case, because the object literals that act as constructors are already fully fledged objects themselves (not just blueprint, like classes would be).
我不知道任何既定的约定。Kyle Simpson 在 OLOO 中使用大写字母,Eric Elliot 似乎使用小写字母。就我个人而言,我会坚持使用小写,因为充当构造函数的对象文字本身已经是完全成熟的对象(不仅仅是蓝图,就像类一样)。
Singleton
单身人士
If you only wanted a single instance (e.g. for a singleton), you could just call it directly:
如果您只想要一个实例(例如单例),您可以直接调用它:
var duck = Object.assign(Object.create(animal), {
speak : function () {
console.log("quack");
return this;
}
});
duck.move(6, 7).locate().speak();
回答by Bergi
I've read this post by Eric Elliottand if I understand correctly I can use
Object.create
andObject.assign
instead? Is it really that simple?
我已经阅读了 Eric Elliott 的这篇文章,如果我理解正确,我可以使用
Object.create
而Object.assign
不是?真的那么简单吗?
Yes, create
and assign
is much more simple because they're primitives, and less magic is going on - everything you do is explicit.
是的,create
而且assign
要简单得多,因为它们是原始类型,并且正在发生的魔法较少 - 您所做的一切都是明确的。
However, Eric's mouse
example is a bit confusing, as he leaves out one step, and mixes the inheritance of mouses from animals with instantiating mouses.
然而,Eric 的mouse
例子有点令人困惑,因为他省略了一个步骤,将鼠标从动物的继承与实例化鼠标混合在一起。
Rather let's try transcribing your duckling example again - let's start with doing it literally:
相反,让我们再次尝试转录您的小鸭示例 - 让我们从字面意思开始:
const animal = {
constructor() {
this.x = 0;
this.y = 0;
return this;
},
locate() {
console.log(this.x, this.y);
return this;
},
move(x, y) {
this.x += x;
this.y += y;
return this;
}
};
const duck = Object.assign(Object.create(animal), {
constructor() {
return animal.constructor.call(this);
},
speak() {
console.log("quack");
return this;
}
});
/* alternatively:
const duck = Object.setPrototypeOf({
constructor() {
return super.constructor(); // super doesn't work with `Object.assign`
},
speak() { … }
}, animal); */
let daffy = Object.create(duck).constructor();
daffy.move(6, 7).locate().speak();
You should see that what happens here is really no different from using constructors (or class
syntax for that matter), we've just stored our prototypes directly in the variables and we're doing instantiation with an explicit call to create
and constructor
.
您应该看到这里发生的事情实际上与使用构造函数(或与此相关的class
语法)没有什么不同,我们只是将我们的原型直接存储在变量中,并且我们正在通过显式调用create
和进行实例化constructor
。
Now you can figure that our duck.constructor
does nothing but calling its super method, so we can actually omit it completely and let inheritance do its work:
现在你可以发现我们duck.constructor
除了调用它的超级方法之外什么都不做,所以我们实际上可以完全省略它并让继承来完成它的工作:
const duck = Object.assign(Object.create(animal), {
speak() {
console.log("quack");
return this;
}
});
The other thing that is often changed is the initialisation of instance properties. There is actually no reason to initialise them if we don't really need them, it's sufficient to put some default valueson the prototype:
另一件经常改变的事情是实例属性的初始化。如果我们真的不需要它们,实际上没有理由初始化它们,在原型上放置一些默认值就足够了:
const animal = {
x: 0,
y: 0,
locate() {
console.log(this.x, this.y);
}
};
const duck = … Object.create(animal) …;
let daffy = Object.create(duck); // no constructor call any more!
daffy.x = 5; // instance initialisation by explicit assignment
daffy.locate();
The problem with this is that it only works for primitive values, and it gets repetitive. This is where factory functions get in:
问题在于它仅适用于原始值,并且会重复。这是工厂函数的用武之地:
function makeDuck(x, y) {
return Object.assign(Object.create(duck), {x, y});
}
let daffy = makeDuck(5, 0);
To allow for easy inheritance, the initialisation is often not done in the factory but in a dedicated method so it can be called on "subclass" instances as well. You may call this method init
, or you may call it constructor
like I did above, it's basically the same.
为了便于继承,初始化通常不在工厂中完成,而是在专用方法中完成,因此也可以在“子类”实例上调用。你可以调用这个方法init
,也可以constructor
像我上面那样调用它,基本上是一样的。
As an aside, by convention constructor functions are capitalized, should object literals that act as constructors also be capitalized?
顺便说一句,按照惯例构造函数是大写的,作为构造函数的对象文字也应该大写吗?
If you're not using any constructors, you may assign a new meaning to capitalized variable names, yes. It might however be confusing for everyone who's not accustomed to this. And btw, they're not "object literals that act as constructors", they're just prototype objects.
如果您没有使用任何构造函数,您可以为大写的变量名称分配一个新的含义,是的。然而,对于不习惯这一点的每个人来说,这可能会令人困惑。顺便说一句,它们不是“充当构造函数的对象文字”,它们只是原型对象。
回答by Shinobi881
Rather than "inheritance" you should think about what type of "instantiation pattern" you intend to use. They have different purposes for implementation.
您应该考虑您打算使用哪种类型的“实例化模式”,而不是“继承”。它们有不同的实施目的。
The top example is prototypal and the bottom, functional-shared. Check out this link: JS Instantiation patterns
顶部的示例是原型,底部的示例是功能共享的。查看此链接:JS 实例化模式
Also, this is non-es6 related.
此外,这与 es6 无关。