使用 bind 强制创建原型函数的上下文

Using bind to force the context of a prototype function

本文关键字:函数 上下文 原型 创建 bind 使用      更新时间:2023-09-26

我正在研究一些JavaScript代码,这些代码将用于为我们的企业级软件构建"类"(特别是视图模型)的构造函数。我正在做的一件事是建立一个代码强制设计模式,其中实现者将显式定义他们希望由类实例公开哪些函数,代码将它们添加到构造函数的prototype,而不是类的每个实例的属性。当然,这样做的好处是每种类型只有一个实例,而不是每个实例都有一个实例。

这是我大多数示例的代码笔。

问题是,在某些条件下,我正在处理绑定损失问题。例如,使用此构造函数:

  function Foo(myName) {
    this.firstName = myName;
  }
  Foo.prototype.greet = function(yourName) {
    alert("Hello, " + yourName + ". I am " + this.firstName + ".");
  }

。这将起作用:

var sam = new Foo("Sam");
// Alerts "Hello, Denny. I am Sam."
sam.greet("Denny");

。但这不会:

var sad = new Foo("Sad");
// This changes the context of greet. :(
var sadGreet = sad.greet;
// Alerts "Hello, Denny. I am undefined."
sadGreet("Denny");

这是因为当我们执行var sadGreet = sad.greet时,我们正在将greet函数的上下文更改为 window ,因此调用sadGreet("Denny")this.firstName不存在。

因此,我想出的一个解决方案是用调用该原型的属性覆盖原型,并将其包装在Function.prototype.bind()中:

function Bar(myName) {
  this.firstName = myName;
  // Here's where the magic happens.
  this.greet = Bar.prototype.greet.bind(this);
}
Bar.prototype.greet = function(yourName) {
  alert("Hello, " + yourName + ". I am " + this.firstName + ".");
}
var happy = new Bar("Happy");
// Since each instance of Bar overwrites the context for greet, this will work. :)
var happyGreet = happy.greet;
// Alerts "Hello, Denny. I am Happy."
happyGreet("Denny");

我的问题是:我假设Bar不如Foo有效,但这是否完全抵消了将greet声明为prototype方法的任何好处?无论如何,它只是将greet复制为属性,还是我对bind的呼吁只是为Bar.prototype.greet添加一个"包装器"?换句话说,这实际上与上述对 Bar 的定义完全相同吗?

function Bar(myName) {
  this.firstName = myName;
  // Here's where the magic happens.
  this.greet = function(yourName) {
    alert("Hello, " + yourName + ". I am " + this.firstName + ".");
  }
}

如果您不仅可以回答问题,还可以告诉我如何测试它,则可以获得奖励积分!

Bar 的效率肯定不如 Foo,但在时间复杂度方面却没有。相反,它在内存消耗方面的效率较低,因为 Bar 的每个实例现在都有一个唯一且新的"greet"函数对象("bind"函数创建一个新的函数对象)。

看看你的代码,甚至在Bar.prototype上拥有"greet"功能实际上变得有点复杂。相反,这也适用于:

function Bar(myName) {
  this.firstName = myName;
  this.greet = greet.bind(this);
}
var greet = function(yourName) {
  alert("Hello, " + yourName + ". I am " + this.firstName + ".");
};
var happy = new Bar("Happy");
var happyGreet = happy.greet;
happyGreet("Denny");

您可以通过简单地尝试以下命令来测试"greet"函数是否与每个实例的"greet"函数不同:

console.log( greet === happyGreet );