Adding Methods To Objects
Solution 1:
I want to understand the logic.
$elect
is a constructor function that is supposed to be called with the new
keyword (new $select
). If it is not ($elect()
), what will happen then? There is no instance constructed, and the this
keyword will point to the global object (window
) - which we do not want. So this snippet is a guard against that occasion, when it detects that it invokes itself correctly with new
and returns that.
If I remove it, function breaks
When you are calling it like $elect(id)
without the guard, it will add a elm
property to the global object (a global variable in essence) and return nothing. Trying to call the .toggle()
method on that will yield the exception undefined has no method 'toggle'
.
I tried to remove the if statement, assuming
window === this
returns true
Well, then you just created an infinite recursive function that will lead to a stack overflow exception when called.
Btw, the proper way to guard against new
-less invocation is not to compare against window
(the global object might have a different name in non-browser environments) and assume everything else to be OK, but to check for the correct inheritance. You want the constructor only to be applied on instances of your "class", i.e. objects that inherit from $elect.prototype
. This is guaranteed when called with new
for example. To do that check, you will use the instanceof
operator:
function$elect(id) {
if (! (this instanceof $elect)) {
return new $elect(id);
}
this.elm = document.getElementById(id);
}
This also makes the intention of the guard explicit.
Solution 2:
To understand how that condition works, you must first know that in the global scope this
refers to the window
object. In local scope, such as within an object, this
refers to the object itself.
Your $elect
function is a constructor for your framework. If you call it directly, like so:
$elect('some-id-blah')
It will first realize that you are trying to create an instance (because the condition window === this
will evaluate to true), and it will recursively create a new instance of itself. Once it does that, this
will no longer refer to the window
object, it will refer to the new instance of your library, thus the condition will not be met.
I hope that's somewhat understandable.
Solution 3:
Your function is a constructor. By default the this
in any function not called explicitly ($elect()
, not foo.$elect()
) on any other object as a method, will be passed the global object (the window
) in this
. However the if
checks that if the this
indeed refers to the window
object, the return new $elect(id);
is executed. The new $elect(id)
does the following:
- make a new instance of the
$elect.prototype
- put the newly created instance in the
this
and call the method again.
On the second pass, the this === window
evaluates to false
.
Solution 4:
First thing to understand is that this function is calling itself:
function$elect(id) {
if (window === this) {
returnnew $elect(id);
}
this.elm = document.getElementById(id);
}
First time you call the function window is going to be === this. Because there you're invoking the function as a method. When invoked as a method, functions will be bound to the object the function/method is a part of. In this example, this
will be bound to the window, the global scope.
But then with the new
keyword, you're invoking the function as a constructor. When invoked as a constructor, a new Object will be created, and this
will be bound to that object, so this
no longer refers to window
the second time around.
With the new instance of $elect, you're then also setting a local variable elm correctly to the instance, allowing your other functions to call on that elm later.
Solution 5:
This logic:
if (window === this) {
returnnew $elect(id);
}
Ensures that, if your constructor function is called as a function:
var foo = $elect(id);
rather than as a constructor:
var fo = new$elect(id);
it will return the correct result - a new $elect object. When called as a function, the default context will be the global context or window
when running in the browser, triggering the if
clause and returning the result of calling it as a constructor.
Why it isn't working in your fiddle
In the linked fiddle, it is setup to wrap your code in a onload
handler. The result of which looks like this:
window.onload=function(){
function$elect(id) {
if (window === this) {
returnnew $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 = '';
}
}
};
}
Because of that, your $elect
variable isn't visible outside of the onload
handlers scope.
Generally, you would want to add one variable to the global scope to enable access to your framework. Something like this added at the end of your code above will make it visible:
window.$elect = $elect;
Post a Comment for "Adding Methods To Objects"