【问题标题】:Adding methods to objects向对象添加方法
【发布时间】:2013-09-23 12:06:25
【问题描述】:

在用 JS 编码一段时间后,我决定制作自己的框架。类似于 jQuery 的东西。但是一个非常精简的版本。经过一番谷歌搜索,我把这段代码放在一起:

function $elect(id) {
    if (window === this) {
        return new $elect(id);
    }
    this.elm = document.getElementById(id);
}

$elect.prototype = {
    hide:   function () { this.elm.style.display = 'none';  },
    show:   function () { this.elm.style.display = '';      },
    toggle: function ()
            {
                if (this.elm.style.display !== 'none') {
                    this.elm.style.display = 'none';
                } else {
                    this.elm.style.display = '';
                }
            }
};

到目前为止,这似乎有效。但我对功能不感兴趣。我想了解其中的逻辑。添加方法部分是可以理解的。虽然我不明白

的功能
    if (window === this) {
        return new $elect(id);
    }

如果我删除它,功能就会中断。由于这是一个 if 语句,因此有 2 个结果。 Truefalse。因此,我尝试删除 if 语句并仅使用 return new $elect(id); 假设 window === this 返回 true,但这不起作用。然后我认为它可能会返回false,所以删除了整个 if 语句。那也没用。有人可以启发我吗?这段代码也有效吗?我可能错过了一些东西。

刚刚在 jsfiddle 上测试过,它不起作用。虽然它适用于 jsbin o.O

jsfiddlejsbin

编辑:使用$elect(id).toggle(); 调用它。虽然您可以查看演示。

【问题讨论】:

  • 当你不理解的时候,你从哪里得到那个 sn-p?
  • 显示你如何称呼它。我认为这是我缺少的线索。
  • @Bergi 在谷歌某处?我应该发布网址吗?
  • 请。不仅因为它是错误的,而且因为作者可能有一些解释。当然还有正确的归属。
  • @Bergi This 是网站。

标签: javascript function object methods


【解决方案1】:

我想了解其中的逻辑。

$elect 是一个constructor function,应该用new keyword (new $select) 调用。如果不是($elect()),那会发生什么?没有构造实例,this keyword 将指向全局对象 (window)——这是我们不想要的。所以这个 sn-p 可以防止这种情况发生,当它检测到它用new 正确调用自己并返回它时。

如果我删除它,功能会中断

当你像 $elect(id) 那样调用它时,它会向全局对象(本质上是一个全局变量)添加一个 elm 属性并且什么都不返回。尝试对其调用.toggle() 方法将产生异常undefined has no method 'toggle'

我试图删除 if 语句,假设 window === this 返回 true

那么,你刚刚创建了一个无限递归函数,调用时会导致堆栈溢出异常。


顺便说一句,proper way to guard against new-less invocation 不是为了与window 进行比较(全局对象在非浏览器环境中可能有不同的名称)并假设其他一切正常,而是检查正确的继承。您希望构造函数仅应用于“类”的实例,即从 $elect.prototype 继承的对象。例如,当使用 new 调用时,可以保证这一点。要进行该检查,您将使用 instanceof operator:

function $elect(id) {
    if (! (this instanceof $elect)) {
        return new $elect(id);
    }
    this.elm = document.getElementById(id);
}

这也明确了守卫的意图。

【讨论】:

    【解决方案2】:

    要了解该条件是如何工作的,您必须首先知道在全局范围内this 指的是window 对象。在本地范围内,例如在对象内,this 指的是对象本身。

    您的$elect 函数是您的框架的构造函数。如果你直接调用它,像这样:

    $elect('some-id-blah')
    

    它首先会意识到您正在尝试创建一个实例(因为条件window === this 将评估为真),然后它会递归地创建一个自己的新实例。一旦这样做,this 将不再引用 window 对象,它将引用您库的新实例,因此将不满足条件。

    我希望这有点可以理解。

    【讨论】:

    • 我不确定你的意思,代码正在使用 return new $elect(id); 返回一个新实例。
    • 确实,你是对的。我的错误:) 他应该在设置this.elm 后返回this
    • @LeeMeador - 没有理由从构造函数返回 this - 它已经隐含地这样做了。使用它可以按预期工作而无需返回:(new $elect('textarea')).toggle();
    • 在顶部的 cmets 中有人说,作为构造函数,无论如何都会返回“this”。这解释了它Mozilla.org
    • 我删除了所有不正确的 cmets,因此任何未来的读者都不必在脑海中处理它们。对不起。
    【解决方案3】:

    你的函数是一个构造函数。默认情况下,任何未在任何其他对象上作为方法显式调用的函数($elect(),不是foo.$elect())中的this 将被传递给this 中的全局对象(window)。然而,if 检查如果this 确实引用了window 对象,则执行return new $elect(id);new $elect(id) 执行以下操作:

    • 创建$elect.prototype 的新实例
    • 将新创建的实例放入this,再次调用该方法。

    在第二次通过时,this === window 的计算结果为 false

    【讨论】:

      【解决方案4】:

      首先要了解的是这个函数正在调用自己:

      function $elect(id) {
          if (window === this) {
              return new $elect(id);
          }
          this.elm = document.getElementById(id);
      }
      

      第一次调用函数窗口将是 === this。因为您将函数作为方法调用。当作为方法调用时,函数将绑定到函数/方法所属的对象。在本例中,this 将绑定到窗口,即全局范围。

      但是使用new 关键字,您将函数作为构造函数调用。当作为构造函数调用时,将创建一个新对象,并且this 将绑定到该对象,因此this 不再引用window

      使用 $elect 的新实例,您还可以为该实例正确设置一个局部变量 elm,从而允许您的其他函数稍后调用该 elm。

      【讨论】:

        【解决方案5】:

        这个逻辑:

        if (window === this) {
            return new $elect(id);
        }
        

        确保,如果您的构造函数作为函数调用:

        var foo = $elect(id);
        

        而不是作为构造函数:

        var fo = new $elect(id);
        

        它将返回正确的结果 - 一个新的 $elect 对象。当作为函数调用时,默认上下文为全局上下文或在浏览器中运行时为window,触发if子句并返回作为构造函数调用的结果。


        为什么它在你的小提琴中不起作用

        在链接的小提琴中,它被设置为将您的代码包装在 onload 处理程序中。结果如下所示:

        window.onload=function(){
        function $elect(id) {
            if (window === this) {
                return new $elect(id);
            }
            this.elm = document.getElementById(id);
        }
        
        $elect.prototype = {
            hide:   function () { this.elm.style.display = 'none';  },
            show:   function () { this.elm.style.display = '';      },
            toggle: function ()
                    {
                        if (this.elm.style.display !== 'none') {
                            this.elm.style.display = 'none';
                        } else {
                            this.elm.style.display = '';
                        }
                    }
        };
        }
        

        因此,您的 $elect 变量在 onload 处理程序范围之外不可见。

        一般来说,您会希望将一个变量添加到全局范围以启用对您的框架的访问。在上面的代码末尾添加这样的内容会使其可见:

         window.$elect = $elect;
        

        Demo Fiddle

        【讨论】:

          【解决方案6】:

          如果你这样称呼它:

          $elect("theId").hide();
          

          第一次在其中作为函数调用,会找到window === this并执行以下操作:

          return new $elect("theId")
          

          创建一个新函数并再次调用该函数。 (无论函数的第二次调用返回什么,都会返回。)

          第二次进入时,它被调用为构造函数,所以window !== this会向下传递找到元素并在内部存储。

          不从函数本身返回任何内容的构造函数调用将返回创建的实例。

          这样.hide() 部分将以this 的正确值执行(第一次创建的实例)。并且该元素将从屏幕上消失。

          注意,如果你这样称呼它:

          var bob = {};
          bob.$select("theId").hide();
          

          它将不起作用,因为 this 设置为 bob 并设置了 'elm' 属性,并且没有要调用的 'hide' 属性,因为它不是 $elect 类型的对象。

          注意

          如果其他函数中的所有其他方法都返回this,您将能够链接它们:

          $elect('theId').show().red();
          

          假设您添加了一个将元素着色为红色的函数。

          【讨论】:

            猜你喜欢
            • 2023-03-10
            • 1970-01-01
            • 2010-12-25
            • 1970-01-01
            • 1970-01-01
            • 2013-01-18
            • 1970-01-01
            • 2011-04-28
            相关资源
            最近更新 更多