这是在 JavaScript 中定义继承链的“标准”方法吗?

Is this the 'standard' way to define inheritance chains in JavaScript?

本文关键字:标准 方法 JavaScript 继承 定义      更新时间:2023-09-26

我试图理解Javascript中的继承。

我知道每个对象都有一个原型,这是它继承属性的对象。我知道 .prototype 属性只存在于函数上,当它用作构造函数时,它是将设置为从该函数创建的对象的原型

的对象。

我知道对象的原型是无法访问的,尽管某些浏览器支持 __proto__ 属性。(但由于它不是语言的"经典"部分,我想了解如何在没有它的情况下使用该语言)。

因此,如果所有这些都是正确的(?),我想了解定义继承链的标准方法是什么。

我能想到的唯一方法是:

我希望它们从另一个对象继承的所有对象都必须通过构造函数创建。它们的"基对象"将被设置为其构造函数的.prototype

当我希望其中一个对象成为其他对象的"基对象"时,我会将其设置为另一个构造函数的.prototype。等等。

这似乎很奇怪。有没有办法(在"普通"JS中)直接设置对象的"基础"?还是我必须以上述方式使用构造函数才能创建继承链?

创建继承的"标准"方法是什么?我描述的方法是否是标准方法?

JavaScript 支持继承的主要方式是通过原型继承。具体来说,每当在初始对象上找不到属性查找时,JavaScript 中的对象就会委托给其他对象。此委托一直持续到JavaScript引擎到达找到属性或引发错误的Object.prototype

使用特定对象作为原型创建对象的当前最佳实践是使用 Object.create - 您可以在此处查看更多信息。

下面是一个示例:

var methods = {
  method1: function () { console.log( 'something' ); },
  method2: function () { return 'cool'; }
};
/*
 * Now firstObj will delegate to methods whenever a property lookup can't
 * be found on firstObj itself
*/
var firstObj = Object.create( methods ); 
// You can add custom properties on firstObj
firstObj.someOtherProperty = 'custom property'; 
/*
 * You can create a chain of delegations! Property lookup first happens on secondObj.
 * If its not found there, it looks up the property in firstObj. If its not found there,
 * then it looks up the property in methods. Finally, if not found, it tries
 * Object.prototype
*/
var secondObj = Object.create ( firstObj );

JavaScript 中的继承起初有点难以理解,因为:

  1. JavaScript是一种原型的面向对象的编程语言(即对象直接从其他对象继承)。这意味着类和对象之间没有区别。用作类的对象称为原型。
  2. 不幸的是,创建原型实例的传统方法是使用 new(这让人们认为实例继承自构造函数,而不是原型)。这被称为构造函数模式,它是JavaScript中混淆的主要原因。

因此,引入了Object.create。它允许对象直接从其他对象继承。但是,与使用Object.create相比,new很慢。我遇到了和你一样的问题,我正在寻找替代方案;我确实想出了一个。

JavaScript 中 OOP 的传统方式

请考虑以下代码:

function Person(firstname, lastname, gender) {
    this.firstname = firstname;
    this.lastname  = lastname;
    this.gender    = gender;
}
Person.prototype.getFullname = function () {
    return this.firstname + " " + this.lastname;
};
Man.prototype             = new Person;
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
    Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());

这种编写代码的方式存在以下几个问题:

  1. 没有封装。构造函数和原型方法到处都是定义的。它看起来语无伦次。比如沙格蒂。它看起来不像一个逻辑单元。
  2. 我们通过将Man.prototype设置为Person.prototype来继承new Person。但是,在这样做的过程中,我们正在初始化firstnamelastnamegender属性Man.prototype这是错误的。

JavaScript 中的 OOP 新方式

随着Object.create的引入,我们现在可以编写如下代码:

function Person(firstname, lastname, gender) {
    this.firstname = firstname;
    this.lastname  = lastname;
    this.gender    = gender;
}
Person.prototype.getFullname = function () {
    return this.firstname + " " + this.lastname;
};
Man.prototype             = Object.create(Person.prototype);
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
    Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());

唯一的变化是我们写Man.prototype = Object.create(Person.prototype)而不是Man.prototype = new Person。这就解决了传统方法的第二个问题。但是,代码看起来仍然像意大利面条。

但是,Object.create非常强大。您还可以使用它来编写面向对象的代码,而无需创建构造函数。有些人称之为初始值设定项模式:

var person = {
    init: function (firstname, lastname, gender) {
        this.firstname = firstname;
        this.lastname  = lastname;
        this.gender    = gender;
    },
    getFullname: function () {
        return this.firstname + " " + this.lastname;
    }
};
var man = Object.create(person, {
    init: {
        value: function (firstname, lastname) {
            person.init.call(this, firstname, lastname, "M");
        }
    }
});
var bobMarley = Object.create(man);
bobMarley.init("Bob", "Marley");
alert(bobMarley.getFullname());

这解决了传统方法的所有问题。但是,它也引入了一些自己的新问题:

    创建
  1. 原型实例的方式与创建对象文本的方式不一致。
  2. 您必须使用 Object.create 创建一个实例,然后使用 init 初始化新对象。这比简单地使用 new 慢得多。

我的OOP方式是JavaScript

为了解决这个问题,我用JavaScript为OOP编写了自己的函数:

var Person = defclass({
    constructor: function (firstname, lastname, gender) {
        this.firstname = firstname;
        this.lastname  = lastname;
        this.gender    = gender;
    },
    getFullname: function () {
        return this.firstname + " " + this.lastname;
    }
});
var Man = extend(Person, {
    constructor: function (firstname, lastname) {
        Person.call(this, firstname, lastname, "M");
    }
});
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}
function extend(constructor, properties) {
    var prototype = Object.create(constructor.prototype);
    var keys      = Object.keys(properties);
    var length    = keys.length;
    var index     = 0;
    while (index < length) {
        var key = keys[index++];
        prototype[key] = properties[key];
    }
    return defclass(prototype);
}

我在 JavaScript 中为 OOP 定义了两个函数defclassextenddefclass函数从原型创建一个"类"。这是可能的,因为原型和类是同构的。

扩展函数用于继承。它创建一个constructor prototype的实例,并在返回新原型的"类"之前将一些属性复制到该实例上。

这是我目前在 JavaScript 中创建原型链的方式。与其他方法相比,它具有以下优点:

  1. 每个"类"都被封装了。没有到处晃来晃去的原型方法。它看起来不像意大利面。
  2. extend函数使用 Object.create 进行继承。因此,不会向新原型添加额外的属性。这是一个空白的原型。
  3. 您不必担心重置prototype上的constructor属性。它是自动为您完成的。
  4. defclassextend 函数是一致的,这与初始值设定项模式中的对象文本和Object.create函数不同。
  5. 我们使用new而不是Object.createinit创建实例。因此,生成的代码要快得多。

我现在可能是错的,但我不这么认为。希望有帮助。

你可以在javascript中通过2种方式继承 - 经典和原型

古典

function inherit (C, P) {
    var F = function () {};
    F.prototype = P.prototype;
    C.prototype = new F();
}

原型

function inherit (o) { 
    function F() {}
    F.prototype = o;
    return new F(); 
}