Setting properties on functions in JavaScript object literal notation - javascript

var api = {};
api.c = function () {return 1};
api.c.m = function () {return 2};
alert(api.c()); // returns 1
alert(api.c.m()); // returns 2
var api2 = {
c: function () {}; // can't put m inside c in object literal notation
};
How would we embed m in c in object literal notation?

You can't. However, you could do
Object.defineProperty(api.c, 'm', { value: function() { return 2; } });
Since Object.defineProperty returns the object, you could do
var api = {
c: Object.defineProperty(function() { }, 'm', {
value: function() { return 2; }
})
};
Or for multiple properties:
var api = {
c: Object.defineProperties(function() { }, {
m: { value: function() { return 2; } },
...
})
};
This may come closest to satisfying your desire to write the function properties in object literal form.
Or, you could use the extend feature available in most frameworks (or Object.assign in ES6):
var api = {
c: Object.assign(function() { }, {
m: function() { return 2; }
)
};
Feel free to replace Object.assign with $.extend, _.extend, etc.
Depending on your tolerance for ugliness, you might try the following, a variation on #zerkms's proposal without the IIFE (you'll need a variable x):
var api = {
c: (x = function() { }, x.m = function() { return 2; }, x)
};

It technically is possible, but it's ugly
var api2 = {
c: (function() {
var f = function() {};
f.m = 'something else';
return f;
}())
};
So I personally don't see a good reason to do it that way instead of how you do it in the 1st case.

Related

Copy object via closure

I'm trying to get a copy of an object like this:
graphs = (function () {
var trends = {
pointSize: 10,
};
// Object Oriented JavaScript - pp 109
var lockVariable = function(x) {
return function() {
return x;
}
};
var getTrendsConfig = function() {
return lockVariable(trends)();
};
return {
getTrendsConfig : getTrendsConfig
};
}());
c = graphs.getTrendsConfig();
c.pointSize = 11;
console.log(graphs.getTrendsConfig())
I was expecting to get printed a "{pointSize: 10}"
Because the getTrendsConfig function would pass the trends object to the lockVariable function, which would return the local value object: "{pointSize : 10}", instead I get "{pointSize: 11}".
I'm taking this from an example of the book "Object Oriented JavaScript" pp 108-109:
How do I get my expected result? Is it possible? And why this doesn't work?
Primitive values, such as numbers, in JavaScript, are immutable. You can copy i to x (as per the book), change i and leave x unchanged.
Objects are not immutable and are only ever addressed by reference. When you return the value of trends (c = graphs.getTrendsConfig();), you are returning a reference to the object (so c and trends both contain a reference to the same object). When you modify it, you modify that object. Getting another copy of trends gives you another copy of the reference… which still points to the same object.
The simple way to deal with this is to move the logic that creates the object inside the function that gets called.
graphs = (function() {
var lockVariable = function(x) {
return function() {
return x;
}
};
var getTrendsConfig = function() {
var trends = {
pointSize: 10,
};
return lockVariable(trends)();
};
return {
getTrendsConfig: getTrendsConfig
};
}());
c = graphs.getTrendsConfig();
c.pointSize = 11;
console.log(graphs.getTrendsConfig())
Although you could simplify that to
graphs = (function() {
var getTrendsConfig = function() {
return {
pointSize: 10,
};
};
return {
getTrendsConfig: getTrendsConfig
};
}());
c = graphs.getTrendsConfig();
c.pointSize = 11;
console.log(graphs.getTrendsConfig())
To go with something close to what your original code is trying to acheive, you could return an explicit copy of the object by using Object.assign()
graphs = (function() {
var trends = {
pointSize: 10,
};
// Object Oriented JavaScript - pp 109
var lockVariable = function(x) {
return Object.assign({}, x);
};
var getTrendsConfig = function() {
return lockVariable(trends);
};
return {
getTrendsConfig: getTrendsConfig
};
}());
c = graphs.getTrendsConfig();
c.pointSize = 11;
console.log(graphs.getTrendsConfig())
graphs = (function () {
var trends = {
pointSize: 10,
};
// Object Oriented JavaScript - pp 109
var lockVariable = function(x) {
return function() {
return x;
}
};
var getTrendsConfig = function() {
return lockVariable(trends)();
};
return {
getTrendsConfig : getTrendsConfig
};
}());
c = Object.assign({}, graphs.getTrendsConfig()); // same as ...graphs.getTrendsConfig() in ES6 spread syntax
c.pointSize = 11;
console.log(graphs.getTrendsConfig());
console.log("c =", c);
It's not producing your expected result because you re-assigned c.pointSize to 11 and that's why you're getting 11;
In JavaScript, assigning an object to a variable is done by reference and not by value. This means that you're simply copying over the object's location in memory, causing any modification to affect the original value.
In your example when you assign c = graphs.getTrendsConfig();, c will now point to the same object's location/address.
When you did this c.pointSize = 11, you modified the same (original) object and not a copy.
Solution:
In other to make a copy of graphs.getTrendsConfig() you could use Object.assign() or the new ES6 spread syntax .... By making a copy you won't be modifying the original object's pointSize variable.

Defining getters and setters after the object is instantiated

This bit of code comes from MDN:
var o = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2
}
};
$('body').append(o.a + '<br>')
$('body').append(o.b + '<br>')
o.c = 50;
$('body').append(o.a + '<br>')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
What I would like to do is break it down even further. I've gotten as far as line 3 like so:
var o = {}
o.a = 7
o.get b() { // broken
Is there a way that I can break the getters and setters out so that it's not one long definition inside the o = {}?
Object.defineProperty is what you're looking for:
var o = {};
Object.defineProperty(o, 'a', {
get: function() { return 1; },
set: function(val) { console.log(val) }
});
You can achieve this using Object.defineProperty():
Object.defineProperty(o, "b", {
get: function() {
// return something
}
})
See demo below:
var o = {};
o.a = 1;
Object.defineProperty(o, "b", {
get: function() {
return this.a + 1;
}
});
console.log(o.b)

jQuery extend multiple objects with the same function

So I'm using $.extend to combine multiple components (objects). Some of these components have a function with the same key. I want the final extended object to have that same key, but have it point to a function which calls all of the merged components' versions of the functions one after the other.
So it'd look something like this:
var a = { foo: function() { console.log("a"); } };
var b = { foo: function() { console.log("b"); } };
var c = {}; // Doesn't have foo
var d = $.extend({}, a, b, c);
var e = $.extend({}, a, c);
var f = $.extend({}, c);
d.foo(); // Should call function() { console.log("a"); console.log("b"); }
e.foo(); // Should call function() { console.log("a"); }
f.foo(); // Should call function() {}
Is there a pragmatic way of doing this? I only want to do this for a specific set of keys, so I would only want to merge those specific keys' functions together and let the ordering in extend overwrite anything else.
Hopefully that makes sense :S
Note
f.foo(); // Should call function() {}
object c does not appear to have property foo . callling f.foo() returns TypeError: undefined is not a function . Not certain if requirement to add foo function to extended f object , or return object c (empty object) from anonymous function ? At piece below , foo function not added to extended f object.
jquery $.Callbacks() utilized to add functions having foo property at $.each()
Try
var a = { foo: function() { console.log("a"); } };
var b = { foo: function() { console.log("b"); } };
var c = {}; // Doesn't have foo
//d.foo();
// Should call function() { console.log("a"); console.log("b"); }
//e.foo();
// Should call function() { console.log("a"); }
//f.foo();
// Should call function() {}
var callbacks = $.Callbacks();
var arr = [], d, e, f;
$.each([a,b,c], function(k, v, j) {
var j = [a,b,c];
// filter objects having `foo` property
if (v.hasOwnProperty("foo")) {
arr.push([v, v.foo]);
if (arr.length > 1) {
callbacks.add(arr[0][1], arr[1][1]);
// `add` `foo` properties to `callbacks`
// `fire` both `callbacks` when `object.foo` called
j[k -1].foo = callbacks.fire;
d = $.extend({}, j[k - 1])
} else {
// `else` extend original data (`fn`, `object`)
// contained within object
e = $.extend({}, j[k + 1]);
f = $.extend({}, j[++k + 1]);
}
}
});
d.foo(); // `a` , `b`
e.foo(); // `b`
console.log(f); // `Object {}`
f.foo() // `TypeError: undefined is not a function`
jsfiddle http://jsfiddle.net/guest271314/3k35buc1/
See jQuery.Callbacks()
Here's what I ended up with based off of guest271314's answer. toMix is the array of components to be mixed into the object. I actually didn't need the dummy functions I thought I might, and I ended up using an array of functions instead of the $.Callbacks() so that I could control the order in which the functions are called. I also needed to use the call() function so that I could call the functions from the correct this object.
this.functionMerge = function(toMix) {
var callbacks = {};
var functions = {};
var obj = {};
var keys = [
'componentWillMount',
'componentDidMount',
'componentWillUpdate',
'componentDidUpdate',
'componentWillUnmount'
]
$.each(keys, function(key, value) {
callbacks[value] = [];
});
for (i = 0; i < toMix.length; ++i) {
$.each(keys, function(key, value) {
if (toMix[i].hasOwnProperty(value) && typeof toMix[i][value] == 'function') {
callbacks[value].push(toMix[i][value]);
}
});
$.extend(true, obj, toMix[i]);
}
$.each(keys, function(key, value) {
functions[value] = function() {
var that = this;
$.each(callbacks[value], function(key, value) {
value.call(that);
});
};
});
return $.extend(true, obj, functions);
}

How to Extend javascript object methods

I have this object
var X = {
a:function(args){
},
b:function(args){
},
c: ...... etc
}
X.a() // will do somthing
I want to modify the method that it will do one more statement say "counter++"
So
var oldX = X.a();
X.a =function(args){
oldX(args);
counter++;
}
if i want to make this change to all the methods of the object X . So how can i do this by looping through the object like?
for(var i in X){
}
when i tried it by storing in an ooldArray of methods but while executing it will replace all the method with lastone ..
I'm completely sure the behavior you want should be achieved using prototypical inheritance. But I'm know nothing about your architecture and requirements, so here is the solution written in the way you want:
var x = {
a: function(){
console.log('x.a')
},
b: function(){
console.log('x.b')
}
},
oldx = {},
key;
for (key in x) {
oldx[key] = x[key];
(function(key){
x[key] = function(){
oldx[key].apply(this, arguments);
console.log('x.new_'+key);
}
})(key);
}
x.a();
x.b();
Here is what you want:
var x = {
a:function(){
return "this is a";
},
b:function(){
return "this is b";
},
c:function(){
return "this is c";
}
}
for(var abc in x)
{
alert(abc);
}

'this' context during object creation

I am trying to do something like this:
var test = {
a: 10,
b: 20,
c: (this.a+this.b)
};
but it doesn't work. How can I access the test.a from within test.c?
Is it possible?
It's not possible to reference "this" in an expression specifying an object literal. Either do it in a following line or use a constructor like this:
function myobj(a,b) {
this.a = a;
this.b = b;
this.c = this.a + this.b;
}
var test = new myobj(10,20);
In response to which method is faster, creation with the object constructor is faster. Here's a simple test case comparison. Run it yourself on JSBIN.
The results show that the object creation with a constructor vs an object literal is almost twice as fast:
0.450s : testObjectLiteral
0.506s : testObjectLiteralWithFunction
0.280s : testConstructor
Here's the test code inlined as well:
// timer function
function time(scope){
time.scope = time.scope || {};
if(time.scope[scope]) {
var duration = (new Date()).getTime()-time.scope[scope];
time.scope[scope] = null;
var results = document.getElementById("results");
results.innerHTML = results.innerHTML + '<p>'+(duration/1000).toFixed(3)+'s : '+scope+'</p>';
} else {
time.scope[scope] = (new Date()).getTime();
}
}
// object creation function with constructor
function myobj(a,b) {
this.a = a;
this.b = b;
this.c = this.a + this.b;
}
function testConstructor(iterations) {
var objs = new Array(iterations);
for(i=0;i<iterations;i++) {
objs[i] = new myobj(i,i+1);
}
return objs;
}
function testObjectLiteralWithFunction(iterations) {
var objs = new Array(iterations);
for(i=0;i<iterations;i++) {
objs[i] = {
a: i,
b: i+1,
c: function() {
return this.a + this.b;
}
};
}
return objs;
}
function testObjectLiteral(iterations) {
var objs = new Array(iterations);
for(i=0;i<iterations;i++) {
var item = {
a: i,
b: i+1
};
item.c = item.a + item.b;
objs[i] = item;
}
return objs;
}
var ITERATIONS = 1000000;
time("testObjectLiteral");
testObjectLiteral(ITERATIONS);
time("testObjectLiteral");
time("testObjectLiteralWithFunction");
testObjectLiteralWithFunction(ITERATIONS);
time("testObjectLiteralWithFunction");
time("testConstructor");
testConstructor(ITERATIONS);
time("testConstructor");
​
It's not possible within an object literal since this cannot be made to refer to an object that has not yet been created. Your best option is to assign the c property in a separate step:
var test = {
a: 10,
b: 20
};
test.c = test.a + test.b;
You simply can't do this when declaring an object literal, the closest you can do is:
var test = {
a: 10,
b: 20
};
test.c = test.a + test.b;
In your context this refers to whatever parent context you're in, not the test object...and even if it did, you can't declare members like that, for example this is also invalid:
var test = { a: 10, b: 20, test.c: test.a + test.b };
...because test, a and b aren't defined yet, since it's a single statement that hasn't completed.
Why not make c a function so that it always returns the current value of a+b?
var test = {
a: 5,
b: 1,
c: function() {
return this.a + this.b;
}
}

Categories

Resources