[英]Understanding why true prototypal inheritance is better than classical/pseudo prototypal inheritance and why i shouldn't use “new”
Reading some articles from Aadit M Shah like Why Prototypal Inheritance Matters or Stop Using Constructor Functions in JavaScript from Eric Elliott i think i understand all of their arguments, in theoric. 阅读Aadit M Shah的一些文章,比如为什么Prototypal继承很重要或者停止在 Eric Elliott的JavaScript中使用构造函数我认为我理解他们所有的论点,在理论上。 But in practice i don't see the real advantages of this pattern. 但在实践中,我没有看到这种模式的真正优势。
Let's take a look two implementations from two snippets to make inheritance. 让我们看看两个片段中的两个实现来进行继承。
Implementation 1 : 实施1 :
var AugmentPerson = Object.augment(function() {
this.constructor = function(name) {
this.name = name;
};
this.setAddress = function(country, city, street) {
this.country = country;
this.city = city;
this.street = street;
};
});
var AugmentFrenchGuy = AugmentPerson.augment(function(base) {
this.constructor = function(name) {
base.constructor.call(this,name);
};
this.setAddress = function(city, street) {
base.setAddress.call(this, "France", city, street);
};
});
var AugmentParisLover = AugmentFrenchGuy.augment(function(base) {
this.constructor = function(name) {
base.constructor.call(this, name);
};
this.setAddress = function(street) {
base.setAddress.call(this, "Paris", street);
};
});
var t = new AugmentParisLover("Mary");
t.setAddress("CH");
console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH
In this example we are using function constructors instead of inherit directly from a object. 在这个例子中,我们使用函数构造函数而不是直接从对象继承。
Implementation 2 : 实施2 :
var CreatePerson = {
create: function (name) {
this.name = name;
return this.extend();
},
setAddress: function(country, city, street) {
this.country = country;
this.city = city;
this.street = street;
}
};
var CreateFrenchGuy = CreatePerson.extend({
create: function (name) {
return CreatePerson.create.call(this,name);
},
setAddress: function(city, street) {
CreatePerson.setAddress('France', city, street);
}
});
var CreateParisLover = CreateFrenchGuy.extend({
create: function (name) {
return CreateFrenchGuy.create.call(this,name);
},
setAddress: function(street) {
CreateFrenchGuy.setAddress('Paris', street);
}
});
var t = CreateParisLover.create("Mary");
t.setAddress("CH");
console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH
To be honest, i'm trying to see the benefits of the second implementation. 说实话,我试图看到第二个实现的好处。 But i am not able. 但我不能。 The only point i see is more flexible is because we can create the instance using apply: 我看到的唯一一点是更灵活,因为我们可以使用apply创建实例:
var t = CreateParisLover.create.apply(CreateParisLover, ["Mary"]);
This give us more flexibility, it's true. 这给了我们更大的灵活性,这是真的。 But we can do the same with this : 但是,我们可以做同样的这个 :
Function.prototype.new = function () {
function functor() { return constructor.apply(this, args); }
var args = Array.prototype.slice.call(arguments);
functor.prototype = this.prototype;
var constructor = this;
return new functor;
};
Then we can: 然后我们可以:
var t = AugmentParisLover.new.apply(AugmentParisLover, ["Mary"]);
What is the real benefits in terms of flexibility, re-usability, difficulty... Because if you check the performance of both cases. 在灵活性,可重用性,难度方面有什么真正的好处......因为如果你检查两种情况的表现。 Object.create() is pretty much slower than new: http://jsperf.com/inheritance-using-create-vs-new I'm confusing. Object.create()比新的慢得多: http : //jsperf.com/inheritance-using-create-vs-new我很困惑。
Programming is a lot like fashion. 编程很像时尚。 Subconsciously most programmers write code which to them looks aesthetically pleasing. 潜意识里,大多数程序员编写的代码对他们来说看起来很美观。 This is the main reason why Java programmers want to implement classical inheritance in JavaScript. 这是Java程序员想要在JavaScript中实现经典继承的主要原因。 Yes, trying to implement classical inheritance in JavaScript is a monolithic task but that doesn't stop people from doing it. 是的,尝试在JavaScript中实现经典继承是一项单一的任务,但这并不能阻止人们这样做。 It's an overkill but people still do it because they just want their code to look like classes (eg jTypes ). 这是一个矫枉过正,但人们仍然这样做,因为他们只是希望他们的代码看起来像类(例如jTypes )。
In much the same way Eric and I have been trying to popularize the use of factory functions instead of constructor functions. 就像Eric和我一直试图推广使用工厂函数而不是构造函数一样。 However this shift from factories to constructors is not just for aesthetic reasons. 然而,从工厂到施工人员的这种转变不仅仅是出于美学原因。 The two of us are trying to change the mentality of JavaScript programmers because in certain aspects we both believe that JavaScript is fundamentally flawed. 我们两个人正试图改变JavaScript程序员的心态,因为在某些方面我们都认为JavaScript存在根本缺陷。 The new
operator in JavaScript is one such aspect. JavaScript中的new
运算符就是这样一个方面。 Although it's broken yet it's central to the language and hence it cannot be avoided. 虽然它已经破碎但它仍然是语言的核心,因此无法避免。
The bottom line is this: 底线是这个:
If you want to create prototype chains in JavaScript then you have to use new
. 如果你想在JavaScript中创建原型链,那么你必须使用new
。 There is no other way around it (except .__proto__
which is frowned upon). 没有别的办法(除了.__proto__
,这是.__proto__
)。
Interestingly you need neither prototypes nor classes to inherit from multiple objects. 有趣的是,您既不需要原型也不需要从多个对象继承类。 Using object composition you can achieve strong behavioral subtyping in JavaScript as Benjamin Gruenbaum describes in the following answer: https://stackoverflow.com/a/17008693/783743 使用对象组合,您可以在JavaScript中实现强大的行为子类型,正如Benjamin Gruenbaum在以下答案中所述: https : //stackoverflow.com/a/17008693/783743
In this answer I'll touch upon the following topics: 在这个答案中,我将涉及以下主题:
new
? 为什么我们坚持new
? new
? 1.为什么我们坚持new
? The new
keyword is put on a pedestal in JavaScript. new
关键字放在JavaScript的基座上。 There's no way to create a prototype chain in JavaScript without using new
. 没有使用new
,就无法在JavaScript中创建原型链。 Yes you can change the .__proto__
property of an object but only after it's created, and that practice is frowned upon. 是的,你可以改变一个对象的.__proto__
属性,但只有在它被创建之后才会改变,并且这种做法是不受欢迎的。 Even Object.create
uses new
internally: 甚至Object.create
内部使用new
:
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F;
};
As Douglas Crockford mentioned : 正如Douglas Crockford 所说 :
The
Object.create
function untangles JavaScript's constructor pattern, achieving true prototypal inheritance.Object.create
函数解开JavaScript的构造函数模式,实现真正的原型继承。 It takes an old object as a parameter and returns an empty new object that inherits from the old one. 它将旧对象作为参数,并返回一个从旧对象继承的空对象。 If we attempt to obtain a member from the new object, and it lacks that key, then the old object will supply the member. 如果我们尝试从新对象获取成员,并且它缺少该键,则旧对象将提供该成员。 Objects inherit from objects. 对象继承自对象。 What could be more object oriented than that? 什么可能比这更面向对象?
The point is that although the new
keyword in JavaScript is "tangled" up there's no other way to create prototype chains in JavaScript. 关键是虽然JavaScript中的new
关键字“纠缠不清”,但没有其他方法可以在JavaScript中创建原型链。 The Object.create
function, even when implemented natively, is still slower than using new
and hence for performance reasons alone most people still use new
even though Object.create
is a more logically sound option. Object.create
函数,即使在本机实现时,仍然比使用new
更慢,因此出于性能原因,大多数人仍然使用new
,尽管Object.create
是一个更逻辑上合理的选项。
Now you might wonder whether new
is really so bad. 现在你可能想知道new
是不是真的那么糟糕。 After all performance wise it is indeed the best solution. 在所有表现方面,它确实是最好的解决方案。 In my opinion however it shouldn't be so. 但我认为不应该这样。 Whether you use new
or Object.create
performance should always be the same. 无论您使用new
还是Object.create
性能都应始终相同。 This is where the language implementations are lacking. 这是缺乏语言实现的地方。 They should really strive towards making Object.create
faster. 他们应该努力使Object.create
更快。 So besides performance does new
have any other redeeming qualities? 除了表演之外, new
还有其他任何赎回品质吗? In my humble opinion it doesn't. 在我的拙见中,它没有。
Oftentimes you don't really know what's wrong with a language until you start using a better language. 在你开始使用更好的语言之前,你通常不会真正知道语言有什么问题。 So let's see some other languages: 那么让我们看看其他一些语言:
Magpie is a hobby language created by Bob Nystrom . 喜鹊是Bob Nystrom创作的一种爱好语言。 It has a bunch of very interesting features which interact very nicely with each other, namely: 它有许多非常有趣的功能,彼此之间的互动非常好,即:
Classes in Magpie however are more akin to prototypes in JavaScript or data types in Haskell. 然而,Magpie中的类更类似于JavaScript中的原型或Haskell中的数据类型。
In Magpie instantiation of classes is split into two steps: 在Magpie中,类的实例化分为两个步骤:
In JavaScript the new
keyword combines the construction and the initialization of instances. 在JavaScript中, new
关键字结合了实例的构造和初始化。 This is actually a bad thing because as we'll see soon splitting construction and initialization is actually a good thing. 这实际上是一件坏事,因为我们很快就会看到拆分构造和初始化实际上是一件好事。
Consider the following Magpie code: 考虑以下Magpie代码:
defclass Point
var x
var y
end
val zeroPoint = Point new(x: 0, y: 0)
def (this == Point) new (x is Int, y is Int)
match x, y
case 0, 0 then zeroPoint
else this new(x: x, y: y)
end
end
var origin = Point new(0, 0)
val point = Point new(2, 3)
This is equivalent to the following JavaScript code: 这相当于以下JavaScript代码:
function Point(x, y) {
this.x = x;
this.y = y;
}
var zeroPoint = new Point(0, 0);
Point.new = function (x, y) {
return x === 0 && y === 0 ?
zeroPoint : new Point(x, y);
};
var origin = Point.new(0, 0);
var point = Point.new(2, 3);
As you can see here we've split the construction and the initialization of instances into two functions. 正如您在这里看到的,我们将实例的构造和初始化分成两个函数。 The Point
function initializes the instance and the Point.new
function constructs the instance. Point
函数初始化实例, Point.new
函数构造实例。 In essence we have simply created a factory function. 本质上,我们只是创建了一个工厂功能。
Separating construction from initialization is such a useful pattern that the good people of the JavaScript room have even blogged about it, calling it the Initializer Pattern . 将构造与初始化分离是一种非常有用的模式,JavaScript室的优秀人员甚至在博客上发表了关于它的信息,称之为初始化模式 。 You should read about the initializer pattern. 您应该阅读初始化模式。 It shows you that initialization in JavaScript is separate from construction. 它向您展示JavaScript中的初始化与构造是分开的。
Object.create
(+1): Construction is separate from initialization. 像Object.create
(+1)这样的工厂:构造与初始化是分开的。 new
operator (-1): Construction and initialization are inseparable. new
运算符(-1):构造和初始化是不可分割的。 JavaScript has been my favorite language since the past 8 years. 自从过去8年以来,JavaScript一直是我最喜欢的语言。 Recently however I started programming in Haskell and I must admit that Haskell has stolen my heart. 然而,最近我开始在Haskell编程,我必须承认Haskell已经偷走了我的心脏。 Programming in Haskell is fun and exciting. 在Haskell中编程很有趣也很有趣。 JavaScript still has a long way to go before it'll be in the same league as Haskell and there's much that JavaScript programmers can learn from Haskell. 在与Haskell处于同一个联盟之前,JavaScript还有很长的路要走,而且JavaScript程序员可以从Haskell中学到很多东西。 I would like to talk about algebraic data types from Haskell apropos to this question. 我想谈谈从Haskell apropos到这个问题的代数数据类型。
Data types in Haskell are like prototypes in JavaScript and data constructors in Haskell are like factory functions in JavaScript. Haskell中的数据类型就像JavaScript中的原型,Haskell中的数据构造函数就像JavaScript中的工厂函数。 For example the above Point
class would be written as follows in Haskell: 例如,上面的Point
类将在Haskell中编写如下:
data Point = Point Int Int
zeroPoint = Point 0 0
origin = zeroPoint
point = Point 2 3
Succinct isn't it? 简洁不是吗? However I'm not here to sell Haskell so let's take a look at some other features Haskell offers: 但是我不是来卖Haskell所以让我们来看看Haskell提供的其他一些功能:
data Shape = Rectangle Point Point | Circle Point Int
rectangle = Rectangle origin (Point 3 4)
circle = Circle zeroPoint 3
Here rectangle
and circle
are both instances of type Shape
: 这里rectangle
和circle
都是Shape
类型的实例:
rectangle :: Shape
circle :: Shape
In this case Shape
is our prototype (data type in Haskell) and rectangle
and circle
are instances of that data type. 在这种情况下, Shape
是我们的原型(Haskell中的数据类型), rectangle
和circle
是该数据类型的实例。 More interestingly however the Shape
prototype has two constructors (data constructors in Haskell): Rectangle
and Circle
. 更有趣的是, Shape
原型有两个构造函数(Haskell中的数据构造函数): Rectangle
和Circle
。
Rectangle :: Point -> Point -> Shape
Circle :: Point -> Int -> Shape
The Rectangle
data constructor is a function which takes a Point
and another Point
and returns a Shape
. Rectangle
数据构造函数是一个函数,它接受Point
和另一个Point
并返回一个Shape
。 Similarly the Circle
data constructor is a function which takes a Point
and an Int
and returns a Shape
. 类似地, Circle
数据构造函数是一个函数,它接受Point
和Int
并返回一个Shape
。 In JavaScript this would be written as follows: 在JavaScript中,这将写成如下:
var Shape = {};
Rectangle.prototype = Shape;
function Rectangle(p1, p2) {
this.p1 = p1;
this.p2 = p2;
}
Circle.prototype = Shape;
function Circle(p, r) {
this.p = p;
this.r = r;
}
var rectangle = new Rectangle(origin, Point.new(3, 4));
var circle = new Circle(zeroPoint, 3);
As you can see a prototype in JavaScript can have more than one constructor and that makes sense. 正如您所看到的,JavaScript中的原型可以有多个构造函数,这是有道理的。 It's also possible for one constructor to have different prototypes at different instances of time but that makes no sense at all. 一个构造函数也可能在不同的时间实例中具有不同的原型,但这根本没有意义。 Doing so would break instanceof
. 这样做会破坏instanceof
。
As it turns out having multiple constructors is a pain when using the constructor pattern. 事实证明,在使用构造函数模式时,使用多个构造函数会很痛苦。 However it's a match made in heaven when using the prototypal pattern: 然而,当使用原型模式时,它是天堂般的匹配:
var Shape = {
Rectangle: function (p1, p2) {
var rectangle = Object.create(this);
rectangle.p1 = p1;
rectangle.p2 = p2;
return rectangle;
},
Circle: function (p, r) {
var circle = Object.create(this);
circle.p = p;
circle.r = r;
return circle;
}
};
var rectangle = Shape.Rectangle(zeroPoint, Point.new(3, 4));
var circle = Shape.Circle(origin, 3);
You could also use the extend
function from my blog post on Why Prototypal Inheritance Matters to make the above code more succinct: 你也可以使用我的博客文章中关于为什么原型继承很重要的extend
函数来使上面的代码更简洁:
var Shape = {
Rectangle: function (p1, p2) {
return this.extend({
p1: p1,
p2: p2
});
},
Circle: function (p, r) {
return this.extend({
p: p,
r: r
});
}
};
var rectangle = Shape.Rectangle(zeroPoint, Point.new(3, 4));
var circle = Shape.Circle(origin, 3);
Factories written in this way look a lot like the module pattern and it feels natural to write code like this. 以这种方式编写的工厂看起来很像模块模式 ,编写这样的代码感觉很自然。 Unlike with the constructor pattern everything is wrapped up nicely in an object literal. 与构造函数模式不同,一切都很好地包含在对象文字中。 Nothing is dangling here, there and everywhere. 这里,那里和任何地方都没有任何东西悬空。
Nevertheless if performance is your main concern then stick with the constructor pattern and new
. 然而,如果性能是您的主要关注点,那么坚持使用构造函数模式和new
。 In my opinion however modern JavaScript engines are fast enough that performance is no longer the main factor. 在我看来,现代JavaScript引擎足够快,性能不再是主要因素。 Instead I think JavaScript programmers should invest more time in writing code that's maintainable and robust and the prototypal pattern is indeed more elegant and understandable than the constructor pattern. 相反,我认为JavaScript程序员应该花更多的时间编写可维护且健壮的代码,而原型模式确实比构造函数模式更优雅和易懂。
In addition Haskell also teaches us about pure functional programming. 此外,Haskell还教我们关于纯函数式编程。 Since factories are simply functions we can call
and apply
factories, compose factories, curry factories, memoize factories, make factories lazy by lifting them and much more. 由于工厂只是功能,我们可以call
和apply
工厂,组建工厂,咖喱工厂,记忆工厂, 通过提升工厂使工厂变得懒惰等等。 Because new
is an operator and not a function you can't do that using new
. 因为new
是一个操作符而不是一个函数,所以你不能使用new
。 Yes you can make a functional equivalent of new
but then why not just use factories instead? 是的,你可以使功能相当于new
但为什么不只是使用工厂? Using the new
operator in some places and the new
method in other places is inconsistent. 在某些地方使用new
运算符而在其他地方使用new
方法是不一致的。
Alright so factories do have their advantages, but still the performance of Object.create
sucks doesn't it? 好吧所以工厂确实有自己的优势,但是Object.create
的表现还是糟透了吗? It does, and one of the reasons is because every time we use Object.create
we create a new constructor function, set its prototype to the prototype we want, instantiate the newly created constructor function using new
and then return it: 确实如此,其中一个原因是因为每次我们使用Object.create
我们都会创建一个新的构造函数,将其原型设置为我们想要的原型,使用new
实例化新创建的构造函数,然后返回它:
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F;
};
Can we do better than this? 我们能做得比这更好吗? Let's try. 我们试试吧。 Instead of creating a new constructor every time why don't we just instantiate the .constructor
function of the given prototype? 每次为什么不实例化给定原型的.constructor
函数而不是创建新的构造函数?
Object.create = function (o) {
return new o.constructor;
};
This works in most cases but there are a few problems: 这在大多数情况下都有效,但存在一些问题:
o.constructor
might be different from o
. o.constructor
的原型可能与o
不同。 o
, but o.constructor
might have initialization logic as well which we can't separate from the construction. 我们只想构造一个o
的新实例,但o.constructor
也可能有初始化逻辑,我们无法从构造中分离出来。 The solution is pretty simple: 解决方案非常简单:
function defclass(prototype) {
var constructor = function () {};
constructor.prototype = prototype;
return constructor;
}
Using defclass
you can create classes as follows: 使用defclass
可以创建类,如下所示:
var Shape = defclass({
rectangle: function (p1, p2) {
this.p1 = p1;
this.p2 = p2;
return this;
},
circle: function (p, r) {
this.p = p;
this.r = r;
return this;
}
});
var rectangle = (new Shape).rectangle(zeroPoint, Point.new(3, 4));
var circle = (new Shape).circle(origin, 3);
As you can see we've separated construction and initialization and the initialization can be deferred to multiple constructors. 正如您所看到的,我们已经分离了构造和初始化,并且初始化可以推迟到多个构造函数。 It can even be chained as follows: (new Shape).rectangle().circle()
. 它甚至可以链接如下:( (new Shape).rectangle().circle()
。 We've replaced Object.create
with new
which is much faster and we still have the flexibility to do whatever we want. 我们用new
取代了Object.create
,速度更快,我们仍然可以灵活地做任何我们想做的事情。 In addition everything is nicely encapsulated within a single object literal. 此外,一切都很好地封装在一个对象文字中。
As you can see the new
operator is a necessary evil. 正如你所看到的那样, new
算子是一种必要的恶魔。 If new
was a implemented as a factory function then that would be great but it's implemented as an operator instead and operators in JavaScript are not first class. 如果new
是作为工厂函数实现的那么那将是很好的但是它实现为运算符而JavaScript中的运算符不是一流的。 This makes it more difficult to do functional programming with new
. 这使得使用new
进行函数式编程变得更加困难。 Factories on the other hand are flexible. 另一方面,工厂是灵活的。 You can tailor make any number of factory functions for your prototypes and the ability to do whatever you want is the biggest selling point of factory functions. 您可以为您的原型定制任意数量的工厂功能,并且能够做任何您想要的工作是工厂功能的最大卖点。
In JavaScript, what people call "pseudo-classical" inheritance is prototypical inheritance. 在JavaScript中,人们称之为“伪经典”继承是典型的继承。 That's the only kind of inheritance JavaScript has. 这是JavaScript唯一的继承类型。 Avoiding new
is like avoiding switch
statements because you can do that using if/else if
instead. 避免使用new
就像避免使用switch
语句一样,因为你可以使用if/else if
来实现。 Sure you can, and sometimes you should. 当然可以,有时你应该。 Other times, switch
is exactly the right choice. 其他时候, switch
是正确的选择。 Same with new
and Object.create
: Use the best one for what you're doing. 与new
和Object.create
相同:使用最适合您正在做的事情。
To me , and this is a bit subjective (as is the whole "pseudo-classical inheritance is bad" meme, in my view): 对我来说 ,这有点主观(在我看来,整个“伪经典继承是坏的”模因):
new
is for when I'm doing class-like things. new
是,当我在做类似的东西了。 I use new
and constructor functions because it fits well with how the language is designed. 我使用new
和构造函数,因为它非常适合语言的设计方式。 (Yes, that design is unusual, but that's how it is.) So if I'm going to have objects that will represent people and have common behavior, I'll use a Person
constructor, assign behaviors (functions) to Person.prototype
, and use new Person
to create them. (是的,那个设计很不寻常,但就是这样。)所以如果我要拥有代表人并具有共同行为的对象,我将使用Person
构造函数,将行为(函数)分配给Person.prototype
,并使用new Person
来创建它们。 (I use my Lineage
script to make this more concise, and to handle some hierarchy stuff easily.) This is straightforward, familiar, clean, clear: If you see new Person
you know I'm creating a new object. (我使用我的Lineage
脚本使这更简洁,并轻松处理一些层次结构的东西。)这是直截了当,熟悉,干净,清晰:如果你看到new Person
你知道我正在创建一个新对象。 (If I'm not — yes, it's possible to violate that expectation with a constructor function — then to my mind I shouldn't be writing a constructor function in the first place.) (如果我不是 - 是的, 可能违反了构造函数的期望 - 那么在我看来我不应该首先编写构造函数。)
Now of course, you can define a builder function ( createPerson
, buildPerson
, whatever) that does the same thing using Object.create
or similar. 当然,您可以定义使用Object.create
或类似功能执行相同操作的构建器函数( createPerson
, buildPerson
等)。 I don't have a problem with people doing that if that's what they prefer (as long as the function name is clear it creates something). 如果那是他们喜欢的东西,我对那些人没有问题(只要功能名称清楚它会创造一些东西)。 I do have a problem with people saying "you shouldn't use new
" as though it were objective advice; 我确实有人说“你不应该使用new
”,好像这是客观的建议; it's an opinion , it's style advice. 这是一种意见 ,这是一种风格建议。
Object.create
is for when I'm doing instance-level stuff. Object.create
适用于我在做实例级别的东西时。 There's a project I work on that has a bunch of objects in a complex tree/graph. 我工作的项目在复杂的树/图中有一堆对象。 They're data-only, no behavior. 它们只是数据,没有行为。 Sometimes, we need to have data that's not yet verified, and so shouldn't overwrite the previous data. 有时,我们需要拥有尚未验证的数据,因此不应覆盖以前的数据。 So the container has a reference to the verified data ( verified
) and to the unverified data ( current
). 因此容器具有对已验证数据( verified
)和未验证数据( current
)的引用。 To avoid unnecessary branching in the code, the container always has both references, but in the normal case they refer to the same object ( container.verified = container.current = {};
). 为了避免代码中不必要的分支,容器始终具有两个引用,但在正常情况下它们引用相同的对象( container.verified = container.current = {};
)。 Nearly all code uses that current
object because nearly all code should be using the most up-to-date information. 几乎所有代码都使用current
对象,因为几乎所有代码都应使用最新信息。 If we need to add pending data, we do container.current = Object.create(container.verified);
如果我们需要添加挂起的数据,我们会做container.current = Object.create(container.verified);
and then add the data to container.current
. 然后将数据添加到container.current
。 Since current
's prototype is verified
, there's no need to copy all the old data to it and have duplicated data all over the place. 由于verified
current
的原型,因此无需将所有旧数据复制到其中并且在整个地方都有重复的数据。 Eg, the classic use of facade. 例如,门面的经典用途。 new
would be the wrong tool for this job, it would only get in the way. new
对于这项工作来说是错误的工具,它只会妨碍你。
One of the many fantastic things about JavaScript is that you have both options. 关于JavaScript的众多奇妙之处之一就是你有两种选择。 I use both in the same projects, for different things, all the time. 我同时在同一个项目中使用它们,不同的东西。
Similar questions have been asked and answered many times before. 之前已经多次提出并回答过类似的问题。 See: 看到:
Constructor function vs Factory functions Classical Vs prototypal inheritance 构造函数vs工厂函数 Classical Vs原型继承
More learning: https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9#.s0r3i5w6t http://vimeo.com/69255635 更多学习: https : //medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9#.s0r3i5w6t http://vimeo.com/69255635
tl;dr TL;博士
Edit - responding to the "answer" posted by the OP: 编辑 - 回应OP发布的“答案”:
The best thing about Object.create is that it's a dedicated, low-level tool that lets you create a new object and assign any prototype you want to it without using a constructor function. 关于Object.create的最好的事情是它是一个专用的低级工具,它允许您创建一个新对象并在不使用构造函数的情况下为其分配任何原型。 There are lots of reasons to avoid constructors, covered in-depth here: Constructor function vs Factory functions 避免构造函数有很多理由,这里有深入介绍: 构造函数与工厂函数
Classical 古典
var Animal = function Animal(name) {
this.name = name;
};
Animal.prototype.walk = function walk() {
console.log(this.name + ' goes for a walk.');
};
var Rabbit = function Rabbit(/* name */) {
// Because construction and instantiation are conflated, you must call super().
Animal.prototype.constructor.apply(this, arguments);
};
// Classical inheritance is really built on top of prototypal inheritance:
Rabbit.prototype = Object.create(Animal.prototype);
// Fix the .constructor property:
Rabbit.prototype.constructor = Rabbit;
Rabbit.prototype.jump = function jump() {
console.log(this.name + ' hops around a bit.');
};
var myRabbit = new Rabbit('Bunny George');
myRabbit.walk();
// Bunny George goes for a walk.
Prototypal 原型
var animalMethods = {
walk: function walk() {
console.log(this.name + ' goes for a walk.');
}
};
var animal = function animal(name) {
var instance = Object.create(animalMethods);
instance.name = name;
return instance;
};
var rabbitMethods = {
jump: function jump() {
console.log(this.name + ' hops around a bit.');
}
};
var rabbit = function rabbit(name) {
var proto = rabbitMethods;
// This is more commonly done like mixin({}, animalMethods, rabbitMethods);
// where mixin = $.extend, _.extend, mout.object.mixIn, etc... It just copies
// source properties to the destination object (first arg), where properties from
// the last argument override properties from previous source arguments.
proto.walk = animalMethods.walk;
var instance = Object.create(rabbitMethods);
// This could just as easily be a functional mixin,
// shared with both animal and rabbit.
instance.name = name;
return instance;
};
var rabbit2 = rabbit('Bunny Bob');
rabbit2.walk();
// Bunny Bob goes for a walk.
The amount of code required is pretty similar, but to me, it's a LOT more clear what the prototypal stuff is doing, and it's also a lot more flexible, and has none of the classical inheritance arthritic baggage of the first example. 所需的代码量非常相似,但对我而言,原型材料的作用更为清晰,并且它也更加灵活,并且没有第一个例子的经典继承关节炎包袱。
Thank you for your amazing answer. 谢谢你的惊人答案。 But, I am not agree with most of your afirmation. 但是,我不同意你的大部分意见。 Without see the equivalent in both patterns. 没有看到两种模式中的等价物。 Some arguments are subjetive for me. 有些论据对我来说是主观的。 So i would like to focus on facts. 所以我想关注事实。 And in this answer I am going to comment what are the best points of each. 在这个答案中,我将评论每个人的最佳观点。 And without external libraries/snippets because then we can fight about which library is better. 没有外部库/片段,因为那时我们可以争论哪个库更好。
Object.create
关于Object.create
好点 var Shape = {
Rectangle: function (p1, p2) {
var rectangle = Object.create(this);
rectangle.p1 = p1;
rectangle.p2 = p2;
return rectangle;
},
Circle: function (p, r) {
var circle = Object.create(this);
circle.p = p;
circle.r = r;
return circle;
}
};
var rectangle = Shape.Rectangle.call(Shape, zeroPoint, Point.new(3, 4));
var circle = Shape.Circle.call(Shape, origin, 3);
This is not possible using the new
. 这是不可能使用new
。
new
关于new
好点 function Rectangle(p1, p2) {
this.p1 = p1;
this.p2 = p2;
}
function Circle(p, r) {
this.p = p;
this.r = r;
}
vs VS
Rectangle: function (p1, p2) {
var rectangle = Object.create(this);
rectangle.p1 = p1;
rectangle.p2 = p2;
return rectangle;
},
Circle: function (p, r) {
var circle = Object.create(this);
circle.p = p;
circle.r = r;
return circle;
}
Whatever case you always have to write more code to implement the same. 无论如何,您总是需要编写更多代码来实现相同的功能。
var rectangle = Shape.Rectangle(zeroPoint, Point.new(3, 4));
var circle = Shape.Circle(origin, 3);
What happen if tomorrow you want to change the name of Shape to Geometry? 如果明天要将Shape的名称更改为Geometry会发生什么? You have to review all your code and change all the words Shape for each instantiation of cicle or rectangle. 您必须检查所有代码并更改每个cicle或矩形实例的所有单词Shape。 This point is more remarkable when you are doing inheritance. 当你做继承时,这一点更加引人注目。 Because you always have to call exacly the same name of the constructor to access to the super.methods 因为您总是必须使用相同的构造函数名称来访问super.methods
If in my code I'm seeing new Rectangle(...)
i know I'm creating a new instance of the object Rectangle. 如果在我的代码中我看到new Rectangle(...)
我知道我正在创建一个对象Rectangle的新实例。 However Shape.Rectangle(...)
don't tell me if is a new object or if is just a function or if Shape is a unique instance like var Shape = new Whatever()
. 但是, Shape.Rectangle(...)
不会告诉我是否是一个新对象,或者只是一个函数,或者Shape是一个唯一的实例,如var Shape = new Whatever()
。
var Person = function() {
var a = 5;
this.method = function (b) { return a*b; };
};
var obj = new Person;
obj.method(5); // return 25
obj.a; // return undefined
vs VS
var Person = {
a: 5,
method: function (b) { return this.a*b; }
};
var obj = Object.create(Person);
obj.method(5); // return 25
obj.a; // return 5
You always can have private methods and properties on the new
pattern. 您始终可以在new
模式上拥有私有方法和属性。 On the object.create
pattern you can if your implementation is specific. 如果您的实现是特定的,您可以在object.create
模式上。 If you do this, your code is more difficult and verbose to write (But this is a personal opinion). 如果你这样做,你的代码写起来会更加困难和冗长(但这是个人意见)。
var Person = function(a) {
this.method = function (b) { return a*b; };
};
var obj = new Person(5);
obj.method(5); // return 25
vs VS
var Person = {
method: function (b) { return a*b; }
};
var obj = Object.create(Person);
obj.method(4); //Error
No way to do instanceof natively with the Object.create
pattern. 无法使用Object.create
模式本机地执行instanceof。
I leave this for the last one, because most of the other points can be solved with extra javascript. 我把它留给最后一个,因为大多数其他点可以用额外的javascript来解决。 But in this case can't be the same to the new
pattern. 但在这种情况下new
模式不能相同。 Also i think this is the most important advantage for new
pattern. 另外我认为这是new
模式最重要的优势。 Because if you are programming for browser the performance sometimes doesn't matter. 因为如果您正在为浏览器编程,性能有时无关紧要。 But if you are working of the backend and you are making a very big and scalable app, the performance matter. 但是如果你正在使用后端并且你正在制作一个非常大且可扩展的应用程序,那么性能就很重要。 Why Paypal leave java and go to node.js? 为什么Paypal离开java并转到node.js? Because the performance it's very important in big projects. 因为性能在大项目中非常重要。
So, if new
it's 10 times faster than Object.create
. 所以,如果是new
它比Object.create
快10倍。 I think only for this reason, and only for this reason worth keep programming with new. 我想只是出于这个原因,并且只是因为这个原因值得用新的编程。 Besides, give me more flexibility and i can do things with new
pattern that I can't with Object.create
. 此外,给我更多的灵活性,我可以使用Object.create
不能用new
模式做事。 And I agree that the nature of the Javascript is to using the Object.create
. 我同意Javascript的本质是使用Object.create
。 But i think i get more benefits if i use new
. 但我认为如果我使用new
我会获得更多的好处。
Sorry for my English. 对不起我的英语不好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.