7

我最近一直在通过编写一些 gnome shell 扩展来学习 javascript,因此我对 Javascript 的理解受到了我在 gnome-shell javascript 源代码中观察到的示例的影响。我有一种感觉,我一直在理解错误的类,只是想澄清一下。

我已经编写了一些自己的子类,并且在每种情况下,我都通过遵循 gnome-shell javascript 源代码中的类似代码来简单地定义它们:

Subclass = function() {
    this._init.apply(this,arguments);
}
Subclass.prototype = {
    __proto__: Superclass.prototype,
    _init: function() {
        Superclass.prototype._init.call(this);
    },
    // add other methods of Subclass here.
}

到目前为止,我认为这是制作Subclass基本上是Superclass附加功能的课程的标准方式。我假设每个对象都有一个_init方法。

我最近尝试应用相同的方法来创建 a 的子类Clutter.Actor(重要的是它不是 GNOME-shell 定义的类),并意识到上述子类化对象的方式不是标准。一方面,并​​非每个类都具有_init我假设的功能。这只是 GNOME-shell 在他们的 javascript 类中所做的事情。

所以,我的问题是

  1. 是否有关于上述创建子类的方法的文档?我见过的所有教程都说要设置Subclass.prototype = new Superclass()而不是执行该Subclass.prototype = { __proto__:Superclass.prototype, define_prototype_methods_here }方法,但我的想法是,如果 gnome-shell 始终使用它,则必须有一些方法?
  2. 如果我想尽可能地接近上述定义类的方式(这样我可以保留一些与我正在为其编写扩展的 GNOME-shell 的代码相似性),我应该用什么替换Superclass.prototype._init.call(this)确保Subclass._init得到Subclass.prototype所有的方法/属性Superclass(然后我在我的定义中添加Subclass.prototype),如果Superclass没有_init函数(即它是否有一些我调用的等效构造函数)?

我真的对这一切感到困惑,所以如果我的问题没有多大意义,请原谅我;这将是因为我的误解和困惑的程度!

编辑:澄清: - 我知道__proto__不推荐,因为它是非标准的,但我的代码永远不会在浏览器中运行 - 它只会使用 GNOME javascript(基本上是 Mozilla javascript 引擎)运行,所以我不需要担心交叉兼容性。

4

3 回答 3

8

如前所述,不要使用__proto__. 这是一个非标准属性。 (它现在在浏览器中已针对 JavaScript 进行了标准化。仍然不要使用它。)但是

Subclass.prototype = new Superclass(); // Don't do this

也不是一个很好的方法。如果Superclass期望参数怎么办?

你有更好的选择。

ES2015 及以上

class为您处理所有这些管道;完整的例子:

class Superclass {
    constructor(superProperty) {
        this.superProperty = superProperty;
    }
    method() {
        console.log("Superclass's method says: " + this.superProperty);
    }
}
class Subclass extends Superclass {
    constructor(superProperty, subProperty) {
        super(superProperty);
        this.subProperty = subProperty;
    }
    method() {
        super.method(); // Optional, do it if you want super's logic done
        console.log("Subclass's method says: " + this.subProperty);
    }
}

let o = new Subclass("foo", "bar");
console.log("superProperty", o.superProperty);
console.log("subProperty", o.subProperty);
console.log("method():");
o.method();

ES5 及更早版本

让我们只Subclass.prototype继承Superclass.prototype。例如,这可以通过 ES5 来完成Object.create

Subclass.prototype = Object.create(Superclass.prototype);
Subclass.prototype.constructor = Subclass;

然后在 中Subclass,您调用引用该对象Superclassthis以便它有机会初始化:

function Subclass() {
    Superclass.call(this); // Pass along any args needed
}

完整示例:

function Superclass(superProperty) {
    this.superProperty = superProperty;
}
Superclass.prototype.method = function() {
    console.log("Superclass's method says: " + this.superProperty);
};
function Subclass(superProperty, subProperty) {
    Superclass.call(this, superProperty);
    this.subProperty = subProperty;
}
Subclass.prototype = Object.create(Superclass.prototype);
Subclass.prototype.constructor = Subclass;

Subclass.prototype.method = function() {
    Superclass.prototype.method.call(this); // Optional, do it if you want super's logic done
    console.log("Subclass's method says: " + this.subProperty);
};

var o = new Subclass("foo", "bar");
console.log("superProperty", o.superProperty);
console.log("subProperty", o.subProperty);
console.log("method():");
o.method();

ES3 及更早版本

ES3 和更早的版本没有Object.create,但您可以轻松编写一个函数,以几乎相同的方式为您设置原型:

function objectCreate(proto) {
    var ctor = function() { };
    ctor.prototype = proto;
    return new ctor;
}

(注意:你可以通过创建一个只接受一个参数的半垫片Object.create,但多参数版本的Object.create不能垫片,所以如果页面上的其他代码也使用Object.create.)

然后你做和我们的 ES5 例子一样的事情:

完整示例:

function objectCreate(proto) {
    var ctor = function() { };
    ctor.prototype = proto;
    return new ctor;
}

function Superclass(superProperty) {
    this.superProperty = superProperty;
}
Superclass.prototype.method = function() {
    console.log("Superclass's method says: " + this.superProperty);
};
function Subclass(superProperty, subProperty) {
    Superclass.call(this, superProperty);
    this.subProperty = subProperty;
}
Subclass.prototype = objectCreate(Superclass.prototype);
Subclass.prototype.constructor = Subclass;

Subclass.prototype.method = function() {
    Superclass.prototype.method.call(this); // Optional, do it if you want super's logic done
    console.log("Subclass's method says: " + this.subProperty);
};

var o = new Subclass("foo", "bar");
console.log("superProperty", o.superProperty);
console.log("subProperty", o.subProperty);
console.log("method():");
o.method();

于 2012-05-14T09:29:12.960 回答
1

虽然__proto__现在已经被标准化为在 Web 浏览器上使用时 JavaScript 的必需扩展,但在设置继承层次结构时没有理由使用它。

取而代之的是Object.create(一个 ES5 函数,如果你真的需要支持过时的浏览器,可以将其关键部分用于我们的目的)。

这是一个例子

var BaseClass = function() {
};

var SubClass = function() {
    // Important that SubClass give BaseClass a chance to init here
    BaseClass.call(this/*, args, if, necessary, here*/);

    // ...
};

// Make `SubClass`'s `prototype` an object that inherits from
// `BaseClass`'s `prototype`:
SubClass.prototype = Object.create(BaseClass.prototype);

// Fix up the `constructor` property
SubClass.prototype.constructor = SubClass;

而已。

如果BaseClass的构造函数需要一个参数,则将参数传递给它SubClass

这是一个例子

var BaseClass = function(arg) {
    this.prop = arg;
};

var SubClass = function(baseArg) {   // Can accept it, or provide a
    BaseClass.call(this, baseArg);   // hardcoded one here

    // ...
};

SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;

当然,从 ES2015(又名“ES6”)开始,您可以只使用class(并在必要时进行转译)。

class BaseClass {
    constructor(arg) {
        this.prop = arg;
    }
}

class SubClass extends BaseClass {
    constructor(baseArg) {
        super(baseArg);
    }
}
于 2016-10-08T14:33:30.657 回答
0

__proto__永远不要在您的代码中使用它,因为 JavaScript 引擎实现使用它来引用对象类定义原型。参考:MDN:__proto__

1>

 Subclass.prototype = new Superclass();

是继承超类的正确方法。

2> 如果你想在子类上调用 _init,它实际上会执行超类的 _init 方法,那么不要在子类上定义它。当您尝试访问子类(子类的实例对象)上的 _init 方法时,JS 引擎会尝试在当前 obj 上查找它,如果未找到该方法,则在子类原型链(即超类)中进行搜索

如果要在子类方法中调用超类方法,请使用

Superclass.prototype._init.call(this)

在当前对象范围内执行超级函数。这完成了在 SubClass 方法中调用 super 方法的技巧

于 2012-05-14T09:16:16.053 回答