3

在观看 Douglas Crockford 关于高级 JavaScript 的讲座时,他提出了寄生继承的想法,它本质上是让构造函数调用其他构造函数来修改相关对象。这是他的代码:

function gizmo(id, secret) {
  secret = secret || {};
  secret.id = id;
  return {
    toString: function () {
      return "gizmo " + secret.id;
    }
  };
}

function hoozit(id) {
  var secret = {},
      that = gizmo(id, secret);
  that.test = function (testid) {
    return testid === secret.id;
  };
  return that;
}

var myHoozit = hoozit(20);
console.log(myHoozit.test(20)); //returns true

我理解代码,这里没有什么太难掌握的地方。混乱发生在hoozit函数中。如果你不设置secret = {},你将不会得到 atrue被返回。

这令人费解,因为在gizmo函数中,您会看到secret = secret || {}哪个应该为我们处理这个问题……但事实并非如此。

secret = secret || {}为什么没有在gizmo函数中传递第二个参数hoozit(Chrome和Firefox中的中断)时,函数中的短路不能正常工作( )?

4

3 回答 3

5

secret = secret || {}为什么没有在gizmo函数中传递第二个参数hoozit(Chrome和Firefox中的中断)时,函数中的短路不能正常工作( )?

很简单,因为您无法访问secret内部that.test,因为它在该范围内不存在:

function hoozit(id) {
  var that = gizmo(id);
  that.test = function (testid) {
    // secret is not defined in this or in any higher scope
    // hence you get a refernece error
    return testid === secret.id;
  };
  return that;
}

唯一secret存在的对象是gizmo函数的本地对象。


如果您定义它并且只是不将其传递给gizmo,那么secret = secret || {}将评估为secret = {},即在gizmo函数内部创建一个新对象。该值只能在函数内访问,与函数中的变量gizmo完全无关。里面的对象与里面的对象不同。secrethoozitsecretgizmohoozit

function hoozit(id) {
  var secret = {},      // secret object is created here
      that = gizmo(id);
  that.test = function (testid) {
    // you never set `secret.id`, hence the comparison results in `false`
    return testid === secret.id;
  };
  return that;
}

没有任何问题secret = secret || {},它按预期工作。

于 2013-08-03T21:42:49.717 回答
2

让我们看一个更简单的例子。

function Foo(baz){
    baz = baz || {};
    baz.boo = 1;
}

function Bar(baz){
    baz = baz || {};
    Foo(baz);
    return baz;
}

如果我们调用Bar(),我们将一个对象传递给 Foo。boo然后,它通过设置一个属性来对对象进行 aguments 。甜的!

现在,假设我们的 Bar 函数如下所示:

function Bar(baz){
    baz = baz || {};
    Foo();
    return baz;
}

唯一的区别是我们没有将对象传递给 Foo。因此,Foo 在其范围内创建了一个对象。我们在那个新对象上设置一个属性,然后函数结束。该对象不是 Bar 的父范围,因此 Bar 永远不知道该对象已创建,也无法访问它。在接下来的几毫秒内,该对象将从 RAM 中删除,因为没有对其的引用。


您的问题并非完全如此。该toString函数引用它。因为外部作用域是完整的,所以它现在是该toString函数的局部变量。如果我们没有传递一个secret对象,那么它就永远不会离开那个范围。它必须以某种方式被导出。

更合乎逻辑的策略是将其创建为我们原始对象的属性。知道 JavaScript 是什么的用户可以轻松访问我们的秘密,因此我们应该省去一些麻烦并使用合理的继承方法。

如果你不知道SomeFunction.call需要任何数量的参数。第一个是你想this在函数中出现的任何内容,其余的只是常规参数。

function gizmo() {
    this.toString = function () {
        return "gizmo " + this.id;
    };
};

function hoozit(id) {
    this.id = id;
    gizmo.call(this); // allow gizmo to manipulate this object

    this.test = function (is) {
        return this.id === is;
    };
};

h = new hoozit(1);
console.log(h.test(1)); // true
console.log(h.toString()); // "gizmo 1"
于 2013-08-03T22:13:12.830 回答
1

你需要secret = {}.


return testid === secret.id;因为secret需要存在所以出错了。

您可能正在寻找的主要魔法是,在哪里secret.id填充,因为所有操作都发生在gizmo(). 答案是这一行:that = gizmo(id, secret);

secret传递给 gizmo,在 JavaScript 中对象是通过引用传递的。这意味着如果您有一个本地对象并将该对象作为参数传递给另一个函数,则对该对象的任何操作都将在本地反映。

如果您不希望这种情况发生,则需要对参数进行某种形式的复制/克隆(库已错误地使用术语克隆来暗示深层副本)。但在示例中,您确实希望更改secretin gizmo 以更新secretin hoozit,因此一切正常。


这是另一种写法:

function gizmo(secret) {              // only receive secret, which already has an "id"
   secret = secret || {'id':null};    // if secret not passed create it with a default "id"
   return {
      toString: function () {
         return "gizmo " + secret.id; // the reason why we needed a default "id"
      }
   };
}

function hoozit(id) {
   var secret = {'id':id},            // create a object and set key "id" to hoozit's argument
       that   = gizmo(secret);        // only pass secret

   that.test  = function (testid) {
      return testid === secret.id;
   };

   return that;
}

var myHoozit = hoozit(20);
console.log( myHoozit.test(20) ); //returns true
于 2013-08-03T22:15:34.713 回答