var a = 1;
var b = {
a : 2,
c : function () {
console.log(this.a);
}
};
b.c(); // logs 2
(b.c)(); // logs 2
(0, b.c)(); // logs 1
The first is understandable, for "this" is pointed to Object "b". But why does the second one log the same result? I thought "this" should be pointed to the global execution context. And the third one, it seems that the comma operator influences the execution context.
You really have a nice corner case there!
My take on it:
the first is straightforward. Just a standard call. The '.' operator lets you call the function setting b as the execution context.
the second is exactly the same thing: the parens are entirely optional and the interpreter is treating the expression inside it as a bound function call. Actually I didn't expect this: I thought the interpreter would be going to reset this to the global object, but actually it's keeping it linked. Probably just so "casual" language users do not freak out.
the third one is more standard (at least for those who live in JavaScript land): as soon as your function is passed on in an expression (in this case by the , operator) the this value is lost. This is because b.c is a Property Reference (deep rabbit hole details in the specification, here, courtesy of T.J.Crowder). So, you are actually passing around the function itself, no more bound to the declaring object. So when you call it this is going to be passed in as the global object.
See it this way: (object.function)() gets simplyfied into object.function(), because the enclosing parens are completely optional; (0, object.function)() is parsed as (expression yielding a function)() which is going to lose the object binding to this, because function is already unbound.
Really nice example!
Refer to Indirect eval call, which gives more details about it.
( 0 , b.c ) ( )
|____| |_____| |_____|
Literal Operator Identifier
|_________________________|
Expression
|______________________________|
PrimaryExpression
|______________________________| |________|
MemberExpression Arguments
|________________________________________________|
CallExpression
We can use the comma operator to fashion an indirect call to b.c which will force it to execute in the global context, the value of a is 1 in the global context.
Also the result of (b.c = b.c)() is 1
> (b.c = b.c)()
1
Speaking in terms of ECMAScript, this is because both — comma operator (in (0, b.c) example) and = operator (in (b.c = b.c) example) perform GetValue on its operands.
Other indirect call formats as below
> (b.c, b.c)()
1
> (1? b.c: 0)()
1
> (__ = b.c)()
1
Related
I start to read JavaScript Patterns, some codes confused me.
var global = (function () {
return this || (1, eval)('this');
}());
Here are my questions:
Q1:
(1, eval) === eval?
Why and how does it work?
Q2: Why not just
var global = (function () {
return this || eval('this');
}());
or
var global = (function () {
return this;
}());
The difference between (1,eval) and plain old eval is that the former is a value and the latter is an lvalue. It would be more obvious if it were some other identifier:
var x;
x = 1;
(1, x) = 1; // syntax error, of course!
That is (1,eval) is an expression that yields eval (just as say, (true && eval) or (0 ? 0 : eval) would), but it's not a reference to eval.
Why do you care?
Well, the Ecma spec considers a reference to eval to be a "direct eval call", but an expression that merely yields eval to be an indirect one -- and indirect eval calls are guaranteed to execute in global scope.
Things I still don't know:
Under what circumstance does a direct eval call not execute in global scope?
Under what circumstance can the this of a function at global scope not yield the global object?
Some more information can be gleaned here.
EDIT
Apparently, the answer to my first question is, "almost always". A direct eval executes from the current scope. Consider the following code:
var x = 'outer';
(function() {
var x = 'inner';
eval('console.log("direct call: " + x)');
(1,eval)('console.log("indirect call: " + x)');
})();
Not surprisingly (heh-heh), this prints out:
direct call: inner
indirect call: outer
EDIT
After more experimentation, I'm going to provisionally say that this cannot be set to null or undefined. It can be set to other falsy values (0, '', NaN, false), but only very deliberately.
I'm going to say your source is suffering from a mild and reversible cranio-rectal inversion and might want to consider spending a week programming in Haskell.
The fragment
var global = (function () {
return this || (1, eval)('this');
}());
will correctly evaluate to the global object even in strict mode. In non-strict mode the value of this is the global object but in strict mode it is undefined. The expression (1, eval)('this') will always be the global object.
The reason for this involves the rules around indirect versus direct eval. Direct calls to eval has the scope of the caller and the string this would evaluate to the value of this in the closure. Indirect evals evaluate in global scope as if they were executed inside a function in the global scope.
Since that function is not itself a strict-mode function the global object is passed in as this and then the expression 'this' evaluates to the global object. The expression (1, eval) is just a fancy way to force the eval to be indirect and return the global object.
A1: (1, eval)('this') is not the same as eval('this') because of the special rules around indirect versus direct calls to eval.
A2: The original works in strict mode, the modified versions do not.
To Q1:
I think this is a good example of comma operator in JS. I like the explanation for comma operator in this article: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/
The comma operator evaluates both of its operands (from left to right) and returns the value of the second operand.
To Q2:
(1, eval)('this') is considered as indirect eval call, which in ES5 does execute code globally. So the result will be the global the context.
See http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope
Q1: Multiple consecutive javascript statements separated by a comma take the value of the last statement. So:
(1, eval) takes the value of the last one which is a function reference to the eval() function. It apparently does it this way to make the eval() call into an indirect eval call that will be evaluated in the global scope in ES5. Details explained here.
Q2: There must be some environment that doesn't define a global this, but does define eval('this'). That's the only reason I can think of for that.
I'm currently studying javascript by following the "you dont know js" series.
In section "this & object prototype", the author came up with an way to soft bind this.
However, I am extremely confused by the code. So I was wondering if someone could kindly explain it to me, steps by steps, what the code really does?
//step 1: if "softBind" property does not exist on `Function.prototye`
if (!Function.prototype.softBind) {
//step 2: create a property named "softBind" on "Function.prototype" and assign to "softBind" the following function
Function.prototype.softBind = function(obj) {
//step 3: what is the point of assigning "this" to the variable "fn"?
//what does "this" represent at this point in time?
var fn = this,
//step 4: I understand that "arguments" is an array-like object, i.e. "arguments" is not a true array.
//But you can convert "arguments" to an true array by using "[].slice.call(arguments)".
//The thing I dont understand here is, why the 1?
//I understand it tells "slice" method to start slicing at index 1, but why 1?
//And what is the purpose of "curried" variable?
curried = [].slice.call( arguments, 1 ),
bound = function bound() {
//step 5: I understand what "apply" function does
return fn.apply(
//step 6: I dont really understand how "!this" works.
(!this ||
//step 7: utterly confused...
(typeof window !== "undefined" &&
this === window) ||
(typeof global !== "undefined" &&
this === global)
//step 8: if the above statements evaluates to "true", then use "obj",
//otherwise, use "this"
) ? obj : this,
//step 9: can't I write "curried.concat(arguments)" instead?
//why the convoluted syntax?
curried.concat.apply( curried, arguments )
);
};
//step 10: Why assign the "fn.prototype" as the prototype to "bound.prototype"?
bound.prototype = Object.create( fn.prototype );
return bound;
};
}
I'm very sorry for the long question, but I thought instead of dividing the question into several posts, it is more convenient if they are put into one place.
step 3: what is the point of assigning "this" to the variable "fn"?
The value of this holds a pointer to the currently executing function object. Only function objects can be held, so only things actually created through new() or equivalent notation. This can be useful for passing a reference to an outer object to an inner object created within said outer object.
Here's a minimal example:
function fn1() {
var x = this; // x = fn1.
this.u = 5;
function fn2() {
this.u = 10;
console.log(this.u); // Prints the member of fn2.
console.log(x.u); // Prints the member of fn1.
};
var W = new fn2();
}
var V = new fn1();
The output should be:
10
5
First, an object of type fn1 is created called V. It has a member variable u holding the value 5. Then, we create an object of type fn2 called W within fn1. It also has a member variable u, but here it holds the value 10. If we wanted to print the value of V.u within W, then we need a pointer to V. Calling this.u within W would output its u value (10), which is not what we want. So we define a variable x within the scope of the class fn1, holding the this pointer for us. Now it's possible to access the members of fn1 within fn2.
Step 4
The first argument is the object being bound to. You don't want to pass that to the function being bound, that would break its functionality, for it does not expect an extra argument prepended to its normal list of arguments. So, the first argument has to be removed.
step 6: I dont really understand how "!this" works.
!this is simply a way of checking whether this is defined. If it is not, then the value will be true. Otherwise, since this
would be an object (which evaluate to true when cast to a boolean), then it is false.
step 7: utterly confused...
Here, the original author checks if this is equal to either window, or global. Note; In modern browsers, checking for just window is enough, but IE exists (as do non-browser javascript environments). So, the full statement evaluates to this thing:
If I am not called from within an object, or if I'm called from the object window or global, then return the object softbind was created with. Otherwise, return the object I was called from
Note that this is exactly what the author of the original article wants. When a library function is called with this special binding, then we can be sure that whatever the library does; it can't access the global context through the use of the this variable. But, it can access any other object, allowing you to interface with the library.
step 9: can't I write "curried.concat(arguments)" instead?
Curried holds all arguments the original softbind function was called with, except for the first argument. arguments , at this point, is not equal to arguments in the earlier call. Here, it refers to the arguments the bound function is called with, not those it was bound with. This line integrates the two sets of arguments, allowing you to provide default arguments. The trick used here is to concatenate the arguments, e.g. Suppose your function has default arguments [1,2,3,4] and you supply [5,6]:
[1,2,3,4].concat([5,6]) produces [1,2,3,4,5,6].
Why not simply concatenate, and use the prototype? Arrays are passed by reference in javascript, so this will keep curried the same, while concatenating arguments to the call. Equivalently, you could write this:
curried2 = curried.concat(arguments);
return fn.apply(
(.....)
curried2);
Admittedly, the terseness does not help the understandability of this example. Simply re-naming arguments to calledArguments and curried (an advanced math term not relevant to the explanation) to be defaultArguments and using a simple for loop over each argument would be far easier to understand, if a little more verbose. I guess the author wanted to be fancy.
step 10: Why assign the "fn.prototype" as the prototype to "bound.prototype"?
Go up a bit on the article to the part where the author talks about the default bind function and how it works: basically, the end result of replacing the prototype back with the default prototype during the function call means that when your softbind enabled function is called with the new operator this will be set to itself, rather than the default bound object. prototype will not work when simply calling the bound function.
It also enables inheritance, meaning that creating things for a softbind enabled function using its prototype will not have that prototype be overruled by that of softbind when it is bound. (That would make softbind incompatible with prototypes). Instead, both prototypes are used.
Also see this reddit post.
A word of warning
We're extending the language with new features here. Features that aren't exactly necessary, and are largely concerned with semantics. If you're just interested in learning the language, this really goes way too far, you don't exactly need special binding semantics. Worse, it can be confusing if this does not behave in a way you expect it to.
A simpler alternative
Enable strict mode. Now this will default to undefined whenever it points to the global object. Prevents the problem this convoluted code is trying to solve (by usually resulting in errors from functions trying to access member variables or functions of undefined), while at the same time being far easier to use, and at the same time it will complain about a lot of syntax that is valid regular javascript, but a bug in any normal use-case. Also see the MDN article about it. It will catch a lot of potential errors for you instead of silently doing nonsensical things.
Another alternative
bind tries to resolve 'losing' the object when you pass a member function of it to another function, such as setTimeout. Another way of doing it is by using an anonymous function. Instead of using (soft) binding, assuming obj is an object holding a function fn being passed a parameter param;
setTimeout(obj.fn(param), 500);
You can use:
setTimeout(function(param){obj.fn(param);}, 500);
Which avoids the problem through a layer of indirection by passing an anonymous function. Also see this question.
I am a newbie to js although years of experience with Java
I suppose when I declare a function,it is essentially a special type of object,and got some builtin fields that are accessible directly such as "arguments" and "length"
I notice I can access something like "arguments" inside the scope of a function
i.e.
function add(a,b) {
return arguments[0]+arguments[1]
}
Also I can access something like "length" outside the scope
//2
alert(add.length)
the above snippet should be the right way to use
however
function sum(a,b) {
// error
return length
}
// null
alert(sum.arguments)
I suppose arguments and length are not of the same basic, is it right idea?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After some research,I got the root cause of the confusion.
The issue revolves around property VS variable in JavaScript
Property belong to Object while Variable belong to Context.
The two ideas can be interchangeable sometimes:
The global context happen to be window
<script>
//property
window.foo="a"
//variable
var bar="b"
//a
alert(foo)
//b
alert(bar)
</script>
In most scenario,say,function context,they are completely different ideas,partly because you can never access the function object in your code.Thus,contrary to a global setting ,assign a property is impossible! What is possible is just declare a variable in scope
In my question
"arguments" is a variable
while "length" is a property
I failed to distinguish the two
For more information,please refer to
this post
Functions are objects in JavaScript. Proper, real objects.
length
The length property of a function is the number of declared arguments it has (this is also called the "arity" of the function). The arity (length) of add is two because you declared two formal arguments for it: a and b.
arguments
The arguments pseudo-array is not part of the function object. It's an object created when the function is called, and only in scope within that function call's context. It contains all of the arguments that the function was actually called with, which can be different from the number of arguments it declares. Each separate call to the function gets its own separate arguments object.
In JavaScript's "loose mode" (the only mode it had before 2009's ECMAScript 5th edition specification), there's a live connection between the arguments pseudo-array and the declared arguments:
// In loose mode only
function foo(a) {
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
a = 42;
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
}
foo(67);
In loose mode, that outputs:
a = 67
arguments[0] = 67
a = 42
arguments[0] = 42
In "strict" mode (which is the preferred mode to use), that link doesn't exist (we'd see arguments[0] = 67 at the end), which is useful for JavaScript engines for optimization purposes.
Yes, length and arguments are different concepts.
Contrary to Java, where fields (attributes) of your object, methods, and local variables are all accessible in the same scope (just via an identifier), JavaScript does distinguish heavily between scope variables (with closures, lexically referenced) and object properties (possibly inherited) - not only in concept, but also in syntax. Properties are always1 accessed via . or [] notation.
The .length of Function instances is such a property. You will always have to write add.length or sum.length (or …["length"]). If you just use length inside of the function, it doesn't automatically refer to the property - it's a variable reference, in this case to an undeclared one.
The arguments object in contrast works like a variable. It cannot be accessed as a property of the function2. Admittedly, it's a bit special, because you cannot see the declaration of the variable - it is created implicitly in every function scope. It works a bit as if there was a const arguments = {0: a, 1: b}; statement in the first line of the function.
1: Not exactly true, there's a with statement that explicitly lifts object properties into a scope. It's despised due to its ambiguity and deprecated in strict mode though.
2: There also is a deprecated .arguments property on functions, accessing it on strict mode functions throws. If not accessed during the invocation, it's null.
I start to read JavaScript Patterns, some codes confused me.
var global = (function () {
return this || (1, eval)('this');
}());
Here are my questions:
Q1:
(1, eval) === eval?
Why and how does it work?
Q2: Why not just
var global = (function () {
return this || eval('this');
}());
or
var global = (function () {
return this;
}());
The difference between (1,eval) and plain old eval is that the former is a value and the latter is an lvalue. It would be more obvious if it were some other identifier:
var x;
x = 1;
(1, x) = 1; // syntax error, of course!
That is (1,eval) is an expression that yields eval (just as say, (true && eval) or (0 ? 0 : eval) would), but it's not a reference to eval.
Why do you care?
Well, the Ecma spec considers a reference to eval to be a "direct eval call", but an expression that merely yields eval to be an indirect one -- and indirect eval calls are guaranteed to execute in global scope.
Things I still don't know:
Under what circumstance does a direct eval call not execute in global scope?
Under what circumstance can the this of a function at global scope not yield the global object?
Some more information can be gleaned here.
EDIT
Apparently, the answer to my first question is, "almost always". A direct eval executes from the current scope. Consider the following code:
var x = 'outer';
(function() {
var x = 'inner';
eval('console.log("direct call: " + x)');
(1,eval)('console.log("indirect call: " + x)');
})();
Not surprisingly (heh-heh), this prints out:
direct call: inner
indirect call: outer
EDIT
After more experimentation, I'm going to provisionally say that this cannot be set to null or undefined. It can be set to other falsy values (0, '', NaN, false), but only very deliberately.
I'm going to say your source is suffering from a mild and reversible cranio-rectal inversion and might want to consider spending a week programming in Haskell.
The fragment
var global = (function () {
return this || (1, eval)('this');
}());
will correctly evaluate to the global object even in strict mode. In non-strict mode the value of this is the global object but in strict mode it is undefined. The expression (1, eval)('this') will always be the global object.
The reason for this involves the rules around indirect versus direct eval. Direct calls to eval has the scope of the caller and the string this would evaluate to the value of this in the closure. Indirect evals evaluate in global scope as if they were executed inside a function in the global scope.
Since that function is not itself a strict-mode function the global object is passed in as this and then the expression 'this' evaluates to the global object. The expression (1, eval) is just a fancy way to force the eval to be indirect and return the global object.
A1: (1, eval)('this') is not the same as eval('this') because of the special rules around indirect versus direct calls to eval.
A2: The original works in strict mode, the modified versions do not.
To Q1:
I think this is a good example of comma operator in JS. I like the explanation for comma operator in this article: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/
The comma operator evaluates both of its operands (from left to right) and returns the value of the second operand.
To Q2:
(1, eval)('this') is considered as indirect eval call, which in ES5 does execute code globally. So the result will be the global the context.
See http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope
Q1: Multiple consecutive javascript statements separated by a comma take the value of the last statement. So:
(1, eval) takes the value of the last one which is a function reference to the eval() function. It apparently does it this way to make the eval() call into an indirect eval call that will be evaluated in the global scope in ES5. Details explained here.
Q2: There must be some environment that doesn't define a global this, but does define eval('this'). That's the only reason I can think of for that.
There are things like
f.call(...)
f.apply(...)
But then there's this
(1, alert)('Zomg what is this????!!!11')
"1" does not seem to mean much in this context, the following works just fine:
(null, alert)('Zomg what is this????!!!11')
(1, null, alert)('Zomg what is this????!!!11')
(undefined, alert)('Zomg what is this????!!!11')
Could you point to a specific part of ECMAScript which describes that syntax?
You are just using The Comma Operator.
This operator only evaluates its operands from left to right, and returns the value from the second one, for example:
(0, 1); // 1
('foo', 'bar'); // 'bar'
In the context of calling a function, the evaluation of the operand will simply get a value, not a reference, this causes that the this value inside the invoked function point to the global object (or it will be undefined in the new ECMAScript 5 Strict Mode).
For example:
var foo = 'global.foo';
var obj = {
foo: 'obj.foo',
method: function () {
return this.foo;
}
};
obj.method(); // "obj.foo"
(1, obj.method)(); // "global.foo"
As you can see, the first call, which is a direct call, the this value inside the method will properly refer to obj (returning "obj.foo"), the second call, the evaluation made by the comma operator will make the this value to point to the global object (yielding "global.foo").
That pattern has been getting quite popular these days, to make indirect calls to eval, this can be useful under ES5 strict mode, to get a reference to the global object, for example, (imagine you are in a non-browser environment, window is not available):
(function () {
"use strict";
var global = (function () { return this || (1,eval)("this"); })();
})();
In the above code, the inner anonymous function will execute within a strict mode code unit, that will result on having the this value as undefined.
The || operator will now take the second operand, the eval call, which is an indirect call, and it will evaluate the code on the global lexical and variable environment.
But personally, in this case, under strict mode I prefer using the Function constructor to get the global object:
(function () {
"use strict";
var global = Function('return this')();
})();
Functions that are created with the Function constructor are strict only if they start with a Use Strict Directive, they don't "inherit" the strictness of the current context as Function Declarations or Function Expressions do.
It's the comma operator, which evaluates both of its operands and returns the value of the second one.
So, something like (null, alert) evaluates to the alert function, which you can immediately call using parentheses.
It's described in section 11.14 of ECMA-262 (PDF).
The comma operator causes expressions to be evaluated sequentially. Wrapping the expressions in parentheses returns the value of the last expression. So (1, alert)("hello") is functionally equivalent to:
1;
alert("hello");
Off the top of my head, I can't think of a reason to do this.