【问题标题】:How to give a specific context to a JavaScript function without invoking it right away如何在不立即调用 JavaScript 函数的情况下为 JavaScript 函数提供特定上下文
【发布时间】:2012-11-25 22:02:43
【问题描述】:

我想创建一个带有一些隐藏(使用闭包)变量的对象,以及利用这些隐藏变量的方法。但是,我不想为每个实例重新创建这些方法。我想让他们记住一次。我正在使用普通函数来实例化对象。

这里有一些代码:

function Mario () {
  var mario = {}; // create the obj
  mario.name = "Mario"; // create a property on it
  mario.hp = (function () { // set up a closure
    var currentHp = Mario.baseHp * 100; // create a "hidden" variable
    return function (value) { // return the hp function that utilizes the hidden currentHp variable
      if (!!value) {
        return currentHp = ((currentHp * Mario.baseHp) + value);
      }
      return currentHp * Mario.baseHp;
    };
  }());
  return mario; // send along the newly created object
}
Mario.baseHp = 1;

var mario = Mario();
log(mario);
log(mario.hp());
log(mario.hp(-20));
log(mario.hp());

这里的问题是,每次我创建另一个“mario”对象时,我都会在内存中再次创建 hp 函数。到目前为止,这是我对解决方案的尝试:

function Mario () {
  var mario = {}; // create the obj
  mario.name = "Mario"; // create a property on it
  mario.hp = (function () { // set up a closure
    var currentHp = Mario.baseHp * 100; // create a "hidden" variable
    return Mario.hp; // reference the hp function below..... but the context is wrong, it needs access to currentHp variable.
  }());
  return mario; // send along the newly created object.
}
Mario.baseHp = 1;
Mario.hp = function (value) { // Create the hp function once in memory
  if (!!value) {
    return currentHp = ((currentHp * Mario.baseHp) + value);
  }
  return currentHp * Mario.baseHp;
};
var mario = Mario();
log(mario);
log(mario.hp());
log(mario.hp(-20));
log(mario.hp());

但显然 Mario.hp 的上下文是错误的。我在想可能有一种方法可以使用 call 或 apply 来解决这个问题。任何帮助都会动摇!

【问题讨论】:

  • 我不确定你想做什么,但you may find this interesting.
  • 哇,这太令人费解了。你能想出一个minimalist的例子来说明这个问题吗?
  • @Pointy 谢谢!我以前遇到过 bind,但现在对我来说更有意义了。

标签: javascript oop closures


【解决方案1】:

通常的解决方案是使用原型和新的:

function Mario () {
  this.name = "Mario"; // create a property on it
  this.baseHP = 33;
}
Mario.prototype.hp = function () { 
    var currentHP = this.baseHp * 100;
    ...
};

var mario = new Mario();
log(mario);
log(mario.hp());
log(mario.hp(-20));
log(mario.hp());

这会创建通常称为Mario 的类。使用new Mario()创建这个类的实例,它们都使用同一个函数hp(函数中的this指的是实例)。

编辑

如果你想存储 currentHP 变量和 hp 函数来返回它,只需这样做:

function Mario () {
  this.name = "Mario"; // create a property on it
  this.baseHP = 33;
  this.currentHP = this.baseHp * 100;
}
Mario.prototype.hp = function () { 
    return this.currentHP;
};

var mario = new Mario();
log(mario);
log(mario.hp());

【讨论】:

  • 如果你在原型的闭包中有currentHP,那么它对于所有实例都是相同的值。我认为这不是 OP 想要的。
  • 我的代码中没有closure。每次调用 hp 时,都会使用实例的 baseHP 值动态计算 currentHP。
  • 你之前的编辑有Mario.prototype.hp = (function () { ...,好像里面有一个封闭的函数。我指的是那个。
  • 左括号只是一个错字:复制粘贴后我忘记删除的括号。
  • 好的,我明白你的意思了。您希望 hp 函数成为访问器。见编辑。
【解决方案2】:

我认为所有关于原型的讨论都是多余的,而且对您的情况并没有那么大的帮助。您在这里并不真正需要原型,因为您的所有Mario 实例对于currentHp 都将具有自己的价值。同样,您无需将 Mario 设为真正的构造函数,也不必担心必须记住 new 关键字(尽管您可以根据需要进行操作)。

据我所知,您尝试做的所有事情都可以通过闭包来处理,另外还可以让您的私人成员(如 baseHP 和您计算 HP 的逻辑)真正保密。试试这个:

var Mario = (function () {
    //"private" variable encapsulated by the closure
    var baseHp = 1;

    //"private" method for calculating HP given an instance's current HP
    var getHp = function (currentHp, value) {
        return currentHp * Enemy.baseHp + (value || 0);
    };

    //in OOP terms, baseHp and getHp would be like private static members used by
    //the whole class.

    return function () {
        //another "private" variable, but this one is only for 
        //the current instance.
        var currentHp = baseHp * 100;

        //instance:
        return {
            name: "Mario",
            hp: function (value) {
                return !!value
                    ? currentHp = getHp(currentHp, value)
                    : getHp(currentHp);
            }
        };
    };
})();

我已经在控制台中对此进行了测试,它似乎工作正常:

//test
var Enemy = {
    baseHp: 3
};
var log = console.log.bind(console);
var mario = Mario(); //or new Mario(); either one is fine
log(mario);
log(mario.hp());
log(mario.hp(-20));
log(mario.hp());

在 jsFiddle 上试用:http://jsfiddle.net/jmorgan123/zkw2P/

【讨论】:

  • 谢谢贾斯汀!这很有帮助。旁注:我将 baseHp 保持为公开状态,以便以后更改它,我可以影响所有马里奥实例的 hp。
【解决方案3】:
var Mario = (function() {
  var name = "Mario"; 
  var hp = function (value) { 
      var currentHp = baseHp * 100; 
      if (!value) {
          return currentHp = ((currentHp * Enemy.baseHp) + value);
      }
      return currentHp * Enemy.baseHp;
  }
  return {
        name:name,
        hp:hp
  }
})();
console.log(Mario);
console.log(Mario.hp());
console.log(Mario.hp(-20));
console.log(Mario.hp());

我对你所做的其余部分的有效性不做任何声明,因为似乎有很多变量显示但没有考虑,但你的外壳似乎在错误的地方。

【讨论】:

  • var function (value) { 这是错字吗?看起来那条线(以及下面匹配的};)不应该在那里。或者 var 应该是 return 并且应该立即运行封闭函数。
  • @JustinMorgan 你是绝对正确的。我一定错过了清理 OP 代码的尝试。
  • 与dystory的答案相同的挑战。每次调用 hp 时,hp 函数都会重置 currentHp 的值。我需要 currentHp 来坚持。我实际上是在尝试将 hp 实现为访问器。
  • 好的,这也很有帮助,关于闭包的位置有一个有趣的地方。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-23
  • 1970-01-01
  • 1970-01-01
  • 2018-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多