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
Loop Through an Array in JavaScript
Window.Onload VS $(Document).Ready()
Convert Date to Another Timezone in JavaScript
Convert a JavaScript String in Dot Notation into an Object Reference
Escape String For Use in JavaScript Regex
Why Does Parseint Yield Nan With Array#Map
How to Work Around JavaScript'S Parseint Octal Behavior
How to Export JavaScript Array Info to CSV (On Client Side)
What's the Difference Between "Array()" and "[]" While Declaring a JavaScript Array
Remove Duplicate Values from Js Array
How to Make an Ajax Call Without Jquery
Is There an "Exists" Function For Jquery
Difference Between a Function Call and Function Reference
Cloud Functions For Firebase Trigger on Time
How to Insert an Item into an Array At a Specific Index (JavaScript)