3

我精通 C++,所以我在 OOP 方面有很好的背景,但我是 JavaScript 新手,我正在尝试了解如何在 JavaScript 中正确实现多态性。我已经阅读了许多与该主题相关的教程和 StackOverflow 问题,但没有一个详细解释如何正确地从基类继承以及向子类添加新成员

function Obj1() {}
function Obj2() {}
function Obj3() {}

Obj2.prototype === Obj3.prototype;  // false

好奇心#1: 如果Obj2Obj3(以及,就此而言,Obj1)都继承自Object,为什么它们的原型不相同?每个人都收到了自己Obj2副本吗?Obj3Object

Obj2.prototype = Object;
Obj2.prototype = Object;
Obj2.prototype === Obj3.prototype;  // true
Obj2.prototype = Obj1;
Obj3.prototype = Obj1;
Obj2.prototype === Obj3.prototype;  // true

好奇心#2: 在这两种情况下,Obj2现在Obj3共享同一个原型。发生了什么变化?

Obj2.prototype.pi = function() {  return 3.14159;  }
Obj2.prototype.pi();  // 3.14159
Obj3.prototype.pi();  // 3.14159
Obj1.pi();  // 3.14159

真正的问题: 更改原型Obj2也会更改原型Obj3和函数Obj1本身。这对这位 C++ 程序员来说是非常令人震惊的,尽管这正是我从上述语法中所期望的:毕竟,由于 和 的原型Obj2Obj3等同于Obj1,因此对一个的更改将产生对另外两个相同的更改。不过,我想将andObj1用作两者的基类,Obj2并在其上插入一个新函数,它不会以任何方式由or共享。我怎么做?Obj3 Obj2Obj1Obj3

当这些对象被实例化时,会产生更多的好奇:

var x = new Obj2();
x.pi();  // 3.14159
var y = new Obj3();
y.pi();  // 3.14159
var z = new Obj1();
z.pi();  // Error: pi() not defined on z.

Obj2并且Obj3两者都有一个函数pi(),这意味着它们仍然共享一个包含函数的基类pi()。这个基类就是——但是不包含的Obj1实例,尽管有相反的证据!Obj1pi()

4

3 回答 3

2

在你的第一个证明中:

function a () {};
function b () {};

a.prototype === b.prototype; // false

prototype您正在了解不是Object- 它不是基类的事实。它是一个对象,它又在原型上被扩展,具有Object.
并且任何测试{} === {};都将返回 false, always,除非您正在测试指向同一个对象的两个指针:

var a = {},
    b = a;

a === b; // true

之后您的继承 trainwreck 直接指向缺少这个概念,因为您已将所有三个函数设置为指向同一个对象(最初指向Object自身,然后指向Obj1的原型对象)。
Aprototypelive,在对象构造之后对原型的任何更改都将反映在包含指向该对象的指针的对象中。

你已经给出了Obj1,Obj2Obj3一个指向完全相同的对象的指针。
因此,对它们共享的原型对象的任何修改都将反映在它们所有构造的对象中,因为继承解析是实时发生的,当您尝试访问属性时(而不是生活在一些静态的、预编译的 GOTO状态)。

如果你想要子类化的能力,你可以这样做:

var A = function () {};
A.prototype.foo = function () {};
var a = new A();

var B = function () {};
B.prototype = a; // or new A(); -- an object which can live-lookup the prototype of A
// problem is, `constructor` on the prototype will now point to `A`, so...
B.prototype.constructor = B; // now b instanceof B should work in tests
B.bar = function () {};


var b = new B();
b.foo(); // A.prototype.foo
b.bar(); // B.prototype.bar

但实际上,如果您尝试将其扩展得太远,您会发疯的。
在 JS 中更明智的做法是基于组件的对象构造和依赖注入。

于 2012-10-07T16:12:41.853 回答
1

prototype属性也返回一个对象,它是对象的一个​​实例,但不是同一个实例,因此它们不是同一个对象,相等的比较自然会返回 false。

更详细地说,Obj1、Obj2 和 Obj3 都是函数和对象,它们的原型也是对象。在 C++ 术语中,将prototype属性视为指向另一个对象的指针,而不是类定义或基类。

对于 Obj2,您将声明一个单独的方法,如下所示:

function Obj2() {
    this.prototype = new Obj1();
    this.myOwnMethod = function (x) { ... }
}

我试图用我自己的话来写一个关于 Javascript 中面向对象的很好的解释,但我认为这会花费太长时间:) 相反,我决定删除我写的半连贯的文本,并给出两个帮助我理解的指针它更深入,希望他们也会帮助你:

于 2012-10-07T15:50:49.800 回答
-3

使用ember骨干网或其他一些框架为您提取它。要让继承像你期望的那样工作,需要做很多事情。

于 2012-10-07T15:54:18.580 回答