向对象添加方法

Adding methods to objects

本文关键字:方法 添加 对象      更新时间:2023-09-26

在用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

JSFIDDLE JSBIN

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

我想了解其中的逻辑。

$elect是一个构造函数,应该使用 new 关键字 (new $select ) 调用。如果不是($elect()),那会发生什么?没有构造实例,this 关键字将指向全局对象 ( window ) - 我们不想要。因此,当它检测到它使用 new 正确调用自身并返回该时,此代码段可以防止这种情况。

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

当你在没有守卫的情况下像$elect(id)一样调用它时,它将向全局对象(本质上是一个全局变量)添加一个 elm 属性并且不返回任何内容。尝试调用 .toggle() 方法将产生异常undefined has no method 'toggle'

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

好吧,然后你刚刚创建了一个无限递归函数,该函数在调用时会导致堆栈溢出异常。


顺便说一句,防止new-less调用的正确方法不是与window进行比较(全局对象在非浏览器环境中可能具有不同的名称)并假设其他一切都正常,而是检查正确的继承。您希望构造函数仅应用于"类"的实例,即从$elect.prototype继承的对象。例如,当使用 new 调用时,这是可以保证的。要执行该检查,您将使用 instanceof 运算符:

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

这也使守卫的意图明确

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

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

$elect('some-id-blah')

它将首先意识到您正在尝试创建一个实例(因为条件window === this的计算结果为 true),并且它将递归创建自身的新实例。一旦这样做,this将不再引用window对象,它将引用库的新实例,因此将不满足条件。

我希望这在某种程度上是可以理解的。

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

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

在第二遍时,this === window的计算结果为 false

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

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

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

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

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

这个逻辑:

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;

演示小提琴

如果你这样称呼它:

$elect("theId").hide();
第一次在其中调用

为函数,将找到window === this并执行以下操作:

return new $elect("theId")

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

第二次,它被称为构造函数,所以window !== this,它将向下传递以查找元素并将其存储在内部。

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

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

请注意,如果您这样称呼它:

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

它不起作用,因为this设置为bob 哪个 get 和 'elm' 属性集,并且没有要调用的"隐藏"属性,因为它不是一个$elect类型的对象。

注意

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

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

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