jQuery extend multiple objects with the same function - javascript

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);
}

Related

How do I call a JavaScript method based on a variable value?

I have method a(), method b(), and method c(). I will get a response message from server, which contains a or b or c and so on.
If the response message is a, then I need to call method a().
If the response message is b, then I need to call method b()
And so on...
I don't want to write any if else conditions or switch case to identify the method.
I don't want to do this:
if(res == 'a')
a();
else if(res == 'b')
b();
Instead of that I need something like reflections in Java.
If you have defined the function in Global/window Scope then you can directly use res variable
window[res]();
Otherwise define the function in object and then use it
var obj = {
a : function(){},
b : function(){}
}
obj[res]();
You could use an object and store the function inside, like
var functions = {
a: function () {},
b: function () {},
c: function () {}
default: function () {} // fall back
}
Usage:
functions[res]();
Or with default
(functions[res] || functions.default)();
For this purpose you can define a class that allows you to define and call methods, and determine the calling context:
var MethodsWorker = function () {
this._context = window;
this._methods = {};
}
MethodsWorker.prototype.setContext = function (context) {
this._context = context;
}
MethodsWorker.prototype.defineMethod = function (name, method) {
this._methods[name] = method;
};
MethodsWorker.prototype.invoke = function (methodName, args) {
var method = this._methods[methodName];
if (!method) { throw {}; }
return method.apply(this._context, args);
};
Usage:
var methodsWorker = new MethodsWorker ();
methodsWorker.setContext(Math);
methodsWorker.defineMethod('sqrtOfSum', function() {
var sum = 0;
for (var i = 0, n = arguments.length; i < n; i++) {
sum += arguments[i];
}
return this.sqrt(sum);
});
var result = methodsWorker.invoke('sqrtOfSum', [1, 2, 3]);
alert (result);

Javascript: How to get key of function.apply()

I am trying to cache 'func.apply(this, func)' value so that it can be looked up later rather than running the function again. The problem is that I don't know how or what to use as the key.
Is there a way to assign an key of a function that can be looked up later?
Code example:
var m = function(func) {
var cached = {};
return function() {
var key = ''; // how do I get or create the key of func.apply(this, func)?
if (cached[key]) {
return cached[key];
}
cached[key] = func.apply(this, arguments);
return cached[key];
};
};
The m() function should return a function that, when called, will check if it has already computed the result for the given argument and return that value instead if possible.
What are you looking for is called Memoization
See: Implementing Memoization in JavaScript
Here are an example:
var myFunction = (function() {
'use strict';
var functionMemoized = function() {
// set the argumensts list as a json key
var cacheKey = JSON.stringify(Array.prototype.slice.call(arguments));
var result;
// checks whether the property was cached previously
// also: if (!(cacheKey in functionMemoized.cache))
if (!functionMemoized.cache.hasOwnProperty(cacheKey)) {
// your expensive computation goes here
// to reference the paramaters passed, use arguments[n]
// eg.: result = arguments[0] * arguments[1];
functionMemoized.cache[cacheKey] = result;
}
return functionMemoized.cache[cacheKey];
};
functionMemoized.cache = {};
return functionMemoized;
}());
Why do you need an object with an index. Just store the result/key.
var m = function(func) {
var result=null;
return function() {
if (result===null) {
result = func.apply(this, arguments);
}
return result;
}
};
But I am not sure that is what you want. If the function returns different values based on arguments, than you want to use a key based off the arguments.
var m = function(func) {
var results = {};
return function() {
var key = [].slice.call(arguments).join("-");
if (results[key]===undefined) {
results[key] = func.apply(this, arguments);
}
return results[key];
}
};
var multiply = function (a,b) {
return a * b;
}
var mult = m(multiply);
console.log(mult(2,5)); //runs calculation
console.log(mult(2,5)); //uses cache
If you send the value of the function as a string, you can use that as the index with one minor modification
var m = function(func, scope) {
return function() {
var cached = {};
var index = func; // how do I get or create the index of func.apply(this, func)?
scope = scope || this;
if (!cached[index]) {
func = scope[func]; //Get the reference to the function through the name
cached[index] = func.apply(this, func);
}
return cached[index];
};
};
This does depend on if the index exists in the this object reference. Otherwise you should use a different scope.

Why is the JSON stringification of a subclassed array an object?

/* StackOverflow needs a console API */ console.log = function(x) { document.write(x + "<br />"); };
B = function() {}
B.prototype = Array.prototype;
var a = new Array();
var b = new B();
a[0] = 1;
b[0] = 1;
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));
JSON stringifies the subclass as an object ( { "0": 1 } ) instead of as an array ( [1] )`
Is there any way to modify this behaviour?
EDIT
I'm using (non-negotiably) ES5. I've simplified the example slightly. In reality, the subclassing is set up through a function inherit() which does this:
var inherit = function(base, derived) {
function F() {}
F.prototype = base.prototype;
derived.prototype = new F();
derived.prototype.constructor = derived;
};
As far as I know, you cannot inherit from array. As soon as you create a constructor function, the instances of it will be objects. When you want the functionality of an array, rather create an array and add the methods on it you want. This can be done with a function:
function createExtendedArray () {
var a = [];
a.method1 = function() {};
return a;
}

Extending an object by reference

I have an extend method that extends objects like you would expect it would in JavaScript:
var objA = {a: true};
var objB = extend({b:false}, objA);
console.log(objB); // {a: true, b: false}
However, I would like to extend the source's (objA) properties onto the target (objB) object by reference so that any changes made to the source are reflected in the target after the fact, like so:
var objA = {a: true};
var objB = extend({b: false}, objA);
console.log(objB); // {a: true, b:false}
objA.a = false;
console.log(objB); // {a: false, b:false}
For instance, when you modify an object (which is always assigned by reference) things are working the way I would like them to:
var objA = {A:{a: true}};
var objB = _.extend({b: false}, objA);
console.log(objB) // {A:{a:true}, b:false};
objA.A.a = false;
console.log(objB); // {A:{a:false}, b:false};
So in other words, when changes are made to objA's non-object literal properties (or any value that isn't assigned by reference) I would like those changes to be reflected in objB.
I'm fairly sure this isn't exactly possible without an additional helper method that would depend on some sort of object watch method that is triggered whenever an object changes.
Thoughts?
Code from my extend method:
(l).extend = (l).merge = function () {
var args = (l).fn.__args(arguments, [{
deep: 'bool'
}, {
'*': 'obj:object'
}
]),
target = (l)._object || args.obj,
keys = [],
obj, objs, copy, key, i, o;
// Collect potential objects to merge
objs = (l).filter(args, function (value, index) {
if (index !== "obj" &&
(l).isPlainObject(value) && !((l).isEqual(target, value))) {
return value;
}
});
// When target object is not selected globally
if (!args.obj) {
target = objs.shift();
}
// When target object is not selected globally and only a single object
// is passed extend the library itself
if (!(l)._global && !objs.length) {
target = this;
objs[0] = args.obj;
}
// When a boolean is passed go deep
if (args.deep) {
// Build property reference used to prevent never ending loops
(l).each(objs, function (index, value) {
keys.push((l).keys(value));
keys = (l).flatten(keys);
});
// Add properties to all nested objects
(l).deep(target, function (depth, index, obj, ref) {
if ((l).indexOf(keys, index) === -1) {
for (i = 0; i < objs.length; i++) {
for (key in objs[i]) {
if ((l).isPlainObject(obj)) {
copy = objs[i][key];
obj[key] = copy;
}
}
}
}
}, "*");
}
// Merge first level properties after going deep
for (i = 0; i < objs.length; i++) {
if ((obj = objs[i]) !== null) {
for (key in obj) {
copy = obj[key];
if (target === copy) {
continue;
}
target[key] = copy;
}
}
}
return (l).fn.__chain(target);
};
What are you mentioned here is exactly the prototypal inheritance of JavaScript:
var objA = {a: false};
var objB = Object.create(objA);
objB.b = false;
console.log(objB.a, objB.b); // true,
objA.a = false;
console.log(objB.a, objB.b); // false, false
Of course if objB overrides the a property, you will lose that link, because JavaScript will found the property a in the object objB, therefore won't look up in the prototype's chain.
Using the ECMAScript 5 methods, you could have your extend function like that:
function extend(properties, proto) {
var object = Object.create(proto);
var descriptor = {};
Object.getOwnPropertyNames(properties).forEach(function(name) {
descriptor[name] = Object.getOwnPropertyDescriptor(properties, name);
});
return Object.defineProperties(object, descriptor);
}
var objA = {a: true};
var objB = extend({b: false}, objA);
console.log(objB.a, objB.b); // true, false
objA.a = false;
console.log(objB.a, objB.b); // false, false
Object.extend= function (properties,obj) {
function F() { };
F.prototype = obj;
var newObj = new F();
for (var prop in properties){
newObj[prop] = properties[prop];
}
return newObj;
}

Set length property of JavaScript object

Let's say I have a JavaScript object:
function a(){
var A = [];
this.length = function(){
return A.length;
};
this.add = function(x){
A.push(x);
};
this.remove = function(){
return A.pop();
};
};
I can use it like so:
var x = new a();
x.add(3);
x.add(4);
alert(x.length()); // 2
alert(x.remove()); // 4
alert(x.length()); // 1
I was trying to make .length not a function, so I could access it like this: x.length, but I've had no luck in getting this to work.
I tried this, but it outputs 0, because that's the length of A at the time:
function a(){
var A = [];
this.length = A.length;
//rest of the function...
};
I also tried this, and it also outputs 0:
function a(){
var A = [];
this.length = function(){
return A.length;
}();
//rest of the function...
};
How do I get x.length to output the correct length of the array inside in the object?
You could use the valueOf hack:
this.length = {
'valueOf': function (){
return A.length;
},
'toString': function (){
return A.length;
}
};
Now you can access the length as x.length. (Although, maybe it's just me, but to me, something about this method feels very roundabout, and it's easy enough to go with a sturdier solution and, for example, update the length property after every modification.)
If you want A to stay 'private', you need to update the public length property on every operation which modifies A's length so that you don't need a method which checks when asked. I would do so via 'private' method.
Code:
var a = function(){
var instance, A, updateLength;
instance = this;
A = [];
this.length = 0;
updateLength = function()
{
instance.length = A.length;
}
this.add = function(x){
A.push(x);
updateLength();
};
this.remove = function(){
var popped = A.pop();
updateLength();
return popped;
};
};
Demo:
http://jsfiddle.net/JAAulde/VT4bb/
Because when you call a.length, you're returning a function. In order to return the output you have to actually invoke the function, i.e.: a.length().
As an aside, if you don't want to have the length property be a function but the actual value, you will need to modify your object to return the property.
function a() {
var A = [];
this.length = 0;
this.add = function(x) {
A.push(x);
this.length = A.length;
};
this.remove = function() {
var removed = A.pop();
this.length = A.length;
return removed;
};
};
While what everyone has said is true about ES3, that length must be a function (otherwise it's value will remain static, unless you hack it to be otherwise), you can have what you want in ES5 (try this in chrome for example):
function a(){
var A = [],
newA = {
get length(){ return A.length;}
};
newA.add = function(x){
A.push(x);
};
newA.remove = function(){
return A.pop();
};
return newA;
}
var x = a();
x.add(3);
x.add(4);
alert(x.length); // 2
alert(x.remove()); // 4
alert(x.length); // 1
You should probably use Object.create instead of the function a, although I've left it as a function to look like your original.
I don't think you can access it as a variable as a variable to my knoledge cannot return the value of a method, unless you will hijack the array object and start hacking in an update of your variable when the push/pop methods are called (ugly!). In order to make your method version work I think you should do the following:
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};
These days you can use defineProperty:
let x = {}
Object.defineProperty(x, 'length', {
get() {
return Object.keys(this).length
},
})
x.length // 0
x.foo = 'bar'
x.length // 1
Or in your specific case:
Object.defineProperty(x, 'length', {
get() {
return A.length
}
})
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};

Categories

Resources