将函数连接到原型与否之间的差异

Differences Between Hooking a Function onto Prototype or Not

本文关键字:之间 函数 连接 原型      更新时间:2023-09-26

之间有什么区别

function Gandalf() {
    this.color = 'grey';
}
Gandalf.prototype.comeBack = function() {
    this.color = 'white';
}

和:

function Gandalf() {
    this.color = 'grey';
    this.comeBack = function() {
        this.color = 'white';
        return this;
    };
}

在什么情况下,我应该将方法挂接到原型上,或者不挂?

好吧,Kay探员,我会用不同于Esailija的方式来处理这个问题。让我们从简单的对象开始。例如,假设您已经有一个名为gandalf:的对象

var gandalf = {
    color: "grey",
    comeBack: function () {
        this.color = "white";
        return this;
    }
};

此代码中没有构造函数。因此,此代码易于理解。我是对的还是对的?


好吧,现在我要做一些很棒的事情。我将从gandalf:创建radagast

var radagast = Object.create(gandalf);
radagast.color = "brown";

这里发生了什么?让我们把它分解:

  1. 我们使用Object.creategandalf创建了一个名为radagast的新对象。因此CCD_ 7继承自CCD_
  2. 我们将radagastcolor设置为棕色,而不是灰色

更简单地说,想象一下你和我进行了以下对话:

I: Hey Agent Kay, I met Radagast yesterday.

U: Who's Radagast?

I: Hmm... do you know who's Gandalf?

U: Yes, I do.

I: Well Radagast is just like Gandalf except that he's brown instead of white.

这正是继承在JavaScript中的工作方式。您可以看到JavaScript具有原型继承。在原型继承中,对象从其他对象继承。在这种情况下,radagast继承自gandalf


现在,他们通常在JavaScript中实现原型继承的方式很奇怪。我称之为原型继承的构造函数模式。例如,以您的代码:

function Gandalf() {
    this.color = "grey";
}
Gandalf.prototype.comeBack = function() {
    this.color = "white";
    return this;
};

现在,当您创建Gandalf的实例时,该实例从哪个对象继承?它继承自Gandalf.prototype:

var gandalf = new Gandalf;
alert(Object.getPrototypeOf(gandalf) === Gandalf.prototype); // true

当你说new Gandalf是时会发生什么

var gandalf = Object.create(Gandalf.prototype);
Gandalf.call(gandalf);

如果你扩展Gandalf.call(gandalf),你会得到:

var gandalf = Object.create(Gandalf.prototype);
gandalf.color = "grey";

现在举第二个例子:

function Gandalf() {
    this.color = "grey";
    this.comeBack = function() {
        this.color = "white";
        return this;
    };
}

在这种情况下,当您创建Gandalf的实例时,实际上是在执行以下操作:

var gandalf = Object.create(Gandalf.prototype);
gandalf.color = "grey";
gandalf.comeBack = function () {
    this.color = "white";
    return this;
};

因此,每次创建新的Gandalf时,都会创建一个新的函数comeBack。因此,如果你调用new Gandalf 10次,你也会有10个不同的comeBack函数。

与第一个例子相比,因为comeBack是在Gandalf.prototype上定义的,所以每次调用new Gandalf时,我们都会得到一个继承自Gandalf.prototype的新对象。因此,只有一个comeBack函数在Gandalf的所有实例之间共享。这不是更好吗?


从本质上来说,你的第一个例子是这样的:

var gandalfPrototype = {
    create: function () {
        var gandalf = Object.create(this);
        gandalf.color = "grey";
        return gandalf;
    },
    comeBack: function () {
        this.color = "white";
        return this;
    }
};
var gandalf = gandalfPrototype.create(); // there's only one comeBack function

类似地,你的第二个例子相当于这个:

var gandalfPrototype = {
    create: function () {
        var gandalf = Object.create(this);
        gandalf.color = "grey";
        gandalf.comeBack = function () {
            this.color = "white";
            return this;
        };
        return gandalf;
    }
};
var gandalf = gandalfPrototype.create(); // there's 1 comeBack for each instance

请记住,在函数调用之前使用new时,实际上是从函数的prototype继承的。而不是函数本身。


最后,我想说(因为我是J.R.R.托尔金的忠实粉丝),这就是我写代码的方式:

var wizard = {
    create: function (color) {
        var wizard = Object.create(this);
        wizard.color = color;
        return wizard;
    }
};
var gandalf = wizard.create("grey");
gandalf.comeBack = function () {
    this.color = "white";
    return this;
};
var saruman = wizard.create("white");
var radagast = wizard.create("brown");

谢谢你阅读我的答案。

之所以将其放在原型上,是因为创建的每个对象都可以使用相同的函数对象(函数只是可调用的对象),它们只需要不同的标识和数据。但在第二种情况下,您将为每个对象创建唯一的函数对象,即使它们可以使用相同的函数对象。

既然你似乎了解PHP,那么在PHP中的区别是相似的:

class Gandalf {
    public $color = "grey";
    public function comeBack() {
        $this->color = "white";
    }
}

class Gandalf {
    public $color = "grey";
    //Create the method inside constructor everytime
    public function __construct() {
        $this->comeBack = function() {
            $this->color = "white";
            return $this;
        }->bindTo($this, $this);
    }
}

当性能不受关注时,很难说何时使用另一个。就我个人而言构造函数的模糊和额外的缩进级别足以让我更喜欢原型。

要回答:

在什么情况下,我应该将方法挂接到原型上,或者不挂?

如果您有一个函数需要访问使用构造函数创建的对象的私有vars,那么它需要位于构造函数内部。

function Gandalf() {
    var age = '30000'; //approximate
    this.alertAge1 = function() {
        alert( age );
    };
}
Gandalf.prototype.alertAge2 = function() {
    // age not visible from here
};