This example code below is #36 in John Resig`s Learning Advnaced JavaScript. http://ejohn.org/apps/learn/#36
It is called We need to make sure the new operator is always used.
Six Questions - I would appreciate as much detail as you can provide
1) Is function User
ever actually called in this code? I note that when it says assert(user...)
, user is lower case. If the function gets called, how? does it get called when it asserts the variable user, which has a function call attached to it i.e. User("John," name)
2) If Im correct in assuming that function User is never called, is there a way that the code
this.name = first + " " + last;` is run?
3) If the function User is called, or if it were to be called, can you please explain the order of operations inside the function User. For example, it returns new User before it does this.name = first + " " + last;
how would that work if this function were called or is called?
4) in what way could !(this instanceof User)
if be true. since the function User is the object, wouldn`t "this" always be an instance of itself?
5) regarding the first assert i.e. assert(user, "this was defined correctly, even if it was by mistake")
, can you please explain how it was defined correctly, and, importantly, please explain how it was a mistake? How should it have been done so it`s not a mistake?
6) regarding the second assert, why is it a noteworthy that the right name was maintained? Isnt it as simple as variable
namehaving been assigned
Resig`. In what way might you have expected name to change?
function User(first, last){
if ( !(this instanceof User) )
return new User(first, last);
this.name = first + " " + last;
}
var name = "Resig";
var user = User("John", name);
assert( user, "This was defined correctly, even if it was by mistake." );
assert( name == "Resig", "The right name was maintained." );
This example is demonstrating a fundamental flaw in JavaScript's design, which is that calling functions intended to be used as constructors without the new
operator can cause unintentional modification of the global object.
User
is called on this line: var user = User("John", name);
. Lowercase user
holds a reference to the new uppercase User
instance.
See #1.
If User
is called without new
, then this
will not be an instanceof
User
. When this happens, we immediately call new User(...)
. In this second call, this
will be an instanceof
User
, so the conditional block is skipped and we simply continue on with the constructor, creating a new instance of User
as originally intended.
When it's called without the new
operator, the value of this
in a function simply refers to the global object, which is window
in a browser. This is a serious mistake in the design of JavaScript, for which this exercise is demonstrating a workaround.
Because there is no explict return from the User
constructor (without the instanceof
check's block) , if User
were called without new
it would return undefined
and the assert
would fail. The instanceof
check prevents this.
Without the protection of the instanceof
check, calling User
without new
would cause unexpected results. Since this
would refer to window
, the assignment to this.name
would update window.name
, not the name
property of a new User
instance, and the assert
would fail.
Any given function object can be used two ways.
The difference being the value of the this
binding. When you use a function as a constructor the this
binding will be a new object that inherits from the constructor's public prototype. Normally calling a function will set the this
binding to the global object. (With strict mode in ES5 this will not happen.)
The second assert is very important now that you know that the this
binding can be coerced to the global object.
A Simplification of Calling a Constructor
function ConstructUser(fname, lname) {
var obj = {}; // Empty Object
// In reality a special internal Prototype property
// is assigned rather than 'obj.prototype'.
if (User.prototype instanceof Object) {
obj.prototype = User.prototype;
} else {
obj.prototype = Object.prototype;
}
// Now call the User function with the
// new object as the 'this' binding.
User.call(obj, fname, lname);
return obj;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With