使用.prototype和__proto__创建子类

时间:2012-05-14 08:53:22

标签: javascript

我最近通过编写一些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的相似性,我正在编写扩展名),我要替换什么{{1在Superclass.prototype._init.call(this)中使用确保Subclass._init获取Subclass.prototype的所有方法/属性(然后我在Superclass的定义中添加Subclass.prototype }),如果Superclass没有_init函数(即它是否有一些我调用的等效构造函数)?
  3. 我真的很困惑这一切所以请原谅我,如果我的问题没有多大意义;这是因为我误解的程度和混乱!

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

3 个答案:

答案 0 :(得分: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中,您使用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,则页面错误。)

然后你做我们的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();

答案 1 :(得分: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);
    }
}

答案 2 :(得分:0)

永远不应在代码中使用

__proto__,因为JavaScript Engine实现使用它来引用对象类定义原型。 参考:MDN:__proto__

1>

 Subclass.prototype = new Superclass();

是继承超类的正确方法。

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

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

Superclass.prototype._init.call(this)

在当前对象范围内执行超级函数。这样做可以在SubClass方法中调用super方法