Blog-Archiv

Samstag, 27. September 2014

This JS new ROFLCOPTER

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!"

If you need stronger stuff, go to this site ...

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 the prototype 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 dot
  • this 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:

forgotNew=undefined
A = 1
B = 2
appliedNew.A = 3
appliedNew.B = 4
A = 1
B = 2
We see that
  • 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
Mind: by leaving out 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:

fooCapsule.A = 5
fooCapsule.B = 6
A = 1
B = 2
We can see that the global variables were not affected by that, still A=1 and 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.
And this is the reason why some JavaScript Gurus recommend to NOT use "this"!
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:

fooBar1.A = 9, fooBar1.B = 10
fooBar2.A = 11, fooBar2.B = 12
A = 7
B = 8
We see that the global variables A and B were not affected. And we created new Object instances without using the 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: