Please explain what hack is used here (I can see that null is passed as a context to a function returning a property of it's context. So I can't clearly understand what is actually happening here.
function getGlobal(){
return (function(){
return this.dust;
}).call(null);
}
Setting the context to null will make this pointing to the global object. So the code provided will act as accessing the dust property of the global object.
According to the specification of ECMA 262 v5, 10.4.3 Entering Function Code
if thisArg is null or undefined, set the ThisBinding to the global object.
see http://es5.github.com/#x10.4.3
The trick is to use the fact that if you don't have a receiver of the function, window (in fact the global object of the executed script, hence the name) is used.
So this trick enables to bypass a property (dust) defined in the nearest embedding context and use the one defined in the global object.
Related
Just now I started using the this keyword, and I have been wondering, why doesn't "this" work?
var obj = {
thas: this,
obj2: {
obj3: {
thos: this,
lol: "lol",
pap: function() {
console.log(obj.obj2.obj3.lol);
console.log(this.lol);
console.log(thos.lol);
}
}
}
};
obj.obj2.obj3.pap();
take a look at the third console.log().
I am defining the variable so why am I this error: Uncaught ReferenceError: thos is not defined
I think thos needs to referenced in the same way as lol. You used this.lol to access lol, so to access thos you have to access this.thos, which probably wouldn't work as this.thos.lol. OOP gets weird sometimes. If you want to use thos instead, define it outside of the object and give that a try...
EDIT: Here's the modified code:
// this is the new line:
var thos = this;
var obj = {
thas: this,
obj2: {
obj3: {
lol: "lol",
pap: function() {
console.log(obj.obj2.obj3.lol);
console.log(this.lol);
console.log(thos.lol);
}
}
}
};
obj.obj2.obj3.pap();
Hope this helps!
As MDN tells us:
A function's this keyword behaves a little differently in JavaScript
compared to other languages. It also has some differences between
strict mode and non-strict mode.
In most cases, the value of this is determined by how a function is
called. It can't be set by assignment during execution, and it may be
different each time the function is called. ES5 introduced the bind
method to set the value of a function's this regardless of how it's
called, and ECMAScript 2015 introduced arrow functions whose this is
lexically scoped (it is set to the this value of the enclosing
execution context).
When a function is called as a method of an object, its this is set to the object the method is called on. So, this.lol works fine and equals to just calling lol, since they're both in the scope.
But what happens when thos.lol is called? Since this is set an object property value, the virtual machine thinks this is in global scope:
In the global execution context (outside of any function), this refers to the global object, whether in strict mode or not.
So, you call thos.lol, it sees the global object and gives you undefined error. Try to obj.obj2.obj3.thos.lol; in console and you'll see undefined. Try to obj.obj2.obj3.thos; in console and you'll see the global object.
this refers to the parent object when referred to in an object's property. Inside of the pap() method you can declare:
var thos = this;
Then you can do:
console.log(thos.lol);
you're trying to access thos when it's not in scope. pap: function() { } creates a new function scope that doesn't know anything about thos, so you need to either pass it in or do console.log(obj.obj2.obj3.thos.lol); instead of just console.log(thos.lol);
I'm reading Nicholas Z.Zakas' Professional JavaScript for web developer 3rd edition and inside the Reference types chapter I got a bit confused at the function type part.
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments);
}
alert(callSum1(10,10)); //20
He says:
"In this example, callSum1() executes the sum() method, passing in this as the this value (which is equal to window because it's being called in the global scope) and also passing in the arguments object."
I understand what he says, but I don't understand the why. This supposed to point to the function doesn't it? Now I'm confused that it's pointing to the window because it's being called in the global scope ..
Can someone be kind to explain it to me? :)
I'm not sure if I will or I have to use this technique later, but I want to make sure I understand it, especially the this part. More details about this and its usage will come in the OOP section, but it's now an interesting question.
Thanks in advance!
The value of this depends on how you call a function.
If you call a function using call or apply then the value of this will be the first argument to that function.
Here you have sum.apply(this, arguments); so it will pass the current value through. As usual, that value is determined by how you call the function.
Since you call callSum1(10,10) with no context, this will be window.
(If you were in strict mode, it would be undefined. If you weren't in a browser, it would be whatever the default object of the JS environment you were using was).
Here:
alert(callSum1(10,10)); //20
...you're calling callSum without doing anything to set this during the call, and so this is defaulted to the global object (window, on browsers). (In loose mode; in strict mode, this would have been undefined.)
Since you're then using apply and passing that same this into it:
return sum.apply(this, arguments);
...naturally the global object is also used when calling sum.
I understand what he says, but I don't understand the why. This supposed to point to the function doesn't it?
No, this almost never refers to functions. (It can, of course, it just very rarely does.)
Now I'm confused that it's pointing to the window because it's being called in the global scope ..
It's not because it's being called in the global scope; it would be the same if it were being called from inside a function. What matters isn't where the call occurs, but how. Any time you call a function without doing anything to set this, in loose mode, this during the function call will be the global object. It doesn't have to be at global scope; what matters is how you call the function.
The rule is actually a lot simpler than people make it out to be: If you call a function without doing anything to set this, during the call, this will be the global object (in loose mode) or undefined (in strict mode). Here are the things you can do to set this during a function call:
Call the function as part of an expression getting the function from a property on an object. For example, foo.bar(); retrieves a reference to the function from the bar property on foo, then calls bar. Because we did it that way, during the call to bar, this refers to foo.
Call the function using .call or .apply; this will be what you pass in the first argument. (In loose mode, if you pass undefined, null, or a non-object in the first argument, during the call this will be the global object. In strict mode, it'll be what you pass.)
Call a bound function. That's a function created via Function#bind (an ES5 feature). Bound functions have this "baked into" them.
More (on my blog):
Mythical methods
You must remember this
Under the Window object part ~20 pages later, I'm reading this: "As mentioned previously, the this value is equivalent to the Global object when a function is executed with no explicit this value specified (either by being an object method or via call()/apply())." :)
Maybe I didn't get it before, but now it's clear with his words as well. By the way, the book is awesome! Now with Math object I can understand much more the apply() method, too.
var values = [1,2,3,4,5,6];
var max = Math.max.apply(Math, values);
John Resig wrote:
Finally, a long-standing (and very annoying) bug has been resolved: Cases where null or undefined is coerced into becoming the global object. Strict mode now prevents this from happening and throws an exception instead.
(function(){ ... }).call( null ); // Exception
what bug is he referring to?
Basically, you are using the call() method from Function.prototype, which by default takes scope as the first parameter. If execution scope is undefined or null, it defaults to the global object. In some situations, using the call method with an Immediately Invoked Function Expression(the above code is rather uncommon) doesn't use the global object as the default fallback execution scope.
If this is evaluated within strict mode code, then the this value is not coerced to an object. A this value of null or undefined is not converted to the global object and primitive values are not converted to wrapper objects. The this value passed via a function call (including calls made using Function.prototype.apply and Function.prototype.call) do not coerce the passed this value to an Object (10.4.3, 11.1.1, 15.3.4.3, 15.3.4.4 clauses of the ECMA language specification).
Each function object should have two "hidden" properties ( per JavaScript The Good Parts, The Functions Chapter )
context
and
code
Is there a way to access these properties?
Well, you can access the function code pretty easily - by using toString() (or Mozilla's non-standard toSource()):
var x = function() { alert('Here is my happy function'); };
console.log(x.toString());
As for context, I suppose DC meant more than simple this, and actually wrote about Execution Context.
UPDATE: Have found an interesting snippet in ES5 specification, where these two properties are actually described in some details - and not as abstract concepts:
13.2 Creating Function Objects
Given an optional parameter list specified by FormalParameterList, a
body specified by FunctionBody, a Lexical Environment specified by
Scope, and a Boolean flag Strict, a Function object is constructed as
follows:
...
Set the [[Scope]] internal property of F to the value of Scope.
...
Set the [[Code]] internal property of F to FunctionBody.
At the same time:
Lexical Environments and Environment Record values are purely
specification mechanisms and need not correspond to any specific
artefact of an ECMAScript implementation. It is impossible for an
ECMAScript program to directly access or manipulate such values.
So I guess that closes the question about accessing Scope property of function.
As for Code property, its read-only accessing with toString(), as was rightly noticed by Matt, is implementation-dependent - yet is more often implemented than not. )
I am reading the book "Secrets of the JavaScript Ninja" by John Resig and in it he is explaining how one can try to anticipate future functionality of the language by extending objects' prototypes with this code:
if(!Array.prototype.forEach){
Array.prototype.forEach = function(fn, callback){
for(var i = 0; i < this.length; i++){
fn.call(callback || null, this[i], i, this);
}
};
}
Now, I understand that the "callback || null" statement prevents from passing a possible undefined value to the "call" function. What I do not understand is what could be the consequences of passing null as the context of the "fn" parameter. Wouldn't that make the code crash?
Thank you for any explanation and/or enlightenment you can provide.
There is no "real" problem with this, the context "this" is just equal to null then.
Thats a common way when using .call() or .apply() if you do not have an appropriate value for the context.
Function.call's first parameter is the scope to run the function in. If that parameter is falsey (null is one form of falsey) then it will run that function in the global scope. In browsers that means the function will run with this set to window.
The callback || null isn't needed, if you pass undefined in as the scope, it will use window. Although possibly there is some obscure browser or situation where it doesn't, which would explain why Resig did that.
No, it won't cause a crash. According to https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/call:
[…] if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
(In the case of a Web-page, "the global object" is window.)