If you haven't yet been rolling on the floor laughing today, you could try this. Read the article, and then the comment below ...
If this didn't make you ROFL, go further and try to find out what's the meaning of
Array.prototype.slice.call(arguments, 1)
.
For that you could go to this.
Try to understand the article, read the comments below ...
Still not ROFLING? You must be a JavaScript programmer :-) They've nothing to laugh anymore :-(
For me, I could not stop laughing about these "excellent" comments:
- "I found that javascript is easier to understand than english"
- "This explanation has done more to deepen my understanding of javascript than anything I have ever read."
- "Excellent explanation. You made so many other things clear too!"
But you're right, it is really serious. Failed communication is the root of all evil,
or was it premature optimization?
Whatever, I must find out what happens when I call new
in JavaScript.
For anyone knowing that new
is next to the C-language alloc(bytes)
, finding
this out must be like receiving the >sign of the times<!
The JavaScript keyword "new"
First I try it with an Object as argument:
1 2 | var someObject = {}; var someInstance = new someObject; // [object Object] is not a function |
So this fails, new
requires a function to be behind it.
1 2 3 4 | var someFunction = function() { console.log("Could it be that I am a constructor?"); }; var someInstance = new someFunction(); |
So, this worked, no error. But what is the return?
1 2 3 4 5 6 | console.log("someInstance instanceof Object = "+(someInstance instanceof Object)); console.log("someInstance instanceof Function = "+(someInstance instanceof Function)); console.log("someInstance = "+someInstance); console.log("someInstance.constructor = "+someInstance.constructor); console.log("someInstance.prototype = "+someInstance.prototype); |
This outputs:
someInstance instanceof Object = true someInstance instanceof Function = false someInstance = [object Object] someInstance.constructor = function() { console.log("Could it be that I am a constructor?"); } someInstance.prototype = undefined
The created thing
- is a (new) Object,
- has the given function as "constructor",
- has no "prototype" property (lets say it has no super-Object)
- and the function has been executed in the context of the returned Object.
Because we can not call new
on Objects, it seems that classes are
represented by functions in JavaScript.
To be able to distinguish between functions that create instances
and such that do not, some wise people introduced the convention to
write constructor-functions capitalized.
1 2 3 4 5 | var Foo = function(a, b) { this.A = a; this.B = b; }; var foo = new Foo(-1, 0); |
Now before digging into the "this" keyword, here is a summary of the >sign of the times<
(semantics that have gone into the C alloc()
function). The new
keyword ...
- provides a new Object,
- sets the inaccessible
__proto__
of that Object to theprototype
of the given function (whatever that means), - calls the function with that new Object as context,
- when the function returns an Object, that Object is returned,
- otherwise the newly created Object is returned.
So keep in mind: when your "constructor" function returns an Object, THAT will be what the new
operation returns! (And you are self responsible whether the instanceof
operator works on it correctly.)
1 2 3 4 5 6 7 8 | var Animal = function(givenName) { return { name: givenName }; } var animal = new Animal("Garfield"); console.log("animal instanceof Animal: "+(animal instanceof Animal)); |
This yields:
animal instanceof Animal: false
The JavaScript keyword "this"
When looking for explanations of the this
keyword, you can find
sentences like
this
is whatever is left of the dotthis
in a function is the Object that called the function
Following code refers to the Foo
constructor function above.
1 2 3 4 5 6 7 8 9 10 11 12 | var forgotNew = Foo(1, 2); // "this" is the "window" object console.log("forgotNew="+forgotNew); console.log("A = "+A); console.log("B = "+B); var appliedNew = new Foo(3, 4); // "this" is the object created by "new" console.log("appliedNew.A = "+appliedNew.A); console.log("appliedNew.B = "+appliedNew.B); console.log("A = "+A); console.log("B = "+B); |
This outputs:
We see thatforgotNew=undefined A = 1 B = 2 appliedNew.A = 3 appliedNew.B = 4 A = 1 B = 2
- the function did not return anything (void)
- when called without the
new
operator,- it wrote A and B to the context of the caller, which in this case was the browser's window Object
- when called with the
new
operator,- it wrote A and B to the Object created by the
new
operator
- it wrote A and B to the Object created by the
new
operator you create unintended global variables!
So the convention to write "constructor functions" capitalized gets some more sense. There should never be a constructor function call without a preceding
new
.
The "this" is an Object, I call it context. Functions are not bound to their objects. Sometimes we do not realize that context changes in JavaScript. Look at following example.
1 2 3 4 5 6 7 8 9 10 11 12 | var fooCapsule = { Foo: function(a, b) { this.A = a; this.B = b; } }; fooCapsule.Foo(5, 6); console.log("fooCapsule.A = "+fooCapsule.A); console.log("fooCapsule.B = "+fooCapsule.B); console.log("A = "+A); console.log("B = "+B); |
This outputs:
We can see that the global variables were not affected by that, still A=1 and B=2.fooCapsule.A = 5 fooCapsule.B = 6 A = 1 B = 2
But imagine setting a "function pointer" to some encapsulated function and then calling it:
1 2 3 4 5 6 | var myFoo = fooCapsule.Foo; myFoo(7, 8); console.log("fooCapsule.A = "+fooCapsule.A); console.log("fooCapsule.B = "+fooCapsule.B); console.log("A = "+A); console.log("B = "+B); |
This outputs:
fooCapsule.A = 5 fooCapsule.B = 6 A = 7 B = 8
We see that the globale A=7 and B=8 were affected now. Why? The function was called by a context that was not the Object the function was defined within. It was called from the browser's window Object, and thus it worked in that context.
This is another aspect of scoping in JavaScript:
- functions are not bound to the Objects they were defined in.
Because it might be confusing, and functions called by a wrong context could do unintended damage.
When you don't use "this", you won't need "new". Both are not needed when using what I call "factory functions" to create Objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var fooFactory = function(a, b, context) { if ( ! context ) context = {}; context.A = a; context.B = b; return context; }; var fooBar1 = fooFactory(9, 10); var fooBar2 = fooFactory(11, 12); console.log("fooBar1.A = "+fooBar1.A+", fooBar1.B = "+fooBar1.B); console.log("fooBar2.A = "+fooBar2.A+", fooBar2.B = "+fooBar2.B); console.log("A = "+A); console.log("B = "+B); |
This outputs:
We see that the global variables A and B were not affected. And we created new Object instances without using thefooBar1.A = 9, fooBar1.B = 10 fooBar2.A = 11, fooBar2.B = 12 A = 7 B = 8
new
operator.
The only difference between new
and creating Objects by {}
is the "protoype" property, which is set by the new
operator into
the new Object. But this plays a role only when modelling inheritance hierarchies.
Keine Kommentare:
Kommentar veröffentlichen