【问题标题】:Making subclass with .prototype and __proto__使用 .prototype 和 __proto__ 创建子类
【发布时间】:2012-05-14 08:53:21
【问题描述】:

我最近通过编写一些 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 方法。

我最近尝试应用相同的方法来创建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 引擎)运行,所以我无需担心交叉兼容性。

【问题讨论】:

  • 这不是一个真正的答案,但我强烈推荐这个网站来提高你的 JS 知识:yuiblog.com/crockford

标签: javascript


【解决方案1】:

如前所述,不要使用__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 中,您调用Superclass 并使用this 引用该对象,以便它有机会初始化:

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的多参数版本不能被填充,所以它会给页面上的其他代码提供错误的想法如果它也使用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();

【讨论】:

  • 好的,我明白你在说什么。这是否意味着我坚持定义inherits 函数只是为了创建一个子类(/摆弄Tmp 来设置原型)? (__proto__ 是非标准的,这并不困扰我,因为我的代码 only 用于基于 Mozilla javascript 引擎的 gnome javascript - 它从未用于浏览器)。
  • 可以说这是一种更清洁的方式...从长远来看,我认为它会带来回报,而且您可以轻松地将这种模式转移到其他环境中。那么为什么不学习/使用一件你可以在任何地方应用的东西呢? ;) 你不会被这个困扰,你可以使用__proto__,但我建议不要这样做。与使用Object.create基本相同,这是一种更好的方式(如果环境支持ES5)。
  • 谢谢。我将学习一般情况下的“更清洁的方式”。鉴于由于代码的其他功能,我正在编写的代码无论如何都无法使用 GNOME javascript(支持__proto__),如果我确实想要调用超类构造函数,我将如何调用坚持使用__proto__?在Subclass() 中执行this._init.apply(this,arguments),在Subclass.prototype._init 中执行__proto__: Superclass.prototype, _init: function() { Superclass.apply(this,arguments); // rest of initialisation }
  • 是的,您可以这样做。但是,不需要额外的_init 方法。函数本身已经是构造函数。不要让它太复杂;)
  • 好的,我明白了。谢谢! (我只将 _init 方法放入以符合 gnome shell 编码风格 - 所有 gnome shell 代码都意味着具有相同的“外观”,因为我正在编写一个 gnome shell 扩展并且所有 gnome shell 都有 _init 方法,我觉得就像我也必须放一个一样)。
【解决方案2】:

虽然__proto__ 现在已被标准化为在网络浏览器上使用时对 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);
    }
}

【讨论】:

    【解决方案3】:

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

    1>

     Subclass.prototype = new Superclass();
    

    是继承超类的正确方法。

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

    如果你想在子类方法中调用超类方法,那么使用

    Superclass.prototype._init.call(this)
    

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

    【讨论】:

    • 注意 - 我没有在任何浏览器中运行代码。对于 2),我知道如何调用父母的方法。我的问题是如何获得从Subclass.prototype 的指导员中调用Superclass.prototype._init.call(this)效果(效果是Subclass.prototype 获得了超类的所有属性和方法),何时Superclass 中没有 Superclass._init 函数。
    猜你喜欢
    • 2021-11-29
    • 2020-04-16
    • 2016-09-03
    • 2022-11-18
    • 1970-01-01
    • 2017-12-17
    • 2012-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多