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!
Related
I wrote a code which I can't understand why it prints undefined.
I was expecting to see "oops, global" because the call to foo is not from the object so it must be from the global scope which means that the this refers to the global this.
Where am I wrong?
var obj1 = {
a123: 2,
foo: function() {
// console.log(this); obj1 is not in this. why?
console.log(this.a123);
}
};
var a123 = "oops, global";
var f = obj1.foo;
f(); // prints undefined. why? should have printed "oops, global"
I'm running the code in IntelliJ using nodejs.
Because in node js this means module.export if you are in outer scope. In browser this means window. This is the difference.
See this
Meaning of "this" in node.js modules and functions
This can be a little confusing in Node because you are dealing with two different scopes from the beginning.
Node has a global object. Variables assigned without var, let, const will be assigned to this object. When you start a process, however, there is also a module-level scope. Variables assigned with var, let, const live in this scope. This is an important feature that keeps variables from different modules from clashing.
In you example, when you run f(), this points to global. But your variable a123 is not defined on global. You can assign it explicitly and you will see the behavior your expect with:
global.a123 = "oops, global"
This is the same as assigning without var, etc.
Outside of a function this refers to module.exports. For example:
module.exports.foo = 'bar'
console.log(this) // >> { foo: 'bar' }
Else, if you want to access the obj1 from this, you have to remove the local declaration.
for example, instead of
var obj1 = {};
you could use
obj1 = {} //it will add obj1 to this scope (window scope)
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.
I know this is kind of a silly example-- but why does this code result in an error that bar is not defined. Wouldn't javascript look to see if bar was a property of "this" object. I know that adding this, fixes the problem-- but this tends to throw me off. In other programming languages (C#) for instance, this is usually redundant-- why does it need to be added in the code below?
var myObject = {
foo : function(){
alert(bar);
},
bar : "hello world"
};
myObject.foo();
http://jsfiddle.net/Mv86n/
Wouldn't javascript look to see if bar was a property of "this" object
No. It looks at what variables are in scope, not what properties are members of the current context.
Because, as you have demonstrated, JavaScript does not start variable lookups with this.
http://jibbering.com/faq/notes/closures/#clIdRes
http://oreilly.com/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html
What is the scope of variables in JavaScript?
Javascript has function scope, not object scope.
Only identifiers declared within the same function is in the local scope, everything else is accessed from the global scope.
It just doesn't work that way. It could have, but the language designers probably thought that that way would lead to inefficiencies, ambiguities and code that it hard to see what is going on.
I'll admit that I am also sometimes annoyed by the necessity of prepending "this." onto things, and even more annoyed that I am constantly having to say "var self = this;", but such is life in Javascript. You just have to accept it for what it is, and move on.
In javascript, the lookup priority is ancestor functions scopes (which can be nested, unlike in C++ and Java) from the nearest of the code beiing executed to the farthest.
On that trivial example, it looks like having an implicit this would make sense, but it would be much more complicated to specify and used with different levels of nested functions and object.
(function() {
var myVar = "dsjsh";
return {
myVar: "var",
myMethod: function() {
/* which */ myVar;
};
}
})()
Obviously, the language made the choice of simplicity (at least for this matter).
JavaScript doesn't exactly have a concept of classes or class scope; myObject should be thought of as a map, moreso than an object. For example, you could do this:
function foo() {
alert(this.bar);
}
var myObject = {
bar: "hello world"
};
myObject.foo = foo;
In this case, why should foo know about myObject when it's defined? When you call myObject.foo(), JavaScript passes in myObject as this (of course, JavaScript's scoping rules regarding this are kind of strange, so this isn't always what you think it is), and you can access myObject through this. If you just called foo(), this would be something different (in a browser, it would probably be window), and even accessing this.bar wouldn't work.
Think of it this way:
var myObject = {
foo : null,
bar : "hello world"
};
myObject.foo = function(){
alert(bar);
}
Now, what is bar supposed to refer to? As you can see more easily now, bar is not in scope.
In C#, methods belong to objects and it is known what attributes they have, which is why such a thing is possible. But in JavaScript, you don't have that knowledge, "methods" don't belong and attributes are dynamic, which makes such scoping rules unfeasible.
I'm writing a Javascript library that I hope to be able to minify with the Closure Compiler's ADVANCED_OPTIMIZATIONS option. The library has maybe two dozen global variables which set lower and upper range limits, string literals, etc.
To make these variable accessible from other source files and to avoid dead code removal, I have to "export" them. See Advanced Compilation and Externs.
Therefore, rather than declare variables with this syntax:
var fooMinValue = 10;
I plan to use this syntax:
window['fooMinValue'] = 10;
I've tested this and it seems to work fine. My questions are, is there any downside to using this syntax and is it supported in all browsers released since IE 6? (Or should I be using a completely different technique altogether?)
Although both are properties of the global object, there is a difference: when you declare the variable with var, its [[Configurable]] internal attribute gets set to false. Therefore, it's not possible to change its attributes with Object.defineProperty (except for [[Value]]). The most notable effect is that such variables cannot be deleted:
var foo = 'bar';
window['bar'] = 'baz';
console.log(foo); // 'bar'
console.log(bar); // 'baz'
delete foo; // doesn't work, you can't delete vars
delete bar; // works, bar is an object property
console.log(foo); // 'bar'
console.log(bar); // ReferenceError
Also, when assigning a variable to a property, you make a COPY of the value instead of referencing the variable. This means external changes to the property don't affect the value of the variable.
(function() {
var foo = 'bar';
window['foo2'] = foo; //export foo
console.log(foo); // will output 'bar'
setTimeout(function() { console.log(foo) }, 1000); //will output 'bar'
})();
window['foo2'] = 'baz';
console.log(window['foo2']); // will output 'baz'
The above code will produce the following output:
'bar'
'baz'
'bar'
It is the same except that if your script is not running on a browser it is very probable that window will be undefined.
You are welcome!
It will work; it's perfectly valid syntax; and it's supported in IE6 and up.
Demo: http://ie6test.it/?url=http://jsbin.com/usafeg/2
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