I'm trying to create a mini jQuery clone that can support method chaining. So far I've came up with this piece of code:
var $ = (function () {
var elements = [];
function methodOne() {
console.log('Method 1');
return this;
}
function methodTwo() {
console.log('Method 2');
return this;
}
return {
methodOne: methodOne,
methodTwo: methodTwo
};
}());
At page load, the $ variable gets populated with the jQuery clone object returned by the IIFE.
My question is, how can I make the $ object to be called directly as a function while still maintaining the method chaining functionality?
Right now, I can use $.methodOne().methodTwo() but I cant use $('some parameter').methodOne().methodTwo() just like jQuery does.
var $ = function (param) {
var elements = [];
console.log(param);
function methodOne() {
console.log('Method 1');
return this;
}
function methodTwo() {
console.log('Method 2');
return this;
}
return {
methodOne: methodOne,
methodTwo: methodTwo
};
};
$('This is a param').methodOne().methodTwo();
Working fiddle. The comments should be more or less self explanatory.
It might look a bit long, but it will let you create new mini jQuery object every time you call it.
var _ = (function () {
var Magic = function(query){
if(window === this)
return new Magic(query);
// reference to itself
var that = this;
//assign pseudo public methods and variables
that.elements = [];
that.methodTwo = methodTwo;
that.methodOne = methodOne;
//fills inner element array with DOM element
_get(query);
function _get(query){
var elem = document.getElementById(query);
that.elements.push(elem);
}
function methodOne() {
console.log('Method 1');
return that;
}
function methodTwo() {
console.log('Method 2', that.elements);
return that;
}
return this;
}
//returns function, which is assigned to a "_" variable
return function(query){
//everytime "_" is called, it will return new instance for object Magic which makes all the work
return new Magic(query);
}
}());
Let's say I write a jquery plugin that has a method that adds two values and sets the html to that value. I can chain it one way, but not the other and I want to understand why, and how I can make sure I can chain both ways.
$.fn.foo = function() {
this.result = 0;
this.doMath= function(a, b) {
this.result = a + b;
this.html(this.val);
return this;
}
return this;
}
var link = 'Link'
// works as expexted
var a = $(link).foo().doMath(1,2).appendTo('body')
// does not work, likely because appendTo returns jquery
var b = $(link).foo().appendTo('body').doMath(1,2)
https://jsfiddle.net/j7ut1f30/1/
After 2 weeks of trying everything, I finally acheived my goals.
class $Util {
static $wrapper(self) {
if (!self.$wrapper)
throw new ReferenceError('$Util.$wrapper: $wrapper not defined in context of first argument');
$.each($Util.jqueryPrototype, function(i, e) {
self[e] = function() {
self.$wrapper[e](...arguments);
return self;
}
});
}
}
$Util.jqueryPrototype = Object.getOwnPropertyNames($.prototype);
class foo {
constructor() {
this.$wrapper = $('p')
$Util.$wrapper(this);
return this;
}
test() {
console.log('j')
return this;
}
}
var s = new foo();
s.css('color', 'red').test().appendTo('body').test().css('color','green')
https://jsfiddle.net/24r6uxs1/
I would like to known if there is a native way of doing this :
Object.prototype.chain = function(f) { return f.call(this) }
function fun1() {
doSomethingWithObject(this)
return this
}
function fun2() {
doSomethingElse(this)
return this
}
someObject
.method1('something')
.method2()
.chain(checkSomething() ? fun1 : fun2)
.method3()
But I do not feel like changing the prototype of Object. Is there a way to do this without modifying the prototype of Objects or the other constructors that I use (and am not the developer of)
Edits :
I feel I do not explain very well, so let' add some details :
What I would like to do is to use some APIs I do not define. someObject is defined like the following, with chainable methods :
var someObject = {
method1: function(val) {
// do something
return this
},
method2: function() {
// do something
return this
},
method3: function() {
// do something
return this
}
}
Now imagine I cannot change this code, because this object is from a library, and so I don't want to. Then, imagine that I would like to chain methods and some custom functions (see my first snippet) for many more different objects. The simplest thing to do is to attach a chain method to Object.prototype.
But I think that it could result in conflicts in the future. I am looking for a way to do the same thing without touching the prototype.
I'm surprised there are no answers to this to be honest.
There are many ways to natively introduce chaining. I like to use the revealing module pattern.
So I create a basic model (Go ahead and chuck this in your chrome of firefox console)
var Dog = function(name) {
var self = this;
this.name = name;
var core = {
getName:function(){
return self.name;
}
};
this.movement = function(){ //this function will be exposed including its returned functions for chaining
console.log(self.name + " is getting restless... ");
var jump = function(){
console.log(self.name + " jumps around ");
return this //returns the movement scope
};
var run = function(){
console.log(self.name + " has decided to run");
return this //returns the movement scope
};
return {
jump:jump,
run:run
};
}
console.log("A Pup has been born, we shall call him... " + name);
return{
movement:self.movement //only .movement is exposed to the outside world
};
}
Now create a new dog using var p = new Dog("doggyName");
now, you can chain functions. Try:
p.movement().jump().run().jump().run();
You should get the console logged text that corresponds with each function.
By returning the scope of this after executing your movement function you expose the additional functions that are returned in that scope (see the comments in the code). These can then be chained onto the end of your current function provided they are in the same scope. This allows you to scope specific parts of your code. For example with this dog, all movement is scoped to self.movement, you could have all eating scoped to self.eat and so on
Read up on the revealing module pattern. Though this is not the only way to do it.
The wrapper is something that will wrap any object to make it compatible with "chaining" and will add another chain method that will allow you to plug external functions and still get the chaining.
Check this example:
function myObj() {
this.state = {
a: 1
};
this.method1 = function () {
console.log("1");
}
this.method2 = function () {
console.log("2");
}
this.method3 = function () {
console.log("3");
}
this.method4 = function () {
console.log(this.state);
}
}
function objectChainWrapper(obj) {
this.chain = function (fn) {
fn.call(obj);
return this;
}
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop] == 'function') {
this[prop] = (function (methodName) {
return function () {
obj[methodName].call(obj);
return this;
}
}(prop))
}
}
}
var obj = new myObj();
var wrapper = new objectChainWrapper(obj);
var chainMethod = function(){ console.log('chain') };
var chainMethodState = function(){ console.log(this.state) };
wrapper.method1().method2().chain(chainMethodState).method3().chain(chainMethod).method4();
JSFIDDLE.
To "plug" an unbound function into the object's method chain you can assign it to a property and call that:
function fn() {
document.write('hi ');
return this;
}
someObj = {
meth1: function() {
document.write('meth1 ');
return this;
},
meth2: function() {
document.write('meth2 ');
return this;
}
}
someObj
.meth1()
[someObj._=fn, '_']()
.meth2()
This doesn't look very pretty if you ask me. A more readable option is to add the chain method on the fly, like:
function chainable(obj) {
obj.chain = function(fn) {
return fn.call(this);
}
return obj;
}
chainable(someObj).meth1().chain(fn).meth2()
Let's say I have a function named fna() that does a simple thing such as:
var fna = function(ar) {
console.log("argument: ", ar);
return "return value is argument too: " + ar;
}
fna() is coded by some other developer and I can't access to it. He didn't bother casting any events and when it is called, I have to be aware of it. Hopefully, his method is accessible by window.fna().
I want some additional code to be executed. Let's say, add this console.log
var fna = function(ar) {
console.log("Hola, I am some additional stuff being rewired");
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
And I want this to be executed even when called from fnb() by some other part of the code.
var fnb = function() {
return fna("Bonjour, I am fnb and I call fna");
}
Here is a way I found, using the utils.rewire() method. utils is just some utility belt, and it could be added to your favorite framework as a plugin. Unfortunately, it only works on Firefox.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(functionFullName, callback) {
var rewired = window[functionFullName];
console.log("%s() is being rewired", functionFullName)
window[functionFullName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
Use it like this.
utils.rewire("fna",function(){
console.log("Hola, I am some additional stuffs being rewired");
});
This seems to work such as shown in this jsbin, but (and here is my question:) How do I rewire obja.fna()?
var obja = {
fna = function(ar) {
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
};
I cannot make it work to rewire the some.object.method() method.
Extra bonus question: Is there a more cleaner way to do this? Out-of-the-box clean concise and magic library?
Refactor rewire into a rewireMethod function which acts on any given object:
var utils = utils || {};
utils.rewireMethod = function (obj, functionName, prefunc) {
var original = obj[functionName];
obj[functionName] = function () {
prefunc();
return original.apply(this, arguments);
};
};
Note that rewire can now be written as:
utils.rewire = function (functionName, prefunc) {
utils.rewireMethod(window, functionName, prefunc);
};
Then you just call it as:
utils.rewireMethod(obja, "fna", function () {
console.log("Hola, I am some additional stuff being rewired");
});
Note that nothing special is required if you have a method like window.ideeli.Search.init(). In that case, the object is window.ideeli.Search, and the method name is init:
utils.rewireMethod(window.ideeli.Search, "init", function () {
console.log("Oh yeah, nested objects.");
});
Add a parameter to rewire that is the object containing the function. If it's a global function, pass in window.
var utils = utils || {};
// let's rewire a function. i.e. My.super.method()
utils.rewire = function(object, functionName, callback) {
var rewired = object[functionName];
console.log("%s() is being rewired", functionName)
object[functionName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
utils.rewire(some.object, "method", function(){} );
You can simply use a closure to create a generic hook function that allows you to specify another function to be called immediately before or after the original function:
function hookFunction(fn, preFn, postFn) {
function hook() {
var retVal;
if (preFn) {
preFn.apply(this, arguments);
}
retVal = fn.apply(this, arguments);
if (postFn) {
postFn.apply(this, arguments);
}
return retVal;
}
return hook;
}
So, for any function that you want to hook, you just call hookFunction and pass it the function you want to hook and then an optional pre and post function or yours. The pre and post function are passed the same arguments that the original function was.
So, if your original function was this:
var fna = function(ar) {
console.log("argument:",ar);
return "return value is argument too:"+ar;
}
And, you want something to happen every time that function is called right before it's called, you would do this:
fna = hookFunction(fna, function() {
console.log("Hola, I am some additional stuff being rewired right before");
});
or if you wanted it to happen right after the original was called, you could do it like this:
fna = hookFunction(fna, null, function() {
console.log("Hola, I am some additional stuff being rewired right after");
});
Working demo: http://jsfiddle.net/jfriend00/DMgn6/
This can be used with methods on objects and arbitrary nesting levels of objects and methods.
var myObj = function(msg) {
this.greeting = msg;
};
myObj.prototype = {
test: function(a) {
log("myObj.test: " + this.greeting);
}
}
var x = new myObj("hello");
x.test = hookFunction(x.test, mypreFunc2, myPostFunc2);
x.test("hello");
Based on Claudiu's answer, which seems to be the most appreciated way, here is a solution using a for loop and proxying the context... But still, I find this ugly.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(method, callback) {
var obj = window;
var original = function() {};
var tree = method.split(".");
var fun = tree.pop();
console.log(tree);
// Parse through the hierarchy
for (var i = 0; i < tree.length; i++) {
obj = obj[tree[i]];
}
if(typeof(obj[fun]) === "function") {
original = obj[fun];
}
var cb = callback.bind(obj);
obj[fun] = function(ar) {
cb();
return original.apply(this, arguments);
}
}
Well, this looks strange. Consider this
function wrap(fn, wrapper) {
return function() {
var a = arguments;
return wrapper(function() { return fn.apply(this, a) })
}
}
Example:
function foo(a, b) {
console.log([a, b])
return a + b
}
bar = wrap(foo, function(original) {
console.log("hi")
var ret = original()
console.log("there")
return ret
})
console.log(bar(11,22))
Result:
hi
[11, 22]
there
33
To wrap object methods, just bind them:
obj = {
x: 111,
foo: function(a, b) {
console.log([a, b, this.x])
}
}
bar = wrap(obj.foo.bind(obj), function(fn) {
console.log("hi")
return fn()
})
I have now officially spent all day trying to assign a variable in JavaScript!
Forgive me for asking this same question 4 different ways, but here's what I started out with this morning, and this works. I just need to add a second method to it now.
Application = {};
(function() {
var closure = {};
Application.myFirstMethod = function() {
if (arguments.length) {
closure = arguments[0];
} else {
return closure;
}
}
})();
Application.myFirstMethod(3.14);
result = Application.myFirstMethod();
log(result);
So my question is: and please be patient with me, if I add mySecondMethod to Application, then how do I keep the value of arguments[0] without using the variable that is currently called closure?
How about this, it defines a function that takes a string and returns a getter/setter function. The string is used to indicate what property to get/set the value as in variables.
Demo
Application = {};
(function() {
var variables = {};
Application.myFirstMethod = makeGetterSetter('myFirst');
Application.mySecondMethod = makeGetterSetter('mySecond');
function makeGetterSetter(name) {
return function () {
if (arguments.length) {
variables[name] = arguments[0];
} else {
return variables[name];
}
};
}
})();
Application.myFirstMethod(4);
result1 = Application.myFirstMethod();
Application.mySecondMethod(5);
result2 = Application.mySecondMethod();
console.log(result1);
console.log(result2);
If you wanted to have a getter or setter with custom logic in it before either event then it would be easiest to just define them separately. Stick with the this[property] pattern to keep all your fields in one spot.
Application.myCustomMethod = function() {
if (arguments.length) {
// some logic
variables['custom'] = arguments[0];
} else {
// some logic
return variables['custom'];
}
}
It looks like you are searching for adding properties to objects, in the Prototype-Oriented Programming Language sense of the term; just use the "this" object, which stands for the current calling context, and which will be set to your Application object when calling the methods:
Application = {};
(function() {
Application.myFirstMethod = function() {
if (arguments.length) {
this.foo = arguments[0];
} else {
return this.foo;
}
};
Application.mySecondMethod = function() {
if (arguments.length) {
this.bar = arguments[0];
} else {
return this.bar;
}
};
})();
Application.myFirstMethod(3.14);
console.log(Application.myFirstMethod());
Application.mySecondMethod(2097);
console.log(Application.mySecondMethod());
console.log(Application.myFirstMethod());
Here's what I figured out. I probably need to use the word new somewhere though.
Application = {};
(function() {
Application.myFirstMethod = FirstMethod();
Application.mySecondMethod = SecondMethod();
function FirstMethod() {
var closure = {};
return function(myArgument) {
if (arguments.length) {
closure.result = arguments[0]; // myArgument
} else {
return closure.result;
}
}
}
function SecondMethod() {
var closure = {};
return function(myArgument) {
if (arguments.length) {
closure.result = arguments[0]; // myArgument
} else {
return closure.result;
}
}
}
})();
Application.myFirstMethod(3.14);
result = Application.myFirstMethod();
log(result);
Application.mySecondMethod(2013);
result = Application.mySecondMethod();
log(result);