return this || (0,eval)('this'); - javascript

Looking at the doT.js source:
https://raw.github.com/olado/doT/master/doT.js
What does this do?
(function(){ return this || (0,eval)('this'); }()).doT = doT;
To me it looks like it's creating a global var, window.doT. If that's all it's doing, then why not:
window.doT = doT;
?

It's getting a reference to the global object, in order to assign doT to it. This is generally done because with a JavaScript library/framework/etc, its one global identifier needs to be exposed to the outside world.
As for why it's not simply window.doT = doT;, it's because the global object isn't always window, for example, in a non-browser environment. It's also possible to have window assigned to somewhere else at the point this code is executed.
How it works
If this is already truthy, for example, an object such as window, it will return that. It's likely it will be window (at least in the browser), as a plain function call should have its ThisBinding set to the global object. Otherwise, it will execute eval() in the global scope because an indirect call to eval() will set its scope to global, as opposed to the calling environment's scope.
To achieve an indirect call, you have to invoke eval() indirectly, i.e. you can't just call it with eval(). You can use (0, eval) to invoke it. This relies on the comma operator returning the last evaluated expression, in this case eval. It doesn't matter what the preceding operands are. Similarly, (0||eval)() would work.
As for why the body is this, that is the argument to eval(), that is the code to be executed as a string. It will return the this in the global scope, which is always the global object.
It's not really relevant nowadays, but in older IEs, you'd need to use execScript() to execute code in the global scope. I can't remember exactly what versions of IE this was necessary for.

Only adding a bit to the accepted answer as the last question in the condensed comments view "wouldn't (eval)('this') work as well?" has its answer lost in the comment chatter. Simple test:
> foo = 123
< 123
> (function() { const foo = 234; console.log(
foo, // baseline
eval('foo'), // direct eval using local scope
(eval)('foo'), // (eval) still direct
(1, eval)('foo'), // indirect by comma-operator
(false||eval)('foo')); // indirect by or-operator
})()
< 234 234 234 123 123
It shows that
(eval) is not the same as (0, eval) (or, here, (1,eval)) because the comma operator is indirect whereas the parenthesis is not indirect, just a syntactic grouping, the mere parenthesis does not pass its argument through an operation, so it remains direct.
the (false||eval) approach relies on the argument before being falsy, therefore the comma operator is better, it matters less what that first value is.
To me the comma-operator was the biggest aha here, that's why I was dwelling on it a bit.
I have a concern also, i.e., that this trick would by accident be used from a function that might be added to a prototype as a method. In that case the first this is no longer the global object. Which is why, to be really safe, in my opinion one should just stick with the (0,eval)('this') expression only.

Related

using bind method with () function javascript

I have one question, when I use bind with arrow function doesn't work so if there 's anyone can explain it to me.
let username={
email:"test#test",
login:()=>{console.log(this.email);}
};`
when i tap username.login()return undefined also if u tap username.login.bind(username) if there's anyone who can help I will be very thankful.
I m all ears for more info.
Have a read of this from MDN to understand how arrow functions deal with this differently from other functions. This, indeed, was the main reason arrow functions were introduced - they are not primarily about a "nicer" syntax, even though this seems to be how many developers today use them. (And there is nothing wrong with using them that way, but you do absolutely have to be aware of how these functions behave differently.)
As the MDN link explains, the fact that arrow functions "inherit" the this of their enclosing scope is a convenience in many common situations. But, as you have discovered, it also causes problems in cases where you rely on the value of this to be a particular object. Here is a simple demonstration of how the difference arises in your case:
let username1={
email:"test#test",
login:()=>{console.log(this.email);}
};
let username2={
email:"test#test",
login:function(){console.log(this.email);}
};
username1.login(); // logs "undefined"
username2.login(); // works as expected
The reason is because in username2, with the "ordinary" function (not an arrow function) follows the normal rules for what this means inside a function. These are too difficult to go into here (although less difficult than are often thought, and I would well advise understanding what they are) - suffice to say though that the value of this inside a function depends on how the function was called, and that usually, when you call a function as a direct property of an object, as you do with username2.login() here, this will be that object (namely username2).
But with username1.login(), the function is an arrow function, which as the MDN article explains, does not follow these standard rules. Instead the this is adopted from the enclosing scope. Here that's the global scope, not a function scope, so there isn't actually a "real" this to use - and that results in the global (window) object being chosen instead. And since window.email doesn't exist, you get undefined. As proof that this is indeed the global object, recall that window.email is one and the same as a global variable called email. So if we amend the snippet to define a global variable of that name, its value is printed instead:
var email = "this is the global email var";
let username1={
email:"test#test",
login:()=>{console.log(this.email);}
};
let username2={
email:"test#test",
login:function(){console.log(this.email);}
};
username1.login(); // logs global email variable
username2.login(); // works as expected
So that's what's happening here. (Also note that, in real code, the enclosing scope will most often not be global. But it's still quite likely that the this value of the function concerned will be the global object, and even if it isn't, you'll still see undefined printed here unless that particular object happens to have an email property defined.)
The moral of the story is not to use arrow functions for object methods - because any reference to this inside them will not behave as you likely expect. In fact my personal reference is not to use arrow functions at all unless you either need their specific modification of the this rules, or if you need a "throwaway" one-line anonymous function (for example as a function argument to an array's map or filter), in which case the arrow function can make the code more readable. But increased readability and brevity is not the main reason arrow functions exist.
Arrow function is always called with the context in which it was defined. Just use a normal function.
let username={
email:"test#test",
login: function() {console.log(this.email);}
};`
Take a look at ECMAScript 2015 Spec:
Any reference to arguments, super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.

What does JavaScript's strict mode exactly do regarding implicit global declarations?

From MDN article about strict mode:
First, strict mode makes it impossible to accidentally create global
variables. In normal JavaScript mistyping a variable in an assignment
creates a new property on the global object and continues to "work"
(although future failure is possible: likely, in modern JavaScript).
Assignments, which would accidentally create global variables, instead
throw an error in strict mode:
'use strict';
// Assuming a global variable mistypedVariable exists
mistypeVariable = 17; // this line throws a ReferenceError due to the
// misspelling of variable
What does this mean? Does the engine detect if a variable with a similar name already exists(?!) or does strict mode merely prohibit declaring global variables outside of global scope?
The above quotation seems to hint the first possibility but this seems... weird?
The comment in the quoted code is misleading: The "Assuming" part is irrelevant. It should really read: "Assuming that no global variable with exactly this name was defined with let or var,..."
But do note what the purpose was of the code snippet and the comment: to show that in strict mode it is much easier to spot spelling mistakes. In sloppy mode the spelling mistake would go unnoticed (no errors) and you would be working with two variables without knowing it (at least for a while)
As requested, I'll make this into an answer :)
I think you understand what happens here already, and are just getting confused through reading that quote about "assuming a global variable..." too literally. (I will admit it is phrased in such a way as to possibly lead to this confusion though.) The real situation is very simple, and has nothing to do with variables having "similar names" (which JS has no concept of):
What is being talked about is what happens if you assign a value to a variable which has not been formally declared (variables are declared with one of the keywords var, let or const). Not declaring your variables is bad practice, and in strict mode will throw an error - this is a good thing and warns you of your mistake. But in non-strict mode, JS will happily accept this and think you wanted to declare a global variable of that name. This is almost never what you actually want, as it pollutes the global namespace, doesn't inform you of your error, and may lead to all sorts of tricky bugs later.
does strict mode merely prohibit declaring global variables outside of global scope?
strict mode definitely doesn't prohibit declaring global variables from anywhere. In non-strict mode, if you write:
someVar = 't';
it will evaluate to:
window.someVar = 't';
( why is this happening? ) despite of writing inside or outside of a function scope. actually, the line was both declaring and evaluation of variable ( look again, it doesn't have var so it shouldn't declare anything! ).
But it would cause such a side-effect that wasn't totally fine and they've introduced strict-mode which when is active, our first line will throw an error. because it's just evaluation without declaring it first.
now if you need to manipulate global scope inside of a function scope, you only should global object as reference:
var someGlobalVar;
var someOtherGlobalVar;
function hello() {
// this will make *someGlobalVar* to be redefined in the inner scope
// someGlobalVar != window.someGlobalVar
var someGlobalVar;
// manipulating inner variable, not global one
someGlobalVar = 's';
// it's fine, you've accessed global object correctly
window.someGlobalVar = 's';
// you're still editing global object, because
// there's no other variable inside of this scope having the same name
someOtherGlobalVar = 's';
}

How does JavaScript determine when to give a function call a "this" context? [duplicate]

This question already has answers here:
Cases where 'this' is the global Object in Javascript
(4 answers)
Closed 9 years ago.
For obvious reasons, in JavaScript, the following two calls are different:
foo.bar();
var bar = foo.bar;
bar();
Namely, in the first call, this is the foo object. In the second, it's a reference to the global scope. However, the following example is a little less intuitive:
(foo.bar)();
I would expect it to operate the same way as the second example, but it actually operates the same as the first. That is, this references foo, not the global scope.
What are JavaScript's rules for deciding when to make a function call a "method call" and when to simply call the function without a particular this?
EDIT:
As Felix Kling points out in a comment, I'm wondering why the third example doesn't use the window context when theoretically it should simply retrieve the function and call it without the additional context. His example clarifies my question a little:
(true && foo.bar)(); // 'this' refers to the global scope
That's a tricky one and boils down to the inner workings of the ECMAScript standard. The definition of the grouping operator is:
The production PrimaryExpression : ( Expression ) is evaluated as follows:
Return the result of evaluating Expression. This may be of type Reference.
With the added note:
This algorithm does not apply GetValue to the result of evaluating Expression. The principal motivation for this is so that operators such as delete and typeof may be applied to parenthesised expressions.
So this is the key: The result can be of type Reference. A Reference is an internal data type which consists of a base value and a referenced name.
For example evaluating the member expression foo.bar, results in a Reference with base value foo (the object) and the referenced name "bar" (simply a string representation of the identifier).
GetValue(ref) is the internal function which actually accesses the property of the object and returns the value of the property (the function object in this examples). Most operators call GetValue on their operands do resolve these References, but not the grouping operator.
Looking at how CallExpressions are evaluated might also give an idea of how this and References work. For example, one step is:
Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).
So, if you have a Reference value and try to call it, the value of this will be set to the base value of the Reference (foo in the above example).
Regarding my example (true && foo.bar)();: The && operator calls GetValue() on both of its operands, so the result of the grouping operator is not a Reference.
Anything of the form foo.bar() is calling the bar method of the foo object.
However in the case of var bar = foo.bar; bar();, you are creating a reference to the method and then calling the method with no context - hence it is given the default global context.
You can (except in old IE) do this: var bar = foo.bar.bind(foo); bar(); to have the function use the foo context.

Global Eval with return value ?

After searching a while , I was hoping to find a solution for global eval with a return value.
eval will run in the current scope
Function constructor will execute in its own local scope with access to global scope
setTimeout trick is an async operation
script injection can not return value
window.execScript also - can not return value
So my question is:
Is there any other technique which runs at the global scope and can return value ?
(example will be much appreciated).
You can make eval to run in global scope, instead of
eval(s)
just use
window.eval(s);
or
var e=eval; e(s);
or
[eval][0](s)
This surprisingly happens because Javascript is weird and has a special rule about eval: when you're using the original eval object directly to evaluate a string the evaluation happens in the current context.
If an "indirect eval" is used instead (i.e. you store eval in a variable and then use the variable, or even if you access eval using the window object) the evaluation happens in the global context.
You can check this in a Javascript console:
function foo() {
eval("function square(x){ return x*x; }");
}
function bar() {
window.eval("function square(x){ return x*x; }");
}
foo()
square(12) // <-- this gives an error; direct evaluation was used
bar()
square(12) // <-- this returns 144
So window.eval(s) is not the same as eval(s) even if window.eval === eval.
PS
Note that eval has a special specific language rule for this to happen, but the same apparently strange behavior can also be observed in other cases for a different reason.
If you have an object x with a method defined m then
x.m()
is not the same as
var mm = x.m; mm();
because this in the first case will be bound to x during execution of the code in m while in the second case this will be the global object.
So also in this case x.m() is different from mm() even if x.m === mm.
For the same reason x.m() is not the same as [x.m][0]() because in the latter this will be bound to an array object during the execution of method code instead of being bound to x.

(1, eval)('this') vs eval('this') in JavaScript?

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.

Categories

Resources