Javascript / NodeJS : Passed in object seems to overwrite "self" - closure issue? - javascript

My first ever Stackoverflow question (that I can remember). I'm slowly getting back into development after many years absence and experimenting with Node and Javascript.
I'm hoping this is really simple but I just can't see what I'm doing wrong.
I have 2 instances of a fairly simple javascript object.
When I pass one instance of that object to a function on the other things fall down.
The first time I reference a "self" value it's fine.
I then call a function on the passed-in object.
If I try to reference "self" again afterward, it seems that "self" has been overwritten by the passed-in object.
Here's the object source:
"use strict";
//simple object
module.exports.SimpleObject = function SimpleObject(inputName) {
try{
var self = this; //closure so we don't lose this reference in callbacks
self.name = inputName;
}
catch(err) {
console.log('Unable to create SimpleObject: '+err);
}
SimpleObject.prototype.getName = function() {
self = this;
return self.name;
}
SimpleObject.prototype.nestedOverwrite = function(anObject) {
self = this;
return "#1 self.name: "+self.name+" ObjectName: "+anObject.getName()+" #2 self.name: "+self.name;
}
return this;
}
And here's the test case:
var testSimpleObjectModule = require('./simpleObject');
var o0 = new testSimpleObjectModule.SimpleObject('test0');
var o1 = new testSimpleObjectModule.SimpleObject('test1');
console.log(o0.nestedOverwrite(o1));
console.log(o1.nestedOverwrite(o0));
Here's the output I get:
#1 self.name: test0 ObjectName: test1 #2 self.name: test1
#1 self.name: test1 ObjectName: test0 #2 self.name: test0
Hopefully you can see in each case, the first call to self.name is fine, the second has lost context.
any help greatly appreciated!

You're assigning functions to the prototype object that gets assigned to new SimpleObject every time the constructor is called. So the second time the constructor is called, the prototype is updated, and any previous object created using the prototype starts using the new function (with the new self value).
If you want those closures to have self, don't put them on the prototype, put them on this (more below).
But note that in the code you've quoted, there's no reason for the self variable. You can use prototype functions (which is more efficient) rather than creating functions for each instance. To do that, use this (without assigning it to self) and define the functions outside the constructor, like this:
"use strict";
//simple object
module.exports.SimpleObject = function SimpleObject(inputName) {
this.name = inputName;
};
SimpleObject.prototype.getName = function() {
return this.name;
};
SimpleObject.prototype.nestedOverwrite = function(anObject) {
return "#1 this.name: "+this.name+" ObjectName: "+anObject.getName()+" #2 this.name: "+this.name;
};
But if you're doing something with self that you aren't showing that means you really need it (e.g., that you can't trust the value of this when the functions are called &mdasdh; they're being used as callbacks/event handlers, etc.), here's how you do that:
"use strict";
//simple object
module.exports.SimpleObject = function SimpleObject(inputName) {
var self = this;
self.name = inputName;
self.getName = function() {
return self.name;
};
self.nestedOverwrite = function(anObject) {
return "#1 self.name: "+self.name+" ObjectName: "+anObject.getName()+" #2 self.name: "+self.name;
};
};
Side notes:
There's no reason to return this at the end of a constructor function.
Note that we don't want to do self = this; in each of the functions; the whole point of using self is that when the function is called, you don't want to rely on this having the correct value. (If yoU can rely on this being set, then use the prototype version of the functions.)
Never catch an exception in a constructor function and just say you couldn't do the construction, because by doing that, you allow the new Xyz expression to complete, and the un-initialized object is returned as the result of the expression. I've removed the try/catch from the above for that reason.
I recommend not relying on the horror that is automatic semicolon insertion. End expressions with semicolons (including when you're assigning a function to a variable or property).

Related

how to access the properties of the parent object?

please help solve the problem.
i make 3 objects:
level:
var Level = function() {
self = this;
this.cellsObserver = new CellsObserver();
console.log('co from level ' + this.cellsObserver);
this.fieldObj = new Field();
}
field:
var Field = function() {
self = this;
this.init();
};
Field.prototype = Object.create(Level.prototype);
Field.prototype = {
init: function() {
console.log('co from field ' + self.cellsObserver);
}
}
observer:
var CellsObserver = function(){
.............
}
in result console output follow:
co from level [object Object] co from field undefined
I do not understand why in the second case output 'undefined'. because I have appointed a parent:
Field.prototype = Object.create(Level.prototype);
tl;dr: there is no field cellsObserver neither in Field or Level.prototype object. And there is no such thing as a classical 'self' in Javascript.
The long story.
When you're trying to fetch object.property, the Javascript will look at the object trying to find property, then to the object.prototype (which is another object) and so on until it get to the null prototype in the Object.prototype reference.
So, when you're calling second time the this.cellsObserver in Level constructor function, it goes like this:
this is a just new constructed object (if it called with the new keyword), and there is cellsObserver in the property list, so it'll be fetched without any deep lookup.
Then,
Field.prototype = Object.create(Level.prototype);
This only means that Field.prototype now will refer to a new object, which properties are the same as in Level.prototype in that moment.
From your code, there are no non-standard properties in Level.prototype object (you didn't provide any).
Then,
self = this;
Here you just assigning a global variable called self a reference to the just created object or window object (it depends). If you wish to store a reference to the this object, you should var: var self = this. But you should remember that this self variable could be accessed only in the scope where is was declared or in a closure.
Then,
Field.prototype = {
init: function() {
console.log('co from field ' + self.cellsObserver);
}
}
First of all, here you just override the previos instruction (Field.prototype = Object.create(Level.prototype);). If you want to extend the Field.prototype object, you could do in the Object.create call in second argument, or just by accessing a property like that: Field.prototype.init = function(){...}.
Second. The self variable could contain anything when the init function will be executed. Just use this for the current object here.
Third. Let's try to guess what will happen when this.cellsObserver get evaluated inside this init function.
The this object is referring to the Field instance, and there is no cellsObserver property there, so we move to the Field.prototype object, which is defined above ({ init: function () {...}}), there is no cellsObserver either, so we move to the Object.prototype object, which is null. Ok, our lookup is failed, this.cellsObserver is undefined.
What would be it look like if the Field.prototype = Object.create(Level.prototype) wasn't been overridden by the following code? The this object will refer to the Field instance as early. The Field.prototype is referring to the object which is copy of the Level.prototype object, and there is no cellsObserver here either. So, we look to the Object.prototype, and that's it. There is no cellsObserver in any objects except in the instances on Level class, which isn't referenced in any way in the Field instances.
You can play with that here: http://www.objectplayground.com/ by pasting this code:
var Level = function() {
this.cellsObserver = new CellsObserver();
this.fieldObj = new Field();
}
var Field = function() {
this.init();
};
Field.prototype = Object.create(Level.prototype);
Field.prototype.init = function() {};
this.field = new Field();
You are overriding the Field.prototype. Just assign to Field.prototype.init.

JavaScript - how to use "self" from current JS Class

I tried to create this syntax for JavaScript:
var user = new Class({
name: 'No name',
sayHello: function () {
console.log(self.name);
}
});
user.sayHello(); // will print "undefined" because "self" isn't visible
And I created this implementation of Class:
function Class (_properties) {
var self = this;
// copy all properties and methods to this class
for (var _property_name in _properties) {
self[_property_name] = _properties[_property_name];
}
}
But the problem is in self that I wish to use in each class. The self.name will be undefined because self is a [Window object].
Question: how to modify my code to use self inside all functions in class instances? The self is needed to not drop context if the function will be called from external context.
Expected result (I mean that code above should be executed as code below):
function Class (_properties) {
var self = this;
// properties
self.name = 'No name';
// methods
self.sayHello = function sayHello () {
console.log(self.name);
};
}
var user = new Class();
user.sayHello();
The goal of my experiment is to use self that always is reference to current object. Because sometimes when we use calls like this $.get('...', function () { console.log(this); }) - this is set to function local scope. But I wish to use self (magic property) that always be a reference to object of current class (scope of current class).
So I finally figured out that you are talking about the self variable in the sayHello method that is giving you problems.
You need to google JavaScript scope this tutorial - it's not the most intuitive concept, but once you get it, everything will make sense.
In the mean time, it's worth pointing out that self is not a reserved word in JavaScript and doesn't do anything special, but this is.
So what you want, is something like this:
function Class (_properties) {
// copy all properties and methods to this class
for (var _property_name in _properties) {
this[_property_name] = _properties[_property_name];
}
}
var User = new Class({
name: 'No name',
sayHello: function () {
console.log(this.name);
}
});
var user1 = new User();
var user2 = new User();
The keyword new in front of the Class() function causes it to be executed as a constructor function meaning that, inside of the constructor function this will reference it's own scope.
Also, because the property sayHello is a function to be executed inside the scope that you created, you can use this.name - as this will refer to the scope that you created when you instantiated a new Class.
And just because this scope thing is no doubt confusing (don't worry, it probably will be for a while) - if you haven't created a scope (by using a constructor or object literal) you're working inside of the global scope which, in the case of the browser, is the Window object.
That's because you never created an object:
var options = {
name: 'No name',
sayHello: function () {
console.log(self.name);
}
}
var user1 = new Class(options);
var user2 = new Class(options);
"this" refers to the object owner of the current function. In this case would be windows object. At least you construct an object from the function Class.
If you want to avoid someone overwrite a property of Class.prototype is necessary to use hasOwnProperty in your for in

What is a best practice for ensuring "this" context in Javascript?

Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction();
}
}
ex = new Example;
ex.publicFunction();
Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?
Using closure. Basically any variable declared in function, remains available to functions inside that function :
var Example = (function() {
function Example() {
var self = this; // variable in function Example
function privateFunction() {
// The variable self is available to this function even after Example returns.
console.log(self);
}
self.publicFunction = function() {
privateFunction();
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo
Yet another approach is to use "call":
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo
both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.
It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:
If you call a function via an object property as in object.method(), then this will be set to the object inside the method.
If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.
If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.
If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.
If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).
In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.
There's a nice summary of these various methods here on MDN.
So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.
If you want to explictly set it to the current value of this in your method, then you can do so like this:
var Example = (function() {
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction.call(this);
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.
I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable
function Example() {
var that = this;
// ...
function privateFunction() {
console.log(that);
}
this.publicFunction = function() {
privateFunction();
}
}
a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.
var privateFunction = function() {
console.log(this);
}.bind(this);
I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:
var Example = function(){
this.prop = 'whatever';
}
Example.prototype.fn_1 = function(){
console.log(this.prop);
return this
}
Example.prototype.fn_2 = function(){
this.prop = 'not whatever';
return this
}
var e = new Example();
e.fn_1() //whatever
e.fn_2().fn_1() //not whatever
Here's a fiddle http://jsfiddle.net/BFm2V/
If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.
In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:
function boundFn(thisobj, fn) {
return function() {
fn.apply(thisobj, arguments);
};
}
Then use it like this:
var Example = (function() {
function Example() {
var privateFunction = boundFn(this, function() {
// "this" inside here is the same "this" that was passed to boundFn.
console.log(this);
});
this.publicFunction = function() {
privateFunction();
}
}
return Example;
}()); // I prefer this order of parentheses
Voilà -- this is magically the outer context's this instead of the inner one!
You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):
if (!Function.prototype.bind) {
Function.prototype.bind = function (thisobj) {
var that = this;
return function() {
that.apply(thisobj, arguments);
};
}:
}
Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.
ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.
I would say assigning self to this is a common technique:
function Example() {
var self = this;
function privateFunction() {
console.log(self);
}
self.publicFunction = function() {
privateFunction();
};
}
Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.
It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.

How do I retain the properties of a Javascript object while changing the object's constructor to an instance function

I have an instance function in javascript and for naming conventions I have other instance function added as property of the first instance function object. It's better illustrated in the following working JavaScript.
var MyModule = (function() { // instance function
return function() {
console.log("ran MyModule");
}
}());
MyModule.RelatedModule = (function() { //second instance function is a property of first instance function object
return function() {
console.log("ran RelatedModule");
}
}())
var inst1 = new MyModule(), // logs "ran MyModule"
inst2 = new MyModule.RelatedModule(); // logs "ran RelatedModule"
This works as intended with no errors. What I'd like to do though is to create the function definition for MyModule after I've created the MyModule object, can anyone help me achieve this? I've illustrated my attempts below.
var MyModule = {}; // create object first, try to set a constructor on it later
MyModule.RelatedModule = (function() { // add properties to the MyModule object
return function() {
console.log("ran RelatedModule");
}
}())
// the following does not work, I'd like to set the `MyModule` constructor instance function and retain the `MyModule.RelatedModule` property
MyModule.constructor = (function() {
return function() {
console.log("ran MyModule");
}
}());
So, how do I retain the properties of an object and change it's constructor?
You're confusing a couple of concepts. In your first example, MyModule doesn't have a constructor, it is a constructor. That is, it's a function object that you intend to use with the new operator to create new objects. Those objects will have a constructor property that points back to MyModule (the function that created them). Changing this constructor property won't have any effect on MyModule; that property is just a pointer back to the function that instantiated the object.
In other words, you can't change MyModule's constructor. That's a meaningless statement.
Now, when you write:
var MyModule = {};
...you create a new object whose constructor property is Object():
console.log(MyModule.constructor) // prints Object()
Again, changing this property doesn't really do much (except obfuscate some useful book-keeping).
At this point MyModule is just a plain-old object. It's not a function at all. That's why you're getting the "not a function" error. Because it's not, but you're trying to use it as though it is. If you want that name to refer to a function (i.e. to a different object) then you're going to lose all references to any properties you previously set, because you're pointing at an entirely new object.
That's just the way it is.
Now, you could save a reference to the object that contains all those previously-set properties and copy them back into MyObject once you've pointed that name at a function. But I'm not sure what the point would be.
Everything lwburk says is correct, however, the below does what you were trying to accomplish, it does this by calling an init() method from MyModule.
var MyModule = function(){
return this.init();
};
MyModule.prototype.init = function(){};
MyModule.RelatedModule = (function() { // add properties to the MyModule object
return function() {
console.log("ran RelatedModule");
};
}());
MyModule.prototype.init = function(){
console.log("ran MyModule");
};
var inst1 = new MyModule(),
inst2 = new MyModule.RelatedModule();
First, there is a known browser bug where the constructor property (of the constructed object) correctly resolves using the prototype, but is not applied in object construction. So you need the following polyfill:
function NEW(clas, ...args)
{
let res = Object.setPrototypeOf({}, clas.prototype);
res.constructor.apply(res, args);
return res;
}
Second, you need to be setting MyModule.prototype.constructor instead of MyModule.constructor. The reason is that MyModule.constructor is the function that constructs new classes, not the function that constructs new objects. (See: Why does a class have a "constructor" field in JavaScript?)
In other words:
var MyModule = {}; // create object first, try to set a constructor on it later
MyModule.RelatedModule = (function() { // add properties to the MyModule object
return function() {
console.log("ran RelatedModule");
}
}())
// set the `MyModule` prototype.constructor instance function and retain the `MyModule.RelatedModule` property
MyModule.prototype = {
constructor() {
console.log("ran MyModule");
}}
var inst1 = NEW(MyModule), // logs "ran MyModule"
inst2 = NEW(MyModule.RelatedModule); // logs "ran RelatedModule"
The short answer is that you can't do that. You can however change the prototype of an object. Check out this answer for insight into how to rethink your approach to work with this constraint: Changing constructor in JavaScript

"this" keyword in Object method points to Window

var name = 'The Window';
var object = {  
name: 'My Object',
getNameFunc: function(){    
return function() {     
return this.name;   
}; 
}
};
console.log( object.getNameFunc()() );
when i tested the code. the result is The Window. but i think this.name; should be My Object. what's wrong with my thinking.
when i add var before the name : "My Object", it show's an error.? why?
this inside a function is the "receiver" it was invoked upon.
That is,
for the construct x.f(), this inside the function (f) will evaluate to the value of x.
for all other cases, this will evaluate to window inside the invoked function. (The functions call, apply, and bind can also alter this... but that's another story.)
In the posted example the second function (the one with this.name) is not invoked using the x.f() form and so this is the window object.
The "simple fix" is to use a closure: (The first function is invoked in the x.f() form and thus this is the same as object, which is as expected. We capture the value of this in the current scope via a closure created with self and the returned function.)
getNameFunc : function () {
var self = this
return function () {
return self.name
}
}
However, I may consider another design, depending :)
Happy coding.
Additional clarification, for comment:
...that is because you are using circle.getArea() which is of the form x.f(). Thus this inside the getArea function evaluates to circle.
In the code posted you are invoking two different functions in a row. Imagine writing the code like this:
var nameFunc = object.getNameFunc()
nameFunc()
The first function call is in the form of x.f() and thus this inside getNameFunc is the evaluation of object. However, in the second line, the function (nameFunc) is not invoked in the form x.f(). Therefore, the this inside nameFunc (the function returned from getNameFunc) will evaluate to window, as discussed above.
var myObject = {
name:'My Object'
};
console.log(myObject.name);
console.log(myObject['name']);
There are various other ways to make objects in javascript.
this is a hidden argument that is automatically passed from the calling function to the callee. The traditional way is to do:
function MyObject() {
this.name = 'My Object';
}
myObject = new MyObject();
console.log(myObject.name);
Nowadays you might just use closures:
[**edit**: redacted because not a good method]
Nowadays you might just use closures, correctly:
function makeObject() {
var THIS = {};
THIS.name = 'My Object';
THIS.sayMyName = function () {
return THIS.name+" is my name";
}
return THIS;
}
There are many libraries that support "smarter" ways to make objects as well.
You need to use .bind() to set the right context for the method, so the this keyword will be what you want it to actually be.
The default is in such a scenario for the this keyword is to point to the window object, because...this is how the JS engine works.
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
}.bind(this); // <-- sets the context of "this" to "object"
}
};
console.log( object.getNameFunc()() );
As the others have written, you need to target this. I believe this piece of code will help you to understand how this in javascript works
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
that = this; // targeting this
return function() {
return that.name;
};
}
};
alert(object.getNameFunc()()); // it is My Object now
var object = {
  name : "My Object",
  getNameFunc : function(){
    return (function(){
      return this.name;
    }).bind(this);
 }
};
.bind, use the ES5-shim for browser support
The problem lies in the way you have declared your function.
The important point we need to remember while placing function inside a method is to use arrow function (if our function block is going to have a this keyword).
Instead of declaring a new variable and assigning this keyword to the variable, we can easily solve this problem using Arrow Functions.
Just convert the normal function into arrow function and boom it will work.
var name = 'The Window';
var object = {  
name: 'My Object',
getNameFunc: function(){    
return () => {     
return this.name;   
}; 
}
};
console.log( object.getNameFunc()() );
This works because arrow functions are always lexically binded and not dynamically binded like any other functions.

Categories

Resources