Javascript context confusion - javascript

I have this object:
var test = {
setup: function() { property = 'hello!'; console.log(this); }
};
When I run test.setup(); and print out test.property(); it gives me undefined. I understand it's because I need to do this.property but my question is: why?
I can clearly see that the context is the test object via console.log(this), but for some reason it doesn't work.
Thanks

When JavaScript finds a loose assignment like property = 'hello!', it will create a global variable (or raise an error, if in strict mode). So if you want it to be a property, you have to be explicit.

Because in this situation, property = 'hello!' is the same as window.property = 'hello!', not this.property = 'hello!'.
If you assign to an undeclared variable, it will create a global variable.

When you declare variable with "no var" you will "accidentally" create global variable (attached to window object) so rule of thumb: Always use "var" with variable declaration.
With "var" here, you will create local variable to setup function.

Related

"this" keyword does not refer to global object in my javascript program

I implemented this code by using javascript to check how this works in arrow function.
variable = 'Global variable';
function normalFunc () {
console.log(this.variable);
}
const arrowFunc = () => {
console.log(this.variable);
};
const objNormal = {
variable: 'Inner variable',
func: normalFunc
};
const objArrow = {
variable: 'Inner variable',
func: arrowFunc
};
objNormal.func();
objArrow.func();
arrowFunc();
I learned that this refers to global object in arrow function, so i expected the output like thisπŸ‘‡
Inner variable
Global variable
Global variable
BUT the actual output was this...πŸ‘‡
Inner variable
undefined
undefined
Actually, it's really strange because this depends on where to run this code... in JS Fiddle, this refer to global object as i expected. But in my terminal and this playground site, this does not refer to global object.
Am i misunderstanding about this or just something is missing in my code?
Please help me to understand why this happens.
The website linked transpiles the JS code to be inside a function, so this is not window. If you change your first line to be
this.variable = 'Global variable';
the code works as expected.
When this is pasted into a .js file (filename.js) and ran with node (node filename.js), the this keyword is an empty object while global is a completely different thing. This is different from running this code in the REPL mode, where this === global.
In both cases, when you just say variable = 'Global variable';, you're assigning to window.variable or global.variable, so you can't access it with this.variable.

javascript - access properties of this defined in outer function

I've experimented with closures and found unexpected behaviour. Can somebody explain why this code works this way, please?
function foo() {
this.a='hello';
return {
aaa:function() {
return a; // this suprises me, how can be here accessed 'a' ?
}
}
}
o=foo();
alert(o.aaa()); // prints 'hello' ! , I expected undefined
I don't understand, why I always use var that=this phrase, If it is possible to access function properties from inner functions directly.
jsfiddle https://jsfiddle.net/5co6f707/
It displays 'hello' because you're not in strict mode, so this is the global window object instead of undefined, and a becomes a global variable when you assign a value to this.a. Since a is a global variable it is accessible everywhere. You could just alert(a); at the very end of your script and it would also display 'hello': https://jsfiddle.net/5co6f707/1/.
It shouldn't work (and doesn't in strict mode) and it shouldn't be used. If you intend to use foo as a constructor then you should use the new keyword when you call it (which would break your code, but in a good way).
When this code is executed:
o=foo();
foo is executed in global context and so this line:
this.a='hello';
adds a property to the global object - window.
When you call your aaa function like this:
o.aaa()
the variable a is not defined within the function so it's looked up on up the scope chain and it's found on window:
function() {
return a; // found on window.a
}
and so window.a is returned.

Ways to break JavaScript Scope

JavaScript normally follows the function scope i.e. variables are accessible only within the function in which they are declared.
One of the ways to break this convention and make the variable accessible outside the function scope is to use the global window object
e.g.
window.myVar = 123;
My question is are there any other ways in JavaScript/jQuery to make the variable accessible outside the function scope?
Not with variable declarations, no. You can obviously declare a variable in an outer scope so that it's accessible to all descendant scopes:
var a; // Available globally
function example() {
a = "hello"; // References a in outer scope
}
If you're not in strict mode you can simply remove the var keyword. This is equivalent to your example:
// a has not been declared in an ancestor scope
function example() {
a = "hello"; // a is now a property of the global object
}
But this is very bad practice. It will throw a reference error if the function runs in strict mode:
function example() {
"use strict";
a = "hello"; // ReferenceError: a is not defined
}
As you wrote, variables are only visible inside the function in which they were defined (unless they are global).
What you can do is assign variables to the function object itself:
function foo() {
foo.myVar = 42;
}
console.log(foo.myVar); // outputs "undefined"
foo();
console.log(foo.myVar); // outputs "42"
But I would advise against it. You should really have a very good reason to do something like this.
You can define them as part of a global object, then add variables later
// Define your global object
var myObj = {};
// Add property (variable) to it
myObj.myVar = 'Hello world';
// Add method to it
myObj.myFunctions = function() {
// Do cool stuff
};
See the link below:
Variable declaration
Also, you can declare it without the var keyword. However, this may not be a good practice as it pollutes the global namespace. (Make sure strict mode is not on).
Edit:
I didn't see the other comments before posting. #JamesAllardice answer is also good.

Global variable is not so global in Chrome

I'm learning JavaScript and going through this tutorial on jQuery's website.
In following example
// A function being attached to an object at runtime
var myName = "the global object";
var sayHello = function ()
{
console.log("Hi! My name is " + this.myName);
};
var myObject = {
myName: "Rebecca"
};
var secondObject = {
myName: "Colin"
};
myObject.sayHello = sayHello;
secondObject.sayHello = sayHello;
sayHello(); // "Hi! My name is the global object"
myObject.sayHello(); // "Hi! My name is Rebecca"
secondObject.sayHello(); // "Hi! My name is Colin"
I don't see expected output when sayHello() is invoked. Instead variable is undefined. But if I define global variable by assigning it to window.myName it works.
I'm using Chrome Version 25.0.1364.152 m.
Is tutorial incorrect or am I missing something ?
Full HTML is here: http://pastebin.com/4M27vDB4
UPDATE: Accepted answer explains what happened. I also want to mention possible solution - declaring global variable above without var. Because of following:
Furthermore, variables that are declared inside a function without the
var keyword are not local to the function β€” JavaScript will traverse
the scope chain all the way up to the window scope to find where the
variable was previously defined. If the variable wasn't previously
defined, it will be defined in the global scope, which can have
unexpected consequences.
You have put this code inside
$(document).ready(function ()
// ...
});
closure. In this case context inside it will not be window (it will be document object) and you will get undefined as you describe.
in your program you have used this.myName. this keyword is used to point current object. when you call only sayHello() then in that case "this" means "window" because default current object is window. Now you have not defined window.myName then it will give "undefined".

Declare variable without var keyword and logical OR

I get a strange behavior when declaring an object with the logical OR.
my_var = my_var || {}; // throws TypeError
If I add the var keyword
var my_var = my_var || {}; // returns empty object
Why is this? I can't seem to find an explanation. my_var is global scope, so why is var changing the behavior?
The first example tries to assign to a property on the global object named my_var by reading the value from an identifier called my_var (OR an empty object). However, the identifier my_var is not defined at that point, so it fails.
In the second example, due to how javascript variable hoisting works, the my_var variable is already declared, when you read from it by assign to it.
Also have a look at this example:
a = a; // fails, undeclared identifier
a = 0;
With var keyword it will work!
b = b; // succeeds allthough identifier undeclared?!
var b = 0;
This is because variable hoisting will turn it into this:
var b; // declaration of b hoisted to the top of scope
b = b;
b = 0;
when defining a variable without var, you're directly accessing the global object. That means, your Javascript engine trys to lookup my_var on the global object (window if you're in a browser). Since that property does not exist yet, your JS engine will throw.
That happens on the right side of your statement, when your engine trys to read a variable with the name my_var. assigning like
my_var = {};
would work tho. But accessing an identifier without var will cause the browser to lookup the scopechain. Since the variable object for the global object is the global object itself, the lookup procedure will end up nowhere ( = exception ).
By putting the var keyword infront, your js engine knows at parsetime that it has to declare a new variable with that identifier name. It actually does declare that variable with an undefined value (that is called "hoisting"). That means, using var will prematurely create a property in the current context with that name. So if that code is not located in any function or eval context, it'll create the property my_var on the window object.
In your second example, the var defines the scope of my_var. This allows the value to be set properly. Here's a quick and dirty look at what's happening in both examples:
With example 1, the JS engine has to do the following:
Check the current scope to see if my_var exists.
Check the scope above (and the scope above that) until it finds my_var.
Create my_var in the global scope when it's not found.*
*Step 3 has not yet happened when you try to assign a value to my_var.
Example 2:
Is there a variable named my_var in the current scope? Yes, you just created it using the var keyword.
Assign its value.
In the 2nd situation, my var is undefined so the variable is assigned an empty object which is {}
Although I cannot quote the specs, I assume that the initialization in a var statement is just syntactic sugar:
var my_var = my_var || {};
is equivalent to:
var my_var;
my_var = my_var || {};
In the first line, my_var is declared and implicitely set to the special value undefined. Then, in the second line, my_var exists, undefined evaluates to false and no error is raised.
In your first example, however, my_var is unknown when the right hand side is evaluated, hence the error.
In the first case you try to assign a variable that was not defined. Its the same as you would write :
my_var = a || {};
in the second case my_var is only undefined as you use the var keyword an create my_var in the actual scope. Undefined is converted to false and it will return the empty object.
Interestingly, this will work:
a = my_var || {}; // 'a' is now an empty object
When you use the 'var' keyword you're literally giving the command "create a new variable and assign to it this value." If you don't use 'var' then you're implying that the variable already exists within the global scope somewhere.
In your example the JavaScript engine attempts to look up the current value of 'my_var', finds that it doesn't exist, cannot create it because you're currently attempting to assign a value to it, and fails.

Categories