Javascript 理解为什么真正的原型继承比经典/伪原型继承更好,以及为什么我不应该';t使用;新";
读一些喜欢或喜欢的文章 从埃里克·艾略特那里,我想我从理论上理解了他们所有的论点。但在实践中,我没有看到这种模式的真正优势 让我们从两个代码段中看一下实现继承的两个实现Javascript 理解为什么真正的原型继承比经典/伪原型继承更好,以及为什么我不应该';t使用;新";,javascript,oop,inheritance,prototype,Javascript,Oop,Inheritance,Prototype,读一些喜欢或喜欢的文章 从埃里克·艾略特那里,我想我从理论上理解了他们所有的论点。但在实践中,我没有看到这种模式的真正优势 让我们从两个代码段中看一下实现继承的两个实现 第一个是使用它是来自Aadit M Shah的脚本 在这个例子中,我们将使用。也是由Aadit M Shah制作的 实施1: var AugmentPerson = Object.augment(function() { this.constructor = function(name) {
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
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
在本例中,我们使用函数构造函数,而不是直接从对象继承
实施2:
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
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
老实说,我正试图看到第二个实现的好处。但我不能。我看到的唯一更灵活的一点是,我们可以使用apply创建实例:
var t=CreateParisLover.create.apply(CreateParisLover,[“Mary”])代码>
这确实给了我们更多的灵活性。但我们也可以这样做:
然后我们可以:
var t = AugmentParisLover.new.apply(AugmentParisLover, ["Mary"]);
在灵活性、可重用性、难度等方面,真正的好处是什么。。。
因为如果你检查两种情况的表现。Object.create()比new慢得多:
我很困惑。在JavaScript中,人们所谓的“伪经典”继承就是原型继承。这是JavaScript拥有的唯一一种继承。避免new
类似于避免switch
语句,因为您可以使用if/else if
代替。当然你可以,有时你应该。其他时候,开关
正是正确的选择。与新建
和对象相同。创建
:在您正在做的事情中使用最好的一个
对我来说,这有点主观(在我看来,整个“伪经典遗传是坏的”模因也是如此):
new
适用于我上课的时候。我使用new
和构造函数,因为它非常适合语言的设计方式。(是的,这种设计是不寻常的,但事实就是如此。)因此,如果我想要有代表人的对象,并且有共同的行为,我将使用Person
构造函数,将行为(函数)分配给Person.prototype
,并使用new Person
来创建它们。(我使用my使其更加简洁,并轻松处理一些层次结构的内容。)这是简单、熟悉、干净、清晰的:如果您看到newperson
您就知道我正在创建一个新对象。(如果我不是——是的,有可能用构造函数违反预期——那么在我看来,我不应该首先编写构造函数。)
当然,现在您可以定义一个生成器函数(createPerson
,buildPerson
,无论什么),该函数使用Object.create
或类似函数执行相同的操作。如果人们喜欢这样做的话,我不反对这样做(只要函数名是明确的,它就会创建一些东西)。我确实对人们说“你不应该使用new
”好像这是一个客观的建议而感到不满;这是一种观点,一种风格建议
Object.create
是我在做实例级工作时使用的。我在做一个项目,在一个复杂的树/图中有一堆对象。它们只是数据,没有行为。有时,我们需要尚未验证的数据,因此不应覆盖以前的数据。因此,容器引用了已验证的数据(verified
)和未验证的数据(current
)。为了避免代码中不必要的分支,容器始终具有两个引用,但在正常情况下,它们引用相同的对象(container.verified=container.current={};
)。几乎所有的代码都使用当前的对象,因为几乎所有的代码都应该使用最新的信息。如果我们需要添加挂起的数据,我们会执行container.current=Object.create(container.verified)
然后将数据添加到container.current
。由于current
的原型经过验证
,因此无需将所有旧数据复制到原型中,也无需到处复制数据。例如,立面的经典用法new
对于这项工作来说是错误的工具,它只会妨碍工作
JavaScript的许多奇妙之处之一是,您可以同时拥有这两种选择。我一直在同一个项目中为不同的事情使用这两种方法。编程很像时尚。在潜意识里,大多数程序员编写的代码在他们看来都很美观。这就是Java程序员希望在JavaScript中实现经典继承的主要原因。是的,尝试在JavaScript中实现经典继承是一项艰巨的任务,但这并不能阻止人们这样做。这有点过分了,但人们仍然这样做,因为他们只是希望自己的代码看起来像类(例如)
Eric和我一直在以同样的方式推广工厂函数而不是构造函数的使用。然而,从工厂到建设者的这种转变不仅仅是出于审美的原因。我们两人都试图改变JavaScript程序员的心态,因为在某些方面,我们都认为JavaScript存在根本性的缺陷。JavaScript中的new
操作符就是这样一个方面。虽然它已经被破坏了,但它是语言的核心,因此无法避免
底线是:
如果要在JavaScript中创建原型链,则必须使用new
。没有其他方法可以绕过它(除了。\uuuuuu proto\uuuuuu
这是不受欢迎的)。
有趣的是,您既不需要原型也不需要类来继承多个对象。使用对象合成可以实现强b
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)
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);
data Point = Point Int Int
zeroPoint = Point 0 0
origin = zeroPoint
point = Point 2 3
data Shape = Rectangle Point Point | Circle Point Int
rectangle = Rectangle origin (Point 3 4)
circle = Circle zeroPoint 3
rectangle :: Shape
circle :: Shape
Rectangle :: Point -> Point -> Shape
Circle :: Point -> Int -> Shape
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);
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);
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);
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F;
};
Object.create = function (o) {
return new o.constructor;
};
function defclass(prototype) {
var constructor = function () {};
constructor.prototype = prototype;
return constructor;
}
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);
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);
function Rectangle(p1, p2) {
this.p1 = p1;
this.p2 = p2;
}
function Circle(p, r) {
this.p = p;
this.r = r;
}
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);
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
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
var Person = function(a) {
this.method = function (b) { return a*b; };
};
var obj = new Person(5);
obj.method(5); // return 25
var Person = {
method: function (b) { return a*b; }
};
var obj = Object.create(Person);
obj.method(4); //Error
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.
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.