How come JavaScript {this} doesn't reference the parent object? - javascript

When making objects I assumed that this would return the object's instance - instead it seems it's something else. Why is this?
var Obj = {
foo : 'value',
bar : function() { return this.foo; } // Error
bar : function() { return Obj.foo; } // Works
}
Update: I must be missing something because in some cases using this inside objects doesn't work. How come this only references the object instance sometimes?

It does. You have a syntax issue.
Use commas to delimit object members
var Obj = {
foo : 'value',
bar : function() { return this.foo; },
}
within member functions this refers to a reference to the object that the function is called on.
jsFiddle Example

Within a JavaScript function this is set depending on how the function was called.
With your example Obj, assuming you correct the syntax error and use commas between properties:
var Obj = {
foo : 'value', // needs comma, not semicolon
bar : function() { return this.foo; }
}
If you use the "dot" syntax on Obj to call the bar function then this will be automatically set to Obj:
Obj.bar(); // this will be Obj
That's an easy way to ensure this ends up set the way you want. But if you call the function in some other way this could be set to something else:
var nonObjBar = Obj.bar; // get a reference to the function
nonObjBar(); // this will (probably) be `window`, but depends if
// in strict mode or inside some other function, etc
var Obj2 = { foo: "other foo" };
Obj.bar.call(Obj2); // the .call() method sets this to Obj2
// so bar will return Obj2's foo, "other foo"
That last example uses the .call() method on Obj.bar to invoke the function in a way that allows you to set this to anything you like (strict versus non-strict mode affects the way this works for some cases).
It might seem strange if you're coming from languages like Java, but JavaScript functions don't belong to any given object. This behaviour is pretty well defined and on purpose.

After fixing the semi-colon after 'value', this seems to work:
var Obj = {
foo : 'value',
bar : function() { return this.foo; } // Error
};
alert(Obj.bar()); // alerts 'value'
See working jsFiddle here: http://jsfiddle.net/jfriend00/UFPFf/.
When you call a method on an object, the javascript engine sets the this pointer to point to the object for the duration of the method call.

You shouldn't have a semicolon after foo.
Actually when you call Obj.bar you will have this referring to Obj.
See http://jsfiddle.net/AAkbR/
var Obj = {
foo : 'value',
bar : function () { return this.foo; }
};
alert(Obj.bar() === Obj.foo);
Alerts true.

Related

What is the difference between offering access to an API through the context or offering it through a parameter?

Given the following API:
SomeAPI = (function() {
var secretKey = 'passw0rd',
foo = 'foo',
bar = 'bar';
return {
use : function(callback) { /* Provide the API to the callback */ }
};
})();
It consists of a couple values and most importantly is used through its use() public method, which allows users to consume the API through a callback.
The callback can access the API in one of two ways:
1) Through this
callback is either called or appl[i]ed. Consuming the API is done with this:
SomeAPI.use(function() {
doStuffWith(this.foo);
});
2) Through a parameter
callback is simply invoked with a parameter containing the API. Consuming the API is done with the given parameter:
SomeAPI.use(function(api) {
doStuffWith(api.bar);
});
I've seen both patterns used. Is there any practical difference between the two? Something possible with one but not the other?
Is there any practical difference between the two? Something possible with one but not the other?
I'm sure there are many, but I'll detail the most important differences as far as I can tell.
For the first example, assign the variables to a private object, then assign the private object to the value of this for the callback.
The good part about this is that we are actually passing a reference to the private object, so we can alter that object.
The bad part about this is that we are actually passing a reference to the private object, so users can alter that object willy nilly, which is never a good thing.
SomeAPI = (function() {
var private = {};
private.secretKey = 'passw0rd';
private.foo = 'foo';
private.bar = 'bar';
return {
use : function(callback) { callback.call(private); }
};
})();
console.log(SomeAPI.private);
// undefined
SomeAPI.use(function(){
console.log(this.secretKey, this.foo, this.bar);
// passw0rd foo bar
console.log(this.constructor);
// Object() { [native code] }
console.log(this);
// Object {secretKey: "passw0rd", foo: "foo", bar: "bar"}
this.hello = 'world';
this.foo = 'bar';
this.bar = 'foo';
});
SomeAPI.use(function(){
console.log(this.secretKey, this.foo, this.bar, this.hello);
// passw0rd bar foo world
console.log(this);
// Object {secretKey: "passw0rd", foo: "bar", bar: "foo", hello: "world"}
});
Let's take stock of what this shows us.
The private object is in fact private.
this refers to the private object.
this contains all of the private properties.
Any changes to the private object persist to the next API call.
For the second example, we pass the values of each private property to the callback.
The good part about this method is the downfall of the previous method. This method locks everything down so only what you explicitly allow to be changed/accessed can be changed/accessed.
The bad part about this method is that you're locked into what is defined by the API.
OtherAPI = (function() {
var secretKey = 'passw0rd',
foo = 'foo',
bar = 'bar';
return {
use : function(callback) { callback(secretKey, foo, bar); },
setFoo : function(val) { foo = val },
setBar : function(val) { bar = val }
};
})();
console.log(OtherAPI.foo);
// undefined
OtherAPI.use(function(secretKey, foo, bar){
console.log(secretKey, foo, bar);
// passw0rd foo bar
console.log(this.constructor);
// Window() { [native code] }
foo = 'bar';
bar = 'foo';
console.log(secretKey, foo, bar);
// passw0rd bar foo
});
OtherAPI.use(function(secretKey, foo, bar){
console.log(secretKey, foo, bar);
// passw0rd foo bar
});
OtherAPI.setFoo('bar');
OtherAPI.setBar('foo');
OtherAPI.use(function(secretKey, foo, bar){
console.log(secretKey, foo, bar);
// passw0rd bar foo
});
Ok, so what does this show us?
The private properties are in fact private
We can read the values of the private properties via the supplied arguments only.
Any changes made to the callback's arguments do not persist to the next API call.
If we define functions inside the returned API object to change the private property's values we can change individual values. These changes do persist to the next API call.
And finally one more example that is, sort of, a compromise between the two and, if done right, can be more versatile than either of the previous examples.
The good part about this method is that you can protect what you want to protect, and choose how the user can access the private variables.
The bad part about this method is that you really have to think about how the user might try to abuse the get and set functions in order
OtherAPI = (function() {
var private = {};
private.secretKey = 'passw0rd';
private.foo = 'foo';
private.bar = 'bar';
private.noaccess = 'No Access!';
return {
use : function(callback) { callback.call(this); },
get : function(prop) {
if(private[prop] && prop != 'noaccess') return private[prop];
return false;
},
set : function(prop, val) {
if(private[prop] && prop != 'noaccess') return (private[prop] = val);
return false;
},
noaccess : function() { return private.noaccess }
};
})();
console.log(OtherAPI.secretKey);
// undefined
console.log(OtherAPI.get('secretKey'));
// passw0rd
OtherAPI.use(function(){
console.log(this.get('secretKey'), this.get('foo'), this.get('bar'), this.get('noaccess'), this.noaccess());
// passw0rd foo bar false No Access!
console.log(this.constructor);
// Object() { [native code] }
// The above object is actually the public scope, not the private scope
this.set('foo', 'bar');
this.set('bar', 'foo');
this.hello = 'world'; // This will be in the public scope
console.log(this.get('secretKey'), this.get('foo'), this.get('bar'), this.get('noaccess'), this.noaccess(), this.hello);
// passw0rd bar foo false No Access! world
});
OtherAPI.use(function(secretKey, foo, bar){
console.log(this.get('secretKey'), this.get('foo'), this.get('bar'), this.get('noaccess'), this.noaccess(), this.hello);
// passw0rd bar foo false No Access! world
});
What does this example show us?
Same as before, private is private.
this refers to the public object (the object returned to OtherAPI)
We can now set or get any of the private properties, as long as they currently exists and we haven't explicitly denied access to that variable.
You can also further control what private properties can be set or retrieved, and how that happens.
This means that your API could be more secure than the first example, while having more flexibility than the second example and, with added security checks in the set and get functions, it can be as secure or more secure than the second example.
In the end it all depends on how much you trust the users of your API, and how much you want to protect the private properties.
In functions the this object can refer to anything from a multitude of objects. It all comes down to how the given function is called, and is exactly what the first argument to apply() is.
Consider the following example:
var fn = function (api) {
doStuffWith(this.foo);
doStuffWith(api.foo);
};
Both doStuffWith calls receive the same argument, the same string object when you invoke fn in the following way:
SomeAPI.use(function(api) {
fn.apply(api, [api]);
});
Other than this (no pun intended), it is entirely up to the API naming conventions and the best applicable logic. Both versions point (refer) exactly to the same object, use what results in more verbose and clean-looking code.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

JavaScript: Add prototype methods to inner class

I want to add a method to the prototype of an inner class. Is that possible?
This is what I tried:
var Foo = function Foo() {};
Foo.prototype = {
Bar: function Bar() { },
Bar.prototype: { // <- this does not work
barMethod: function () {
console.log('hello');
}
},
fooMethod: function () {
var bar = new this.Bar();
bar.barMethod();
}
}
var foo = new Foo();
foo.fooMethod();
But Bar.prototype doesn't work, it throws a SyntaxError with the message Unexpected token ..
Your problem is that you're defining the Foo.prototype object, and as such, you're bound to the rules and syntax of object creation, for example the parser expects you to list the attributes in a key: value, nextkey: nextvalue format, which Bar.prototype does not fit. (Keys are effectively strings, and it doesn't make sense to use . in them, as they would create ambiguity while parsing*)
Try this:
var Foo = function Foo() {};
Foo.prototype.Bar = function Bar() { };
Foo.prototype.Bar.prototype = {
barMethod: function () {
console.log('hello');
}
};
There's a slight semantic difference though, as this way you're not overriding the prototype, just extending it. (consider equaling it to {} first, then extending it with every attribute of the object you tried to create)
(*) A note on ambiguity: I mention above that having a . in your object key would create ambiguity, here's a simple example:
var foo = {
bar: {
baz: 0,
qux: 20
},
bar.baz: 150 //note that this will throw an error
};
console.log(foo.bar.baz);
If this code above wouldn't throw an error, what would you expect console.log(foo.bar.baz) to print, 0 or 150?
That's why it doesn't make sense to use . in a key, and that's why the parser throws the unexpected token error on any ..
Of course, you could use the "bar.baz" string as a key as "bar.baz": 150 above (please don't!), but then you'd have to reference the value as
foo["bar.baz"]
which would be distinctly different from
foo.bar.baz;
All in all, this is just some intuition-based reasoning behind why you can't use a . in your keys, but the real reason is plainly this: because the parser will throw an error.
In an object literal you can only define properties of that object literal, but not properties of the values.
However, if you want to set the prototype when creating the object, consider Object.assign (which can be polyfilled):
Foo.prototype = {
Bar: Object.assign(function Bar() { }, {
prototype: {
barMethod: function () {
console.log('hello');
}
}
}),
fooMethod: function () {
var bar = new this.Bar();
bar.barMethod();
}
};
However, note that replacing all the prototype is a bad practice because you erase the constructor property.

Why can't we add properties to a function object inside function body like we do for object literals in javascript?

Look at the code below
var foo = function(){
prop1 : "value";
}
foo.prop2 = "value";
console.log("prop1" in foo) // logs flase
console.log("prop2" in foo) // logs true
Why can't we add properties to a function object like we do in an object literal? Why had I to declare the property for the function object outside its body? Is there a different syntax to add properties to a function object directly in its body.
That's because the grammar of functions in javascript doesn't allow for it. What you need is just a plain object.
Is there a different syntax to add properties to a function object
directly in its body.
Yes. There is. And it's called a constructor function.
function Foo(){
this.prop1 = "value";
}
var obj = new Foo();
console.log(obj.prop1);
Are you using something akin to CoffeeScript? You forgot the {} on the object.
First I suggest you return a proper object:
var foo = function(){
{ prop1 : "value" };
}
Still it won't work. It will result in:
foo.prop2 = "value2";
{ [Function] prop2: 'value2' }
foo() will return
{ prop1: 'value' }
foo.prop2 will return value2
The reason it doesn't work is because you're adding to the instance. Not to the function body (You can't change the code, just the objects.).
A better solution would be:
var newFoo = foo()
{ prop1: 'value' }
newFoo.prop2 = "value2";
{ prop1: 'value', prop2: 'value2' }
As we've seen, you CAN modify the function, but you can't modify it's return statement, because it would require the software rewriting the function and we're not quite skynet... Yet. ;D
Another interesting way of modifying stuff is prototyping:
Function.prototype.prop2 = "value2"
foo()
{ prop1: 'value' }
foo.prop2
'value2'
But seeing as Function.prototype will make EVERY FUNCTION gain the .prop2 property, it wouldn't make much sense in this case.
Because you have used a semi colon. You do it using a comma.
Correct usage would be:
var foo = {
prop1 : "value",
prop2 : "value"
}

Run code on variable initialization

I just learned this way of initializing a variable in javascript:
var MyGlobalVar = {
Property1 : 1,
Property2 : "Two"
}
Which is pretty neat because is a lot like a static object. But is there any way I can run initialization code inside it, kinda like a static constructor would do?
How about using eval, such as Property1 : eval("[code to run here...]");?
I just don't want to put any initialization or part of my object's definition outside the object itself (outside the brackets).
Edit: My initialization code would actually return a filled up array, so Property2 would be this array. But I don't want to recreate this array every time is needed.
If I understood what you want:
var MyGlobalVar = {
Property1 : 1,
Property2 : (function(){ var arr=["this", "is", "an", "array"];return arr; })()
}
Property2 will be an array...you can have any code after that, and whatever you return will be its value.
EDIT: I changed the above code to return an array.
If you want a constructor, why not use a constructor?
var MyGlobalVar = new function() {
// run whatever code you want in here
// The new object being constructed is referenced via "this"
this.Property1 = 1;
this.Property2 = "Two";
};
This code uses an anonymous function as the constructor. You can run whatever code you want inside. The new object is referenced by this, and it is returned automatically, so MyGlobalVar will look like:
{
Property1: 1,
Property2: "Two"
}
Another approach, and one that is perhaps more common than using an anonymous constructor, is to use a module pattern. It is very similar, but it doesn't use the function as a constructor by calling it with new, and it just returns an object literal.
The above code would be rewritten like this:
var MyGlobalVar = (function() {
// run whatever code you want in here
// The new object is an object literal that we create and return
var _new_obj = {
Property1: 1,
Property2: "Two"
};
return _new_obj;
})();
Just like the first example, it gives you an enclosed environment where you can run whatever code you need in creating the values to be referenced by your object.
The end result is practically identical. The only real difference is that the prototype chain of the object in the first example looks like:
_new_obj ---> {} ---> Object.prototype ---> null
whereas the prototype chain in the new version looks like:
_new_obj ---> Object.prototype ---> null
So there's a slightly shorter chain. It won't make such a difference that you should base any decision on it.
Rather than use eval, you could run an immediate function:
var v = {
prop1: function() {
// whatever
return aResult;
}()
}
Or a previously-defined function:
function foo() {
// whatever
return aResult;
}
var v = {
prop1: foo(),
}
Best to avoid eval if not strictly needed.
If you really want everything inside the object, you can run a function which you also define inside the object literal, which does the following:
runs initialization stuff
returns the value that the property should have
Something like:
var data = {
prop: (function() {
// init
return "foo";
})(),
prop2: (function() {
// init
return "bar";
})()
};

How the JavaScript this keyword works?

I have been doing some JavaScript development and I encountered this problem. Consider the following code
var obj = {};
obj.bind = function () {
console.info(this);
};
obj.bind();
I'm running the code on the FireBug JavaScript console. The expected result is that this displays a reference to the object in the console.
It actually displays undefined.
But, when I make this change to my code
var obj = {};
obj.bind = function () {
this.x = 'x';
console.info(this);
};
obj.bind();
Now the console displays the expected value of this, which is a reference to the obj object.
Why does this happen?
undefined is the return value of the function, which you'll get because you're not returning a value explicitly.
In both Chrome and Firebug, it correctly displays the Object in the console before the return value undefined.
So if you do:
var obj = {};
obj.bind = function () {
console.info(this);
return "foo";
};
obj.bind();
...you should see something like:
Object { }
"foo"
If Firebug is not displaying the Object when it is empty, you may need to check to make sure you're using the most current version.
In your example, "this" should be obj, just as some commenters pointed out. Here are the details that explain why --
In Javascript, the value of "this" changes depending on how you call the function:
When a function is stored as a property on an object, and you invoke that function by calling obj.foo(), "this" will be obj.
EX:
var obj = {
x: 1,
increment: function() {
this.x = this.x + 1;
};
obj.increment(); // Makes "this" be obj
When you invoke a function using syntax that does not refer to any owning object, "this" will be the global environment.
EX:
function someFunc(a, b) {
return a + b; // If you were to refer to "this" here, it would be the global env.
}
someFunc(5, 6);
When you invoke a function as if it were a constructor by using the new operator, a new object will be instantiated for you, and "this" will point to that new object.
EX:
function SomeConstructor() {
this.x = 42; // under the hood, a new object was instantiated for you, and "this" was set to it.
}
var result = new SomeConstructor(); // note the "new" keyword
// result will be { x:42 }
When you use call() or apply(), you can control what "this" is.
(No example here since it's fairly far from your question. Look up docs for apply() or call() for an example.)

Categories