4
\$\begingroup\$

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.

asked Dec 12, 2011 at 1:11
\$\endgroup\$
2
  • 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\$ Commented 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\$ Commented Dec 12, 2011 at 7:52

4 Answers 4

4
\$\begingroup\$

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.

answered Dec 12, 2011 at 1:14
\$\endgroup\$
3
\$\begingroup\$

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);
}
answered Dec 12, 2011 at 1:22
\$\endgroup\$
1
  • \$\begingroup\$ Really cool answer Alex. That's clear enough for me. Thanks \$\endgroup\$ Commented Feb 21, 2014 at 3:54
2
\$\begingroup\$

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.

answered Dec 12, 2011 at 1:19
\$\endgroup\$
1
\$\begingroup\$
// 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).

answered Dec 12, 2011 at 1:21
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.