I'm reading "you don't know javascript" and I find some trouble with one example in the book "This & Object Prototypes".
When discussing the different rules for this, specifically in the "Implicit binding" paragraph, the author gives this example:
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // function reference/alias!
var a = "oops, global"; // `a` also property on global object
bar(); // "oops, global"
However when trying this on JSFiddle I get an undefined output in console instead of the "oops, global".
Conversely, if I define a without var or using window.a I get the output intended by the author regardless of strict mode.
Why is this happening? Did something in ES6 change the way global variables should be declared?
The default settings for JS Fiddle wrap the JS in a function and assign it as an load event handler.
Your tests are not in the global scope.
Related
This question already has answers here:
In what scope are module variables stored in node.js?
(4 answers)
Closed 4 years ago.
I'm reading the "You don't know JS" book series and I tried to run the snippet:
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// `fn` is just another reference to `foo`
fn(); // <-- call-site!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // `a` also property on global object
doFoo( obj.foo ); // "oops, global"
(You can find it in the 2nd chapter of the 3d book: 'this' All make sense now)
If I save this into 'foo.js' and run it with node foo.js (v 8.11.1) then I get undefined. While if I start node REPL and type in the same code I get:
> function foo() { console.log(this.a); }
undefined
> function doFoo(fn) { fn(); }
undefined
> var obj = { a:2, foo:foo };
undefined
> var a = "oops, global";
undefined
> doFoo(obj.foo);
oops, global
undefined
As expected from the book.
Same result on Firefox dev console.
If I remove the declaration and leave only the assignment a = "oops, global" then it run as expected both on REPL and Node.js. This make more sense to me because in this way I'm setting a property on the global object, while in the "original" way I'm just declaring a variable.
Can anyone explain to me this behaviour? Thank you all.
EDIT: I think I'm close to the solution, I noticed that if I make a script foo.js that contains only:
var x = 42;
console.log(this);
I get {}, so x is not attached to the global object. While if I start the Node.js REPL and type in the same code I get a big object with x attached to it:
{
...
x: 42
}
So I think the difference it depends on "who is the global object?" in REPL and in Node.js.
When you run the file in Node.js, your var a isn't actually in global scope, but function scope -- a module function scope (more on this here). Basically all of the contents of the file is run as if inside a function. So var a is actually in that function scope.
Whereas in REPL it's truly in global scope.
function foo()
{
var a=5;
}
Since above function is JavaScript object so we can add properties to it like foo.p=6.But while doing console.log(foo) i am not able to see the p property of object but is accessible through foo.p and foo['p'].
Also i am not able to access foo.a if we consider foo as JavaScript object.
Try console.dir(foo); instead, which lists the properties of the object. concole.log simply outputs some representation of the value, the browser thinks would be useful.
foo.a can't work. a is a local variable, not a property, and only exist while foo is executed.
When logging an object, the browser will choose how to render it in the console. This may be as primitive as calling .toString() on it, or as complex as giving you the ability to navigate its properties.
Furthermore, the console is not a standard. Browsers may implement it however they like, although for convenience they will keep to a common style. Ultimately, however, this means they will be different.
Internet Explorer:
Google Chrome:
Notice how I had to use console.dir() to force Chrome to give me a navigable view of the object, rather than its simple .toString() representation.
"a" is a local variable, this not accessible from outside the function.
you'd like something like this:
var myClass = function(){
this.foo = 'bar';
}
var myInstance = myClass();
myInstance.foo // 'bar'
or as a plain object:
var myObj = {
foo: 'bar'
}
myObj.foo // 'bar'
myObj.foo = 123;
myObj.foo // 123
There are few ways of achieving that. One is using objects in Javascript and another one with the called "Module Pattern".
For using objects, you can do:
function MyObject() {
this.foo = "myObj";
}
var mo = new MyObject();
console.log(mo.foo); //Will print "myObj".
Module pattern works as below:
var MP = (function(){}(
var foo = "MP";
return {
foo : foo
}
));
console.log(MP.foo); // Will print "MP"
It's common to see CommonJS modules defined using the following idiom:
(function() {
var logThis = function() { console.log(this); }
module.exports = logThis;
}).call(this);
Underscore.js, for example, does this.
I just spend half an hour discussing with a colleague why they invoke the closure with call(this). This will cause the value of this inside the closure to be inherited from the caller, rather than being set to the global object. However, when I tested this in Node.js, the value of this inside the module was always the global object, even when I loaded and ran it like this:
var bar = {};
bar.foo = function() { var foo = require("./foo"); foo(); }
I was really expecting to see the bar object in the console, but actually I see the global object. It then occurred to me that this might be because modules like Underscore.js are also used in a web context. But in that case it would be loaded with a <script> tag so this will always be equal to the global object anyway.
What gives? I'm sure there is a reason for using this construct but I can't see the practical difference in this particular case whether the module is being used in Node.js or in a webpage.
Update: Just to clarify, I can think of a number of cases where this could make a difference. For example, if I say:
var bar = {}
var foo = require("./foo");
bar.foo = foo;
bar.foo();
(Thanks to #Pointy for correcting my original example.)
I would expect the closure in the module to be evaluated when require() is called, which means that the value of this inside it would be bound to the global object, which would be written to the console even though foo() is then invoked as a member of the "bar" object. However, I am seeing the "bar" object in the console even in this example. I guess that this is not being bound to the closure as I expected?
In a nutshell, I'm looking for one example where a module like Underscore.js will have different behavior due to being wrapped in a closure invoked with fn.call(this) instead of just fn(), either in Node.js or in a web page.
Your call to "foo" inside "bar.foo" is made without any context, so the global context is used. The fact that it's inside a function where this refers to "bar" is not relevant; that's just not how JavaScript works. The only thing that matters is how the function is invoked, not where it's invoked, in other words.
If "bar.foo" looked like this:
bar.foo = function() { require("./foo"); foo.call(this); }
then you'd see "bar" in the console. Or, you could do this:
var bar = {};
require("./foo");
bar.foo = foo;
Then calling bar.foo() would also log the "bar" object. (Does that really work in Node? That is, I thought require() returned an object, and that it didn't just leave things in the global scope. I'm a rank novice at Node however.)
edit — OK thanks for updating. Thus, my example would be corrected as follows. First, I think that your module should look like this:
(function() {
var logThis = function() { console.log(this); }
module.exports.logThis = logThis;
}).call(this);
That is, I think that you want to explort the "logThis" function, so it needs to be bound to the "exports" object as a named property.
Then:
var bar = {};
var foo = require("./foo");
// At this point, foo.logThis is the function
bar.foo = foo.logThis;
// Now the "foo" property of "bar" is a reference to the same function
bar.foo(); // logs the "bar" object
var fee = { fie: foo.logThis };
fee.fie(); // logs the "fee" object
I know using eval is not at all recommended and I have read this link too. Set Variable inside Eval (JavaScript)
However, this is what I want to do. Lets say we have some code in a textbox. So I have to take that text, and then find out all the global variables, functions and objects in that code.
My idea was to wrap the code in a namespace, eval it and then iterate through the properties of the namespace to get the results. However, even though the eval runs successfully, I can't access the variable defined there. Is there a better solution or some other way to get this working.
http://jsfiddle.net/DbrEF/2/ - This is the Fiddle here.
The "var code" could actually be arbitrary code. I know its unsafe to do it but I need it for a different context.
Thanks
In 2015, creating a Function object is your best bet here, rather than using eval:
new Function('arg1', 'arg2', 'return arg1 + arg2')(3,4) // returns 7
You might have better luck using a Javascript parser, like the one used by JSHint/JSLint
here's a demo on safely using eval using "use strict"
window.onload = function(){
'use strict';
//use strict forces to run code in "strict mode"
//use strict prevents eval from
//contaminating the immediate scope
//let's test with "foo"
var foo = 'lol';
//now code has "foo" but using "use strict"
//makes code in eval stay in eval
//note that at the last of the code, foo is "returned"
var code = 'var foo = {name: "Spock",greeting: function() {return "Hello " + foo.name;}}; foo';
//store eval'ed code in evalO
var evalstore = eval(code);
console.log(evalstore); //code in eval stays in here, which is "inner foo"
console.log(foo); //"outer foo" is unharmed and daisy fresh
};
so whatever code you have, contain it in a function which will serve as your namespace. then have that function returned to the outside world stored as a variable. this demo shows how it can be constructed, however, works only if code is in object literal notation.
window.onload = function() {
'use strict';
var ns = 'lol';
//code must be an object literal
var code = '{name: "Spock",greeting: function(){return "Hello " + foo.name;}}';
//store in a constructor to be returned
var constructorString = 'var ns = function(){return ' + code + '}; ns';
var evalNs = eval(constructorString); //type function/constructor
var evalObj = new evalNs() //object instance
console.log(evalNs); //constructor
console.log(evalObj); //the namespaced object
console.log(ns); //outer "ns" preserved
};
Probably not what exactly OP was looking for but another option is to use outside variables to store values generated inside eval, as in:
var value;
var code = 'var foo = 42';
code = code.replace('var foo', 'value');
eval(code);
value // returns 42;
I noticed some weird (and to my knowledge undefined behavior, by the ECMA 3.0 Spec at least), take the following snippet:
var foo = { bar: "1", baz: "2" };
alert(bar);
with(foo) {
alert(bar);
alert(bar);
}
alert(bar);
It crashes in both Firefox and Chrome, because "bar" doesn't exist in the first alert(); statement, this is as expected. But if you add a declaration of bar inside the with()-statement, so it looks like this:
var foo = { bar: "1", baz: "2" };
alert(bar);
with(foo) {
alert(bar);
var bar = "g2";
alert(bar);
}
alert(bar);
It will produce the following:
undefined, 1, g2, undefined
It seems as if you create a variable inside a with()-statement most browsers (tested on Chrome or Firefox) will make that variable exist outside that scope also, it's just set to undefined. Now from my perspective bar should only exist inside the with()-statement, and if you make the example even weirder:
var foo = { bar: "1", baz: "2" };
var zoo;
alert(bar);
with(foo) {
alert(bar);
var bar = "g2";
zoo = function() {
return bar;
}
alert(bar);
}
alert(bar);
alert(zoo());
It will produce this:
undefined, 1, g2, undefined, g2
So the bar inside the with()-statement does not exist outside of it, yet the runtime somehow "automagically" creates a variable named bar that is undefined in its top level scope (global or function) but this variable does not refer to the same one as inside the with()-statement, and that variable will only exist if a with()-statement has a variable named bar that is defined inside it.
Very weird, and inconsistent. Anyone have an explanation for this behavior? There is nothing in the ECMA Spec about this.
This follows from ECMA-262, 3rd edition, §12.2, as
Variables are created when the execution scope is entered. A Block does not define a new execution scope.
and
A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.
This means you have to think of a declaration with initializer as two seperate steps: first, you create a new variable in the function-local (global) scope when entering the function (executing the script), but you don't assign a value until reaching the statement. In your examples, at this point the scope chain has changed due to with(), and the assignment sees the overloaded varriable, leaving the newly created one undefined.
This means
with({ foo : 42 }) {
var foo = 'bar';
}
is parsed as
with({ foo : 42 }) {
var foo;
foo = 'bar';
}
which is equivalent to
var foo;
({ foo : 42 }).foo = 'bar';
and therefore leaving foo uninitialized.
I think you're just misunderstanding the scoping a bit. Not that I blame you, if you're coming from most other languages it makes you go "WTF?". Javascript has what I call "FUBAR Scoping" (I'm sure there's a proper term here but I never bothered to learn it...knowing how it works > knowing what it's called, at least to me).
Let's make the example even simpler:
with({}) {
var foo = "test";
}
alert(foo); //alerts "test"
with() allows block level definition of a variable, for being passed into a scope (good answer demonstrating this here). But...it doesn't restrict the variable to this scope, it is available afterwards, here's a better example:
with({ bar: 1}) {
this.foo = "test";
alert(bar); //alerts: 1"1"
alert(this); //alerts: "DOMwindow"
}
alert(foo);
If with() was a closure to the element you're dealing with, then this would refer to the { bar: 1} object we passed into the with, but it doesn't :). this in this case is still the global context of window. Since you're still executing in this context, any variables you define there will be available in that context, and therefore available later in the function, after the with(), because they're defined much higher in scope than you would think. Intuition tells you it's defined in a more constrained scope: within the with()...but javascript is different, it all depends what context your closure is running in.
I tried to find a good description for this behavior that gives a lot more scoping examples, this is the best I could find for you. Overall this is more of a closure/scoping question than with() explicitly, this site covers the overall concept in more detail in case I did a terrible job of explaining it...I know it's a backwards concept in many respects, sorry!