I have 2 questions about functions inside object literals. I have included the workarounds I've been using, but they seem hackish. Is there a better way to do this?
ns = {
a: function (x, y) { return x+y; },
// Problem 1
// This does not work: Error: ns.b is not a function
b: this.a,
// Is this the correct workaround for this?
c: function (x, y) { return this.a(x, y); },
// Problem 2
// I understand why this won't work, because a is not defined in d's scope
d: function (x, y) {
function add(n, o) {
return this.a(n, o);
}
return add(3, 4);
},
// Is this the correct workaround for this? It works in Firefox and Chrome.
e: function (x, y) {
function add(n, o) {
return ns.a(n, o);
}
return add(3, 4);
}
};
ns.a(3, 4); //===> 7
ns.b(3, 4); //===> Error: ns.a is not a function
ns.c(3, 4); //===> 7
ns.d(3, 4); //===> Error: no method named a
ns.e(3, 4); //===> 7
EDIT: Thank you for all your replies. As missingno pointed out I am using ns to define a module, though I avoided the module pattern because I don't have any private variables. The following seems to be an elegant solution to my problems.
var ns = {};
ns.a = function (x, y) { return x+y; };
ns.b = ns.a;
ns.c = function (x, y) {
function add(n, o) {
return ns.a(3, 4);
}
return add(x, y);
}
ns.a(3, 4); //===> 7
ns.b(3, 4); //===> 7
ns.c(3, 4); //===> 7
Since this has been moved to Code Review, I'd welcome any critiques of my implementation of Scheme's SRFI-1.
-
2\$\begingroup\$ Why was this moved to code review? It's a technical question about javascript scope and context with an extremely contrived example. \$\endgroup\$Alex Wayne– Alex Wayne2011年12月12日 01:29:16 +00:00Commented Dec 12, 2011 at 1:29
-
\$\begingroup\$ Indeed; this is an SO question through-and-through. It's not "can you improve my program"; it's "what programming technique am I supposed to use?" \$\endgroup\$Lightness Races in Orbit– Lightness Races in Orbit2011年12月12日 07:52:45 +00:00Commented Dec 12, 2011 at 7:52
4 Answers 4
Problem 1
Just assign them one at a time, once the object already exists:
ns = {};
ns.a = function (x, y) { return x+y; };
ns.b = ns.a;
To answer specifically whether there's any other workaround if you're dead set on declaring these inside the object literal syntax: no.
Problem 2
Looks good enough to me, though I'd try to avoid using the object's name so deep into its definition.
Consider using prototyping instead.
Problem 1
When creating an object literal, this
scoped by whatever function is constructing the object. This allows you to do:
this.foo = 'bar';
var obj = { foo: this.foo };
console.log(obj.foo); // 'bar'
In fact the object doesn't exist until the end of the object literal, so there is no object to reference.
The correct work around for you issue would be like so. Because you cannot use the same value for multiple keys in an object literal.
ns = { a: function() {} };
ns.c = ns.a; // the same function now is on .a and .c
Problem 2
This has nothing to do with scope
, but instead context
(the value of this
). In function invocations, the dot syntax is what provides the this
. For example:
obj.someFn() // inside someFn: this === obj
It's the syntax that provides this
. So when there is no dot accessor, this
defaults to the global object (window
in a browser).
The usual work around is to simply save a reference to the current object which is shared with any functions in the same scope.
d: function(x, y) {
var self = this;
var add = function(x, y) {
self.a(x, y);
};
add(3, 4);
}
Or use call
or apply
to explicitly set the function context, which overrides the default binding of this
with whatever object you want.
d: function(x, y) {
var add = function(x, y) {
this.a(x, y);
};
add.call(this, 3, 4);
}
-
\$\begingroup\$ Really cool answer Alex. That's clear enough for me. Thanks \$\endgroup\$thangchung– thangchung2014年02月21日 03:54:24 +00:00Commented Feb 21, 2014 at 3:54
1) The normal workaround for duplicating functions is just using a variable
var f = function(){};
var ns = {
a: f,
b: f
},
2) and the normal workaround for nested this
is using an intermediate variable
var that = this;
var f = function(){
that.a();
}
But... your ns variable name makes it look like you are using your object literal to define a module. If that is the case, consider instead using a more general module pattern so you are less constrained in the things you can do.
//this is just an example. There are many variations on this theme...
var ns = (function(){
var M = {};
var a_private_f = function(){ /*...*/ };
M.a = function(){
a_private_f();
}
M.b = function(){
M.a();
}
return M;
}());
Avoiding the use of this
to reference other functions in your namespace also allows your functions to be passed around as callbacks, in situations where the this
will not point to the namespace object.
// I understand why this won't work, because a is not defined in d's scope
Incorrect. As you wrote it, d's scope is irrelevant in the add
function because you've called it without a scope object, i.e. add(3, 4)
. If you want the scope in add to be the same as the scope in d, you should use add.call(this, 3, 4)
.