1

After coding in JS for a while I decided to make my own framework. Something similar to jQuery. But a very very stripped down version. After some googling I put together this code:

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 = '';
                }
            }
};

So far this seems to work. But I'm not interested in the functionality. I want to understand the logic. Adding methods part is understandable. Though I didn't understand the function of

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

If I remove it, function breaks. Since this is an if statement there are 2 results. True or false. So I tried to remove the if statement and just use return new $elect(id); assuming window === this returns true, but that didn't work. Then I thought it might return false, so removed the whole if statement. That also didn't work. Can someone enlighten me? Also is this code valid? I'm probably missing some stuff.

Just tested on jsfiddle and it doesn't work. Though it works on jsbin o.O

jsfiddle jsbin

EDIT: using $elect(id).toggle(); to call it. Though you can check the demos.

4

6 回答 6

1

我想了解其中的逻辑。

$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);
}

这也使守卫的意图明确。

于 2013-09-18T16:18:36.333 回答
1

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

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

$elect('some-id-blah')

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

我希望这有点可以理解。

于 2013-09-18T16:24:11.110 回答
0

如果你这样称呼它:

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

第一次调用它作为一个函数,并会找到window === this并执行此操作:

return new $elect("theId")

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

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

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

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

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

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

它不起作用,因为它this被设置为bobget 和 'elm' 属性集,并且没有要调用的 'hide' 属性,因为它不是$elect一种对象。

笔记

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

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

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

于 2013-09-18T16:36:38.727 回答
0

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

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

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

于 2013-09-18T16:21:49.390 回答
0

这个逻辑:

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

于 2013-09-18T16:18:26.230 回答
0

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

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

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

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

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

于 2013-09-18T16:21:16.597 回答