关于如何在JavaScript中创建类的困惑

Confusion about how to create classes in JavaScript

本文关键字:创建 于如何 JavaScript      更新时间:2023-09-26

过去在JavaScript中创建"类"时,我是这样做的:

function Dog(name){
    this.name=name;
    this.sound = function(){
        return "Wuf";
    };
}

然而,我只是看到有人这样做:

var Dog = (function () {
    function Dog(name) {
        this.name = name;
    }
    Dog.prototype.sound = function () {
        return "Wuf";
    };
    return Dog;
})();

你能双管齐下吗?还是我做错了?既然如此,为什么?就我们最终的结果而言,两者之间到底有什么区别?在这两种情况下,我们都可以创建一个对象,方法是:

var fido = new Dog("Fido");
fido.sound();

我希望有人能启发我。

您的方式与他们的方式有两个重要区别。

  1. 在自调用函数((function() { ... })();)中封装
  2. 对方法使用this.上的.prototype属性

将事物封装在一个自调用函数中,然后将结果(如return语句中定义的)分配给一个变量,称为模块模式。这是一种常见的模式,可以确保范围得到更好的控制。

this.sound = function()相比,使用Dog.prototype.sound = function() {}是优选的。不同之处在于,使用Dog构造函数为所有对象定义一次Dog.prototype.sound,为创建的每个Dog对象再次定义this.sound = function() {}

经验法则是:一个对象的个体(通常是其属性)将在this上定义,而同一类型的所有对象共享的东西(通常是函数)将在原型上定义。

使用您的代码,您将为正在创建的每个新Dog实例创建一个新函数sound。Javascript的prototype通过只创建一个所有对象实例共享的函数来避免这种情况;基本上是经典的继承。

在您展示的第二段代码中,它只是附加地封装在IIFE中,在这种情况下没有太大作用。

第一种是创建构造函数的传统方法。第二个是立即调用的函数表达式,返回构造函数。此方法允许您将变量保留在模块中,而不会泄漏到可能会出现问题的全局范围中。

就我们最终的结果而言,两者之间到底有什么区别?

正如你所看到的,它们都有相同的结果。其他人已经谈到了prototype,所以我不在这里提及它。

第二个更可取,因为它利用了Javascript的原型继承机制。

原型

Javascript继承是造成混乱的原因,但它实际上相当简单:每个对象都有一个原型,当我们试图访问原始对象以外的属性时,我们会检查这个对象。原型本身将有一个原型;在一个简单的情况下,如Dog,这可能是Object.prototype

在您的两个示例中,由于new操作符的工作方式,我们最终将得到一个原型链,它看起来像这样:fido->Dog.prototype->Object.prototype。因此,如果我们试图在Fido上查找name属性,我们会在对象上找到它。另一方面,如果我们寻找hasOwnProperty属性,我们将在Fido上找不到它,在Dog.prototype上找不到此,然后到达Object.prototype,在那里我们将找到它。

sound的情况下,您的示例在两个不同的地方定义了它:在第一种情况下,fido和我们创建的每个其他狗都将拥有自己的函数副本。在第二种情况下,Dog.prototype将具有该函数的单个副本,当调用该方法时,单个狗将访问该副本。这避免了在存储CCD_ 26函数的副本上浪费资源。

这也意味着我们可以扩展原型链;也许我们想要一个从Dog继承sound函数的Corgi类。在第二种情况下,我们可以简单地确保Dog.prototypeCorgi.prototype的原型链中;在第一种情况下,我们需要创建一个真正的Dog,并将其放入原型链中。