Why private function can't access member variable within Javascript object? - javascript

I'm having issues understanding scoping/access of fields defined with a JavaScript object.
Consider the following sample JavaScript object definition:
function SampleObject(controlName) {
var _controlName = controlName;
this.Display1 = function() {
console.log(_controlName);
}
this.Display2 = function() {
Display3();
}
Display3 = function () {
console.log(_controlName);
}
}
Now consider running the above object with the following example:
var a = new SampleObject("Bob");
var b = new SampleObject("Fred");
a.Display1(); //==> Displays "Bob"
b.Display1(); //==> Displays "Fred"
a.Display2(); //==> Displays "Fred"
b.Display2(); //==> Displays "Fred"
What I'm having difficulty understanding is how to access object fields (properties) from within private functions defined within my object.
Within my example, I am confused as to why _controlName has the value "Fred" when it is displayed via Display2() for both objects a and b.
I suspect that is either an issue with how _controlName and/or Display3() is defined or I am unclear how scoping would work in this instance. Can someone please share some light on this?
Thanks,JohnB

In some other languages such as Java (where I hail from), when you are inside a method on an object, the this is implicit. In Javascript, this is NOT the case.
When you are assigning to this.Display1 and this.Display2, you are creating properties with those names on the object pointed at by this. Because of new, this is a different object each time.
However, what's happening when you assign to Display3 is that you are assigning to a global variable. If it doesn't exist, it is created for you. When you call new SampleObject("Fred"); the function which originally logged "Bob" is now gone, replaced by one which prints "Fred".
If you add 'use strict'; to the top of your SampleObject function, which suppresses this "implicit global" behavior, you get a reference error when you try to assign to the never-declared variable Display3.

Here's the explanation.
In the first call, new SampleObject("Bob"); the global variable Display3 gets set to a function that console-logs the value Bob.
In the second call, new SampleObject("Fred"); the global variable Display3 gets set to a function that console-logs the value Fred.
Now your Display1 is actually a method. Every object you create gets its own Display1 property. So Bob's Display1 logs Bob and Fred's logs Fred.
But because the Display2 methods each call the global Display3 method, they will all log what the function in the last assignment fo Display3 says to log, and that will always be Fred.

Related

What is happening in the creation and execution phase of an object?

I created a normal JavaScript object, like this:
let obj = {
name : "something",
print() {
console.log(this.name);
}
}
let res = obj.print()
console.log(res);
In this example obj has a name and a simple method. I am calling the method from the outside. After that I log the return value to the console, which is undefined.
I don't understand what is happening behind the scenes within the object. Please explain to me the creation and execution phase for this code.
Behind the scenes, the JavaScript interpreter creates an object in memory and refers to it through obj. When you call obj.print(), it refers to the same object and calls the method defined.
In the method, this refers to the object itself (obj), and is set by the JS interpreter as an implicit reference.
At last, you forgot to return the value from print(). So, nothing is assigned to res. Refer to the following example, as it prints the value of res correctly, when a value is returned from the function.
let obj = {
name: "something",
print() {
console.log(this.name);
return this.name;
}
}
let res = obj.print()
console.log(res);
I am going to write about the "behind the scenes" that you are asking for. Unfortunately this might confuse you, instead of making things clearer as JavaScript is quite a "different" language on its own.
In JavaScript a function is an object. Sometimes even called a first-class object. It has everything an object has (attributes and methods) and in addition can further more be invoked. Don't believe me? See for yourself:
function abracadabra()
{
return "this is magic";
}
console.log(abracadabra.name);
console.log(abracadabra.call());
Now a method is simply another function to which an attribute of an object is referring to. Let's take your example:
let obj = {
name : "something",
print() {
console.log(this.name);
}
}
Here obj is defined as an object with two attributes. A value type and a function. When you call the obj.print() function the following happens:
The function is called.
The so-called function context this is set to the object that the method is called upon. You can use this to access other attributes of the same object.
What exactly is this? As said it is the so-called function context that can refer to four different objects depending on how a function is called.
The function is called as a function. e.g. abracadabra() => this is referring to the global context, to which it is always referring by default.
The global context is dependent on the environment JavaScript is executed in. Remember JavaScript can not only run in a browser. It can also be used as a server-side scripting language. In the browser the global context is the current browser window. Don't believe me? See for yourself:
// We are not in any method, still "this" is available:
console.log(this.toString());
The function is called as a method. e.g. obj.print() => this is referring to the object the method is invoked on. I described this above.
The function is called as a constructor. e.g. new abracadabra() => A new empty object is created and this is referring to it. Now the function should extend the empty object with attributes.
The function is called using its apply or call methods => this is referring to whatever you pass as the very first argument of these methods.
So to sum it up: It can get tricky to really understand how these things work in JavaScript. This is because basically there are only objects and functions in the language. Methods look like methods, but are in reality only functions as well.
To get a really good in depth understanding I can recommend the book Secrets of the JavaScript Ninja.

JavaScript: Setting variable to new function on created object, it still calls old function

I've got a "class" that looks like this:
export function IBXMReplay() {
var onRow = function(){console.log("not right")};
....
}
Later on, it's called in a function like this:
var seqRow = function() {
onRow();
....
}
This function is essentially called by callbacks for web audio getAudio. I construct the object like this:
var replay = new IBXMReplay();
replay.onRow = function(){console.log("Right")};
However, when onRow() is called, it always prints "not right."
What am I doing wrong??? I tried creating a setter function but that didn't work either.
The problem is that the onRow function is being defined as a variable in the IBXMReplay local scope, and i'm assuming that the seqRow function is also inside IBXMReplay. Local-scope variables cannot be directly accessed nor modified from the outside.
For this to work, you'd need to make the onRow function a property of the IBXMReplay object (in JS functions are objects too):
export function IBXMReplay() {
this.onRow = function(){console.log("not right")};
....
}
Or better yet, store it in the prototype:
var IBXMReplay = function IBXMReplay() {
....
}
IBXMReplay.prototype.onRow = function(){console.log("not right")};
export IBXMReplay
Either way, you can later do:
var replay = new IBXMReplay();
replay.onRow = function(){console.log("Right")};
It should work as you expected.
Now, some explanation about that. Let's say you have 100 instances of the IBXMReplay 'class'. The differences between storing it as an object property vs. a prototype property are subtle but important:
Object property
Storing the method like this.onRow will mean there'll be 100 copies of it in memory. If you later edit it with replay.onRow = something, it'll be completely replaced for that particular instance.
Doing something like delete replay.onRow would not bring the original function back and will instead throw an 'undefined is not a function' error when executing that instance..
Prototype property
Storing the function in the object's prototype instead will mean there'll only be a single copy of onRow being referenced by all instances. When editing the instance with replay.onRow = something, the underlying prototype onRow is merely being shadowed by an object property with the same name, so any calls inside the IBXMReplay class will reference the overriding function instead.
Deleting the instance property delete replay.onRow will 'uncover' the original function.
This is how JavaScript works, and is called the prototype chain. Whenever you do foo.bar, a bar property is searched directly in the foo object, but if not found then it's searched in foo's prototype, and then the prototype's prototype, and so on, until reaching null, which has no prototype.

Variable vs Parameter Scope and Assignment Values

I have been watching a video series by Douglas Crockford. I am a little confused on the relationship of assigning variables as a parameter of assigning variables as a global variable, let me demonstrate.
var bob = 22; //global scope
function funky(parameter){
parameter = null; //sets the var passed in to null
bob = 44; //sets the bob
}
var hello = [];
funky(hello); // hello still has the value of an empty array even after the function is invoked.
bob; // value is 44
So 'var bob' is assigned 44 when funky() is invoked. This bob holds the new value outside the scope of the function.
var hello is passed as a parameter, while inside funky, it has the value null, outside when hello is invoked in global space, it holds the value of an empty array.
Is this just something I have to memorize? If a defined variable in global scope is passed as a parameter, it will only hold it's new assigned value within the scope of the function? Am I missing something at a broader scope with either how a parameter is passed and assigned in a function?
Here is a repl.it of the code with console.log outputs while inside and outside the function/global scope.
http://repl.it/NgN
Because this line
parameter = null;
Only sets the value of the function's parameter, not the value of the variable that was passed in. However, bob inside the function is interpretted as a reference to the global variable, so it does modify the external value.
Note, however, that objects are references, so if you wrote it like this:
function funky(parameter){
parameter.hello = null;
}
Then calling funky does directly modify the object that the parameter references.
var x = { hello: [] };
funky(x); // x => { hello: null }
Is this just something I have to memorize?
Yes, understanding how parameters are passed is pretty important.
If a defined variable in global scope is passed as a parameter, it will only hold it's new assigned value within the scope of the function?
Parameters only hold their values within the scope of the function. Global variables are, well, global, so if they are modified inside the function, they keep that value outside the function.
Also, be aware of hiding—if a parameter has the same name as a global variable, the parameter hides the variable within the scope of that function.
ok, so the following example will confuse you even more.
var a = {'a': 'A'};
function foo(param){param.a = 'B';}
foo(a);
// now print a
{'a': 'B'}
Here's an important concept:
Primitives are passed by value, Objects are passed by "copy of a reference".
As for more information, you can check this answer:
Does Javascript pass by reference?
Regarding this:
var hello is passed as a parameter, while inside funky, it has the value null, outside when hello is invoked in global space, it holds the value of an empty array.
Do not think of parameter as an alias for hello. They are distinct variables. They are free to point to different things. Assigning a value -- null in this case -- to parameter has no effect on hello. After that line, the global variable hello still points to an empty array. Only parameter points to null.
Furthermore, funky(hello) passes the value of hello to the funky function. Consider the following:
var arr = [];
function addElements(arr) {
arr.push(5);
arr.push(77);
}
The global arr is still empty after addElements executes. The arr inside the function is distinct from the arr in the global scope. (I should note that the contents of each arr may point to the same objects, but that's a separate issue.)
(Note also that some languages do allow the sort of aliasing you seem to expect, but that's not how parameters are passed in JavaScript.)
JavaScript assumes that if you assign a value to a variable it is in the global scope—unless that variable is declared with a var. Like for example, this:
var bob = 22; //global scope
function funky(parameter){
parameter = null; //sets the var passed in to null
var bob = 44; //sets the bob
}
var hello = [];
funky(hello); // hello still has the value of an empty array even after the function is invoked.
bob; // value is 22
Since the function's bob variable is declared with a var, it is owned by the local scope of the function and has no impact on the bob declared outside the function's scope.
Regarding how objects differ from primitives when passed into functions, check out Jonathan Snook's article http://snook.ca/archives/javascript/javascript_pass and the helpful comment from #p.s.w.g.

Javascript clears a variable after there is no further reference it

It is said, javascript clears a variable from memory after its being referenced last.
just for the sake of this question i created a JS file for DEMO with only one variable;
//file start
//variable defined
var a=["Hello"]
//refenence to that variable
alert(a[0]);
//
//file end
no further reference to that variable, so i expect javascript to clear varaible 'a'
Now i just ran this page and then opened firebug and ran this code
alert(a[0]);
Now this alerts the value of variable, If the statement "Javascript clears a variable after there is no further reference it" is true how come alert() shows its value.
Is it because all variable defined in global context become properties of window object, and since even after the execution file window objects exist so does it properties.
Of course the variable HAS to be kept around in this example - after all, you keep the ENVIRONMENT it has been defined in around. As long as you COULD still access it is has to be kept, so if you can make this test and it ever fails - the JS interpreter is "kaputt".
Do this instead:
(function () {
var a = 1;
}());
// and NOW, at THIS point there is nothing any more referencing "a"
Of course, from out "here" you cannot test that - you'd have to look at the internal memory structures of the JS interpreter/compiler (today in the days of V8 it's more like a "interpiler" or "compreter" aynway).
In addition though, like all garbage collecting (GC) languages, the memory is not freed immediately but depending on GC strategy used by the particular implementation of Javascript, which also takes current memory usage and demand into account. GC is a costly operation and is delayed, if possible, and/or run when it does not take away CPU resources from the real app.
By the way, slight variation:
(function () {
var a = 1;
//empty function assigned to global namespace,
//in browsers "window" is the global object
window.example_function = function () {};
}());
In THIS example the result is the same as in yours: as long as you view that page the memory for "a" is NEVER released: the function assigned to property example_function of the global object still exists, so all of the environment is was defined in has to be kept! Only after
delete window.example_function
which deletes that property - which in the end is a pointer to the (empty) function expression - from the global object, could variable "a" be released. Also note that "a" is not even used by that function, see Google and search for "lexical scoping", one of the most defining properties of the Javascript language.
And even more fun:
Had I written
//define a variable in global namespace
var example_function;
(function () {
var a = 1;
//empty function assigned to global namespace,
//in browsers "window" is the global object
example_function = function () {};
}());
I could not use "delete" to remove it, in fact I could not ever release it. That's because in the first example example_function becomes a new property of the global object (emphasis), while in this last example it is a variable, and "delete" works with object properties only. (I used "windows.example_function" in the earlier example, but without the "window." it would have been just the same, example_function would have been created as property in the global object. Only the "var" statement creates variables.)
Is it because all variable defined in global context become properties of window object, and since even after the execution file window objects exist so does it properties.
Yes. You would either have to close the window, or explicitly remove a from the window properties.
A side effect of your script is that there's a property name "a" defined in the global object ( window ), so there is actually still a global reference to a.
(function()
{
var a = 12;
alert(a);
})();
If we wrap the declaration of a in self-executing function like here, a will be inaccessible after the function has exited.
Yes, although you can mitigate this behavior by declaring and acting on your variables within the scope of a function:
( function myfunc(a){
var b = 100;
alert(a); // 200
alert(b); // 100
}(200));
alert(a); // undefined
alert(b); // undefined
Referencing a variable does not mean using it in some manner. As long as there is at least one reference to the variable which can still be accessed, it has to be kept in memory.
Looking at your example, the following line has no effect on whether the variable gets cleared or not. Even if a wasn't being used anywhere, it would still have been kept intact.
//refenence to that variable
alert(a[0]);
Although an object may be created inside a function, its lifetime may extend beyond the function itself. The only thing to remember is whether there are still any valid references to the object in question. Consider this example,
(function() {
var a = ["Hello"];
var copy = a;
a = null;
setTimeout(function() {
// a: null, copy: ["Hello"]
console.log("a: %o, copy: %o", a, copy);
}, 2000);
})();
The variable a is created inside an anonymous function, and another variable copy is referencing it. a is set to null right after, but we still have 1 reference to it from copy. Then we create a timeout which makes another reference to the same variable. Now even though this anonymous function will go out of scope after it executes the setTimeout, we still left 1 reference to it in the timeout function. So this copy variable will still be intact when the timeout function is run.

Still confused about JavaScript's 'this'

I've been reading through quite a few articles on the 'this' keyword when using JavaScript objects and I'm still somewhat confused. I'm quite happy writing object orientated Javascript and I get around the 'this' issue by referring the full object path but I don't like the fact I still find 'this' confusing.
I found a good answer here which helped me but I'm still not 100% sure. So, onto the example. The following script is linked from test.html with <script src="js/test.js"></script>
if (!nick) {
var nick = {};
}
nick.name= function(){
var helloA = 'Hello A';
console.log('1.',this, this.helloA);
var init = function(){
var helloB = 'Hello B';
console.log('2.',this, this.helloB);
}
return {
init: init
}
}();
nick.name.init();
What kind of expected to see was
1. Object {} nick.name, 'Hello A'
2. Object {} init, 'Hello B'
But what I get is this?
1. Window test.html, undefined
2. Object {} init, undefined
I think I understand some of what's happening there but I would mind if someone out there explains it to me.
Also, I'm not entirely sure why the first 'console.log' is being called at all? If I remove the call to the init function //nick.name.init() firebug still outputs 1. Window test.html, undefined. Why is that? Why does nick.name() get called by the window object when the html page loads?
Many thanks
Also, I'm not entirely sure why the first 'console.log' is being called at all?
nick.name = function(){
// ...
}();
Here you define a function, call it immediately (hence ()) and assign its return value ({init: init}) to nick.name
So the execution is:
Create a variable called nick if there isn't one with a non-falsey value already
Create an anonymous function that…
Creates a variable called helloA in its own scope
Outputs data using console.log containing "1" (as is), this (the window because the function is executing in the global context instead of as a method), and this.helloA (window.helloA, which doesn't exist.
Defines a function called init
Returns an object which gets assigned to nick.name
Then you call nick.name.init() which executes the init function in the context of name.
This defines helloB
Then it console.logs with "2" (as is), this (name), and this.helloB (nick.name.helloB - which doesn't exist)
So the first output you get is from console.log('1.',this, this.helloA);
I think your main problem is that you are confusing this.foo (properties on the object on which a method is being called) with variable scope (variables available to a function)
It's much simpler if you think about this as a function, not as a variable. Essentially, this is a function which returns current "execution context", that is, the object the current function was "applied" to. For example, consider the following
function t() { console.log(this)}
this will return very different results depending upon how you call it
t() // print window
bar = { func: t }
bar.func() // print bar
foo = { x: 123 }
t.apply(foo) // print foo
this is defined on a per-function basis when the function call is made. When you call a function as o.f(), this will be o within the function, and when you call it as f(), this will be the global object (for browsers, this is the window).
You wrote nick.name = function(){...}(); and the right-hand part is of the form f(), hence the Window.
var foo = bar; defines a local variable. It may not be accessed as this.foo (well, except when you're at global scope, but that's silly). To define a member, you usually write this.foo = bar; instead.
This is what your code does:
It creates an object and assigns to the variable nick.
It creates an anonymous function.
It calls the function (in the window scope).
It assigns the return value (an object containing the init property) to the name property of the object.
It gets the value from the init property, which is a method delegate, and calls the method.
The anonymous function does this:
It declares a local variable named helloA and assigns a string to it. (Creating a local variable doesn't add it as a property to the current object.)
It logs this (window) and the helloA property (which doesn't exist).
It creates an anonymous function and assignes to the local variable init.
It creates an object with the property init and the value from the local variable init.
The anonymous function assigned to the init property does this:
It declares a local variable named helloB and assigns a string to it. (Creating a local variable doesn't add it as a property to the current object.)
It logs this (the object from the name property, not the nick variable), and the helloB property (which doesn't exist).

Categories