JavaScript: Do I Need to Put This.Var For Every Variable in an Object

Javascript: Do I need to put this.var for every variable in an object?

JavaScript has no classes class-based object model. It uses the mightier prototypical inheritance, which can mimic classes, but is not suited well for it. Everything is an object, and objects [can] inherit from other objects.

A constructor is just a function that assigns properties to newly created objects. The object (created by a call with the new keyword) can be referenced trough the this keyword (which is local to the function).

A method also is just a function which is called on an object - again with this pointing to the object. At least when that function is invoked as a property of the object, using a member operator (dot, brackets). This causes lots of confusion to newbies, because if you pass around that function (e.g. to an event listener) it is "detached" from the object it was accessed on.

Now where is the inheritance? Instances of a "class" inherit from the same prototype object. Methods are defined as function properties on that object (instead of one function for each instance), the instance on which you call them just inherits that property.

Example:

function Foo() {
this.bar = "foo"; // creating a property on the instance
}
Foo.prototype.foo = 0; // of course you also can define other values to inherit
Foo.prototype.getBar = function() {
// quite useless
return this.bar;
}

var foo = new Foo; // creates an object which inherits from Foo.prototype,
// applies the Foo constructor on it and assigns it to the var
foo.getBar(); // "foo" - the inherited function is applied on the object and
// returns its "bar" property
foo.bar; // "foo" - we could have done this easier.
foo[foo.bar]; // 0 - access the "foo" property, which is inherited
foo.foo = 1; // and now overwrite it by creating an own property of foo
foo[foo.getBar()]; // 1 - gets the overwritten property value. Notice that
(new Foo).foo; // is still 0

So, we did only use properties of that object and are happy with it. But all of them are "public", and can be overwritten/changed/deleted! If that doesn't matter you, you're lucky. You can indicate "privateness" of properties by prefixing their names with underscores, but that's only a hint to other developers and may not be obeyed (especially in error).

So, clever minds have found a solution that uses the constructor function as a closure, allowing the creating of private "attributes". Every execution of a javascript function creates a new variable environment for local variables, which may get garbage collected once the execution has finished. Every function that is declared inside that scope also has access to these variables, and as long as those functions could be called (e.g. by an event listener) the environment must persist. So, by exporting locally defined functions from your constructor you preserve that variable environment with local variables that can only be accessed by these functions.

Let's see it in action:

function Foo() {
var bar = "foo"; // a local variable
this.getBar = function getter() {
return bar; // accesses the local variable
}; // the assignment to a property makes it available to outside
}

var foo = new Foo; // an object with one method, inheriting from a [currently] empty prototype
foo.getBar(); // "foo" - receives us the value of the "bar" variable in the constructor

This getter function, which is defined inside the constructor, is now called a "privileged method" as it has access to the "private" (local) "attributes" (variables). The value of bar will never change. You also could declare a setter function for it, of course, and with that you might add some validation etc.

Notice that the methods on the prototype object do not have access to the local variables of the constructor, yet they might use the privileged methods. Let's add one:

Foo.prototype.getFooBar = function() {
return this.getBar() + "bar"; // access the "getBar" function on "this" instance
}
// the inheritance is dynamic, so we can use it on our existing foo object
foo.getFooBar(); // "foobar" - concatenated the "bar" value with a custom suffix

So, you can combine both approaches. Notice that the privileged methods need more memory, as you create distinct function objects with different scope chains (yet the same code). If you are going to create incredibly huge amounts of instances, you should define methods only on the prototype.

It gets even a little more complicated when you are setting up inheritance from one "class" to another - basically you have to make the child prototype object inherit from the parent one, and apply the parent constructor on child instances to create the "private attributes". Have a look at Correct javascript inheritance, Private variables in inherited prototypes, Define Private field Members and Inheritance in JAVASCRIPT module pattern and How to implement inheritance in JS Revealing prototype pattern?

Is var necessary when declaring Javascript variables?

If you don't declare a variable (explicitly creating it in the current scope) using var, let or const then (in non-strict mode) you create an implicit global.

Globals are a fantastic way to have different functions overwriting each other's variables (i.e. they make code a pain to maintain).

If you use var, the scope of the variable is limited to the current function (and anything inside it — it is possible to nest functions).

(const and let scope constants and variables to the current block instead of the function, this usually makes variables even easier to manage than var does.)

Google Adsense uses globals because it splits scripts into two distinct parts (one local and one remote). A cleaner approach would be to call a function defined in the remote script and pass the parameters as arguments instead of having it pick them up from the global scope.


Modern JS should be written in strict mode which bans implicit globals (preferring to explicitly declare them at the top level instead, thus prevent accidental globals when a variable name is typoed).

Why is the variable declared inside the JS class `undefined`

That's because you haven't actually set weight as a property of bar; it's merely a local variable created upon the constructor call (this differs from some other languages, like Java, for example). To create it as a property, you need to use the keyword this:

function Foo() {
this.weight = 10;
}

That sets weight to be a property of Foo objects, including bar, so you should be able to use console.log(bar.weight) without issue.

function Foo() {

this.weight = 10;

}

var bar = new Foo();

document.body.innerHTML = "<code>bar</code>'s weight property is equal to " + bar.weight + ".";

let variables in classes with this context

This has absolutely nothing to do with the prototype. It's much simpler than you may make it out to be:

"Class methods" are merely functions which act on an object. The way they act on an object is through this. That is all. It's a normal function, which has implicit access to this, which is an object. Since in Javascript the value of this is decided at call time, this is an extremely malleable mechanism:

function foo() {

console.log(this.bar);

}

foo.call({ bar: 'baz' });

let baz = { bar: 'baz' };

baz.foo = foo;

baz.foo();

var' vs 'this' vs constructor-parameter variables

var _data = data; creates a local copy (not reference) of data.
this.data = data actually creates a property of the object itself.

I recommend reading this (no pun intended): http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/

Declaring variables with this or var?

If it is global code (the code is not part of any function), then you are creating a property on the global object with the two snippets, since this in global code points to the global object.

The difference in this case is that when the var statement is used, that property cannot be deleted, for example:

var foo = 'bar';
delete foo; // false
typeof foo; // "string"

this.bar = 'baz';
delete bar; // true
typeof bar; "undefined"

(Note: The above snippet will behave differently in the Firebug console, since it runs code with eval, and the code executed in the Eval Code execution context permits the deletion of identifiers created with var, try it here)

If the code is part of a function you should know that the this keyword has nothing to do with the function scope, is a reserved word that is set implicitly, depending how a function is called, for example:

1 - When a function is called as a method (the function is invoked as member of an object):

obj.method(); // 'this' inside method will refer to obj

2 - A normal function call:

myFunction(); // 'this' inside the function will refer to the Global object
// or
(function () {})();

3 - When the new operator is used:

var obj = new Constructor(); // 'this' will refer to a newly created object.    

And you can even set the this value explicitly, using the call and apply methods, for example:

function test () {
alert(this);
}
test.call("hello!"); //alerts hello!

You should know also that JavaScript has function scope only, and variables declared with the var statement will be reachable only within the same function or any inner functions defined below.

Edit: Looking the code you posted to the @David's answer, let me comment:

var test1 = 'test';  // two globals, with the difference I talk
this.test2 = 'test'; // about in the beginning of this answer

//...

function test4(){
var test5 = 'test in function with var'; // <-- test5 is locally scoped!!!
this.test6 = 'test in function with this'; // global property, see below
}

test4(); // <--- test4 will be called with `this` pointing to the global object
// see #2 above, a call to an identifier that is not an property of an
// object causes it

alert(typeof test5); // "undefined" since it's a local variable of `test4`
alert(test6); // "test in function with this"

You can't access the test5 variable outside the function because is locally scoped, and it exists only withing the scope of that function.

Edit: In response to your comment

For declaring variables I encourage you to always use var, it's what is made for.

The concept of the this value, will get useful when you start working with constructor functions, objects and methods.



Related Topics



Leave a reply



Submit