nodejs - Which "this" is this? - javascript

So this is an awkward question to ask, but I'm learning NodeJS and I have a question. In Java when I call a method from an object, the this instance remains the same (as in this example).
private Test inst;
public Test() {
inst = this;
this.myFunction();
}
private void myFunction() {
System.out.println(inst == this);
}
This returns true (in theory, that's code off the top of my head). However, in NodeJS when I attempt to do something similar it fails.
var MyObject = function () {
this.other = new OtherObject();
this.other.on("error", this.onError);
console.log(this); //This returns the MyObject object
}
MyObject.prototype.onError = function (e) {
console.log(this); //This returns the OtherObject object, where I thought it would return the MyObject object.
}
My question is why is this so, and if this is being done incorrectly on my part, how may I correctly reference other variables in the MyObject instance from the onError method?

In JavaScript "methods" are just functions that part of an object.
If you do
var obj = new MyObject();
obj.onError();
The this in onError will be the obj object (because it is the object it is invoked from)
Instead in you case you are passing this.onError to the EventEmitter it will call that function with the EventEmitter (OtherObject) as this.
To avoid that problem use an anonimous function.
var MyObject = function () {
var self = this;
this.other = new OtherObject();
this.other.on("error", function (e) { self.onError(e); });
}
In this way you are binding this back to the object you expect

There is simpler way - you can use bind function.
var EventEmitter = require('events').EventEmitter;
var MyObject = function () {
this.other = new EventEmitter();
this.other.on("error", this.onError.bind(this));
console.log(1, this instanceof MyObject); // 1, true
};
MyObject.prototype.onError = function (e) {
console.log(2, this instanceof MyObject); // 2, true
};
MyObject.prototype.callError = function (e) {
console.log(3, this instanceof MyObject); // 3, true
this.other.emit('error', e);
};
var mo = new MyObject();
mo.callError(new Error(1));
Demo

Related

Javascript prototype method "Cannot set property"

I'm always getting Cannot set property 'saySomething' of undefined but why?
Am I making a mistake somewhere?
var Person = new Object();
Person.prototype.saySomething = function ()
{
console.log("hello");
};
Person.saySomething();
Debugging tip: You get this ..of undefined errors when you try to access some property of undefined.
When you do new Object(), it creates a new empty object which doesn't have a prototype property.
I am not sure what exactly are we trying to achieve here but you can access prototype of function and use it.
var Person = function() {};
Person.prototype.saySomething = function() {
console.log("hello");
};
var aperson = new Person();
aperson.saySomething();
The prototype property exists on functions, not on instantiated objects.
var Person = new Object();
console.log(Person.prototype); // undefined
var Person2 = function () {}
console.log(Person2.prototype); // {}
This is useful because things put on the prototype of a function will be shared by all object instances created with that function (by using new).
var Person = function() {};
Person.prototype.saySomething = function() {
console.log("hello");
};
console.log(
new Person().saySomething === Person.prototype.saySomething // true. they are the same function
);
If all you want is to add a method to the person object, there's no need for a prototype:
var Person = {};
Person.saySomething = function() {
console.log("hello");
};
Person.saySomething();
You can even use object literal syntax:
var Person = {
saySomething: function() {
console.log("hello");
}
};
Person.saySomething();
i was trying out some code thought of posting it, might help others.
<script>
var MODULE = {};
MODULE = (function (my) {
my.anotherMethod = function () {
console.log("hello ");
};
my.newMethod = function(){
console.log("hi new method ");
}
return my;
}(MODULE));
MODULE.anotherMethod();
MODULE.newMethod();
</script>
And please not var MODULE ={}, if this is not initialized with {} then it give cannot set property.
I know i am late to the party but as you see there is no satisfying answer available to the question so i am providing my own.
In your case when you write
var Person = new Object();
you are creating an instance of Object type.
You can add a property using prototype property to the Object, not to the instance of Object.which you can use by the instance laterly.
so you can define like
Object.prototype.saySomething = function ()
{
console.log("hello");
};
now you can call it like this.
Person.saySomething();
You can check here.
var Person = function(name) {
this.canTalk = true;
this.name = name;
};
Person.prototype.greet = function() {
if (this.canTalk) {
console.log('Hi, I am ' + this.name);
}
};
bob = new Person('bob');
bob.greet();

Accessing object properties in listener method

Let's say I have the following code:
var Obj = function() {
this.property = 1;
this.arr = [...] // array containing elements we want to add event listeners to
for (...) {
this.arr[i].addEventListener("click", this.listener, false);
}
}
Obj.prototype.listener = function() {
console.log( this.property ); // DOES NOT WORK! *this* does not point to Obj.
}
var a = new Obj();
How do I access object properties (and methods) within a listener? I would assume I'd need to pass it as a parameter? Is the way I'm going about this structurally wrong?
When the function is called as an event listener, the context (this) is changed to something other that the object itself.
To resolve this, manually bind the context to the object instance in the constructor using bind(). This way, this will always point to the object instance, independent of the calling context:
var Obj = function() {
this.property = 'foo';
this.listener = this.listener.bind(this);
}
Obj.prototype.listener = function() {
console.log(this.property);
}
var a = new Obj();
a.listener.call({});
As suggested by #Tushar, you can use Function.prototype.bind() and pass this.property as parameter
<body>
click
<script>
var Obj = function() {
var obj = this;
this.property = 1;
this.arr = [document.body];
for (var i = 0; i < obj.arr.length; i++) {
obj.arr[i].addEventListener("click"
, obj.listener.bind(obj.arr[i], obj.property), false);
}
}
// note order of parameters; e.g., `prop`, `e`
Obj.prototype.listener = function(prop, e) {
console.log(prop, e); // `1`, `event` object
}
var a = new Obj();
</script>
</body>

creating objects from JS closure: should i use the "new" keyword?

i answered one question about closures here in SO with this sample:
function Constructor() {
var privateProperty = 'private';
var privateMethod = function(){
alert('called from public method');
};
return {
publicProperty: 'im public',
publicMethod: function(){
alert('called from public method');
},
getter: privateMethod
}
}
var myObj = new Constructor();
//public
var pubProp = myObj.publicProperty;
myObj.publicMethod();
myObj.getter();
//private - will cause errors
myObj.privateProperty
myObj.privateMethod
a user commented on my answer saying:
Also, if your function explicitly returns an object it is not a good practice to call it with new because that is misleading - if using new you'd expect the result to be an instance of Constructor
i usually create objects using new. but why is it not a good practice? it seems like using new and not using new returns the same thing. what is the proper way of creating objects from closures?
No, it's not the same thing. Consider when using instanceof:
function C1() {
return {};
}
function C2() {
}
var c1 = new C1();
var c2 = new C2();
alert(c1 instanceof C1); // false; wha...?
alert(c2 instanceof C2); // true, as you'd expect.
Here's a demo.
So instead, create them by assigning to this, possibly with a safeguard to prevent forgotten news.
function Constructor() {
var privateProperty = 'private';
var privateMethod = function() {
alert('Called from private method');
};
this.publicProperty = "I'm public!";
this.publicMethod = function() {
alert('Called from public method');
};
this.getter = privateMethod;
}
Even better, use the prototype when possible:
function Constructor() {
var privateProperty = 'private';
var privateMethod = function() {
alert('Called from private method');
};
this.getter = privateMethod;
}
Constructor.prototype.publicProperty = "I'm public!";
Constructor.prototype.publicMethod = function() {
alert('Called from public method');
};
Consider point 4 from this answer: What is the 'new' keyword in JavaScript?
"It returns the newly created object, unless the constructor function returns a non-primitive value. In this case, that non-primitive value will be returned."
So as function C1 from minitech's answer returns an empty object the variable c1 will be that returned object and not the one created by the 'new' statement. Therefore no instance of the constructor function.
If I try to return a primitive value from the constructor function my webstorm tells me: "When called with new, this value will be lost and object will be returned instead."

avoid needing to declare 'var me = this' for javascript prototype functions

Currently, I create objects in javascript by declaring a construction (regular function) then add methods to the prototype like so
function Test(){
}
Test.prototype.test1 = function(){
var me = this;
}
However, I would like to avoid having to declare var me = this at the top of every function. The following seems to work, but seems like it would be very inefficient:
$(document).ready(function(){
var n = 0;
(function(){
function createTest(){
var me;
function Test(){
this.n = n;
this.testArr = [1, 2, 3, 4];
n++;
}
Test.prototype.test1 = function(){
me.test2();
};
Test.prototype.test2 = function(){
alert(me.n);
$.getJSON('test.php', {}, function(reply)
//want to be able to use 'me' here
me.newField = reply;
});
};
var t = new Test();
me = t;
return t;
}
window['createTest'] = createTest;
})();
var t = createTest();
t.test1();
var t2 = createTest();
t2.test1();
t.test1();
});
This code outputs the expected, but is it actually as inefficient as it looks (the Test object being re-declared every time you call createTest())?
Anyhoo, this would seem a bit hacky... is there a completely different way to do this that is better?
EDIT: The real reason I would like to do this is so that callbacks like the one in test2 will have references to the correct this.
What you can do is bind the current this value to a function and store a copy somewhere. (For the sake of efficiency.)
if (!Function.prototype.bind) {
// Most modern browsers will have this built-in but just in case.
Function.prototype.bind = function (obj) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () { },
bound = function () {
return self.apply(this instanceof nop ? this : (obj || {}),
args.concat(slice.call(arguments)));
};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;
};
}
function Test(n) {
this.n = n;
this.callback = (function () {
alert(this.n);
}).bind(this)
}
Test.prototype.test1 = function () {
this.test2();
}
Test.prototype.test2 = function () {
doSomething(this.callback);
}
function doSomething(callback) {
callback();
}
var t = new Test(2);
t.test1();
I realize your question was not tagged with jQuery, but you are using it in your example, so my solution also utilizes jQuery.
I sometimes use the $.proxy function to avoid callback context. Look at this simple jsfiddle example. Source below.
function Test(){
this.bind();
}
Test.prototype.bind = function(){
$('input').bind('change', $.proxy(this.change, this));
// you could use $.proxy on anonymous functions also (as in your $.getJSON example)
}
Test.prototype.change = function(event){
// currentField must be set from e.target
// because this is `Test` instance
console.log(this instanceof Test); // true
console.log(event.target == $('input')[0]); // true
this.currentField = event.target; // set new field
};
function createTest(){
return new Test();
}
$(function(){ // ready callback calls test factory
var t1 = createTest();
});
Most of the time, I just declare a local variable that references this, wherever I need a reference to this in a callback:
function Foo() {
}
Foo.prototype.bar = function() {
var that=this;
setTimeout(function() {
that.something="This goes to the right object";
}, 5000);
}
Alternatively, you can use bind() like this:
Function Foo() {
this.bar = this.bar.bind(this);
// ... repeated for each function ...
}
Foo.prototype.bar = function() {
}
What this gives you is that every time you create a new Foo instance, the methods are bound to the current instance, so you can use them as callback functions for setTimeout() et al.

Is it possible to append functions to a JS class that have access to the class's private variables?

I have an existing class I need to convert so I can append functions like my_class.prototype.my_funcs.afucntion = function(){ alert(private_var);} after the main object definition. What's the best/easiest method for converting an existing class to use this method? Currently I have a JavaScript object constructed like this:
var my_class = function (){
var private_var = '';
var private_int = 0
var private_var2 = '';
[...]
var private_func1 = function(id) {
return document.getElementById(id);
};
var private_func2 = function(id) {
alert(id);
};
return{
public_func1: function(){
},
my_funcs: {
do_this: function{
},
do_that: function(){
}
}
}
}();
Unfortunately, currently, I need to dynamically add functions and methods to this object with PHP based on user selected settings, there could be no functions added or 50. This is making adding features very complicated because to add a my_class.my_funcs.afunction(); function, I have to add a PHP call inside the JS file so it can access the private variables, and it just makes everything so messy.
I want to be able to use the prototype method so I can clean out all of the PHP calls inside the main JS file.
Try declaring your "Class" like this:
var MyClass = function () {
// Private variables and functions
var privateVar = '',
privateNum = 0,
privateVar2 = '',
privateFn = function (arg) {
return arg + privateNum;
};
// Public variables and functions
this.publicVar = '';
this.publicNum = 0;
this.publicVar2 = '';
this.publicFn = function () {
return 'foo';
};
this.publicObject = {
'property': 'value',
'fn': function () {
return 'bar';
}
};
};
You can augment this object by adding properties to its prototype (but they won't be accessible unless you create an instance of this class)
MyClass.prototype.aFunction = function (arg1, arg2) {
return arg1 + arg2 + this.publicNum;
// Has access to public members of the current instance
};
Helpful?
Edit: Make sure you create an instance of MyClass or nothing will work properly.
// Correct
var instance = new MyClass();
instance.publicFn(); //-> 'foo'
// Incorrect
MyClass.publicFn(); //-> TypeError
Okay, so the way you're constructing a class is different than what I usually do, but I was able to get the below working:
var my_class = function() {
var fn = function() {
this.do_this = function() { alert("do this"); }
this.do_that = function() { alert("do that"); }
}
return {
public_func1: function() { alert("public func1"); },
fn: fn,
my_funcs: new fn()
}
}
var instance = new my_class();
instance.fn.prototype.do_something_else = function() {
alert("doing something else");
}
instance.my_funcs.do_something_else();
As to what's happening [Edited]:
I changed your my_funcs object to a private method 'fn'
I passed a reference to it to a similar name 'fn' in the return object instance so that you can prototype it.
I made my_funcs an instance of the private member fn so that it will be able to execute all of the fn methods
Hope it helps, - Kevin
Maybe I'm missing what it is you're trying to do, but can't you just assign the prototype to the instance once you create it? So, first create your prototype object:
proto = function(){
var proto_func = function() {
return 'new proto func';
};
return {proto_func: proto_func};
}();
Then use it:
instance = new my_class();
instance.prototype = proto;
alert(instance.prototype.proto_func());

Categories

Resources