I want to call a function twice but not through the traditional way. A quick example of what I would be looking to do is below:
var myfunc = {
copy: function(message){
console.log(message);
}
}
myfunc.copy('hello').copy('world');
// result 'hello'
// result 'world'
Is this even possible?
Yes, but you should return the correct object:
var myfunc = {
copy: function(message){
console.log(message);
return this;
}
};
myfunc.copy('hello').copy('world');
// hello
// world
This technique is also known as Method chaining.
No, this will fail because .copy() doesn't return anything, so the second .copy() would throw an undefined error.
Try this:
var myfunc = {
copy: function(message){
console.log(message);
return this;
}
}
You need to chain this, You can also look up to jquerys $.fn how they create method chaining simply return this as this object is your myFunc variable and is used again by the next function
var myfunc = {
copy: function(message){
console.log(message);
return this;
}
};
myfunc.copy('hello').copy('world');
As other answers have pointed out, you need to return this in order to be able to call the same or additional functions on the same object.
You can do this a bit more easily (?) via a higher-order function which we will call "chainify", which takes care of returning this for you:
function chainify(fn) {
return function() {
fn.apply(this, arguments);
return this;
};
}
You can chainify your object methods in various ways, but here's one:
var myfunc = {
init: function() {
this.copy = chainify(this.copy);
return this;
},
copy: function(message){
console.log(message);
}
}.init();
This has the minor advantage that each and every method does not need to be cluttered with the return this at the end, and you don't run the risk of forgetting to do so.
It's called method chaining. Here's a blog that talks about this that you should be able to read and use to answer your question.
Basically you will need to use return this; to return the current object so the next method can use it.
// define the class
var Kitten = function() {
this.name = 'Garfield';
this.color = 'brown';
this.gender = 'male';
};
Kitten.prototype.setName = function(name) {
this.name = name;
return this;
};
Method chaining in JavaScript
This is known a Builder design pattern where in you cascade methods. Google it if it helps.
Related
I've mocked up some code here
var common = common || {};
(function(NAMESPACE) {
NAMESPACE = {
isIE: function() {
return true;
}
};
main();
})(common);
function main() {
console.log(common.isIE());
return 'Hello, World!';
}
I would like to understand a couple of things,
1) Why isn't this working, I guess it has something to do with how scoping is "decided" and IIFE, but not entirely sure.
2) How to make this code work?
common which passed as argument named NAMESPACE needs to be extended instead of assigning new value.
So Object.assign can help here.
var common = common|| {};
(function(NAMESPACE) {
Object.assign(NAMESPACE,{
isIE: function() {
return true;
}
});
main();
})(common);
function main() {
console.log(common.isIE());
return 'Hello, World!';
}
When you are passing an object as an argument in JS, you should remember that you are passing "by-value" the reference of it.
Creating a new object in literal notation and assigning it to the argument like this,
NAMESPACE = {
isIE: function() {
return true;
}
};
can only point the argument to the new object - not to the object of the reference you passed the argument by.
If you had said,
NAMESPACE.isIE = function() {}
it would work.
I just saw this code example from the crypto-library of node.js and wondered how this kind of "concatenated" function-calls are implemented?
crypto.createHash('sha256').update(password).update(salt).digest('base64');
The return value of one function is an object (probably the original object, this here). A property of that object is another function.
var myObj = {
foo: function() {
alert("foo");
return this;
},
bar: function() {
alert("bar");
return this;
}
};
myObj.foo().bar().bar().foo().bar();
<script type="text/javascript">
function Test()
{
console.log('constructor');
this.chaninFunction = function(){
console.log('chain me up');
}
}
Test.prototype.callme = function(first_argument) {
console.log('called him');
this.callBack = function()
{
console.log('call back');
}
};
Test.prototype.message = function(first_argument) {
console.log('message him');
};
var test = new Test();
test.chaninFunction();
test.callme();
test.callme().callBack(); //Error undefined
test.message();
</script>
Hi,
I am learning JS at the moment. having experience few situation.
Is there a way i can call the function within the prototype? the above testing i have done result in error. How can i access the function within prototype, or i can't?
It seems that you're saying you want to be able to chain the .callback() after a call to .callme().
Chaining is very simple. The previous method you called simply needs to return an object that contains the next method you want to call. So in your case, both methods are on the same object, so you just need to do return this;.
Test.prototype.callme = function(first_argument) {
console.log('called him');
this.callBack = function()
{
console.log('call back');
}
return this;
};
How to write chainable functions but do not pollute $.fn ? Write functions only for using inside my plugin. Is it possible?
$('.myclass').makeSomething().andOneMoreFunction().andLast();
It is correct approach?
UPD.
The best solution in my case is extension method:
String.prototype.getMyLength = function(){return this.length;}
And now I can apply this function to any string like this:
var mystring = "test";
mystring.getMyLength();
Or
"teststring".getMyLength()
And make it chainable:
String.prototype.getMe = function(){return this;}
"string".getMe().getMe().getMe().getMe().getMe();
Thanks for answers!
You can chain all you want. If you define a $.fn yourself it is important that you return this at the end of you function.
If you want to write some javascript yourself you can also chain! It just depends on what you return. So if you return some other object, you can chain on from that object. The return value is used for this.
Example
var obj = {
test : function(){
alert("Y");
return this;
},
test2 : function(){
alert("2");
return this;
}
}
obj.test().test2(); // And so on since it returns this
jQuery Plugin API
$.fn.test = function(){
var methods = {
method0 : function(){
alert("method0");
return this;
}
};
return methods;
}
var api = $("obj").test(); // Returns methods
api.method0(); // Calling a function from the returned methods.
// OR
$("obj").test().method0();
Above function is not jQuery chainable anymore. So you can't use the $("obj").test().addClass("test") because you return your own API!
You can avoid pollution by using the first parameter of your plugin's function to specify the method of choice; for instance
(function () {
var o = { // object holding your methods
'bar': function () {console.log('bar', this); return this;},
'foobar': function () {console.log('foobar', this); return this;}
};
$.fn.foo = function (method /*, args*/) {
return o[method].apply(
this,
Array.prototype.slice.call(arguments, 1) // pass your args
);
};
}());
and then
$('something').foo('bar').foo('foobar');
/*
bar, thisobj
foobar, thisobj
*/
This way you keep access to the jQuery object as normal, too.
When you call a.foo(), the function foo is invoked with this set to a. You can use this to your advantage.
Also recall that the expression a.foo() evaluates to whatever you returnd from within the function.
So, just return this.
Then a.foo() evaluates back to a, and (a.foo()).bar() becomes equivalent to calling a.foo() then calling a.bar()... i.e. chained operations on a!
$.fn is not particularly magical — it simply uses the above logic in the same way that you are about to.
I have the following code example to use an object that receives the action from the callback. Doesn't seem like this is a good design pattern. Or is it?
When setTimeOut() fires on the function after 1 second, it uses the objInstance global variable (DOM scope) to access the ClassExample object instance. Can someone recommend a better way to utilize callbacks within an object oriented design?
The whole idea is so I can use the callback to update data within my object instance (increment a variable for example).
function ClassExample{
this.initiate = function() {
setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}
this.afterTimeOut = function() {
alert("Received!");
}
}
var objInstance = new ClassExample(); //instance
objInstance.initiate();
No, you're not. You'll want to do this:
this.initiate = function() {
setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}
Now, if "afterTimeout" needs the proper object context, you could do this:
this.initiate = function() {
var instance = this;
setTimeout(function() { instance.afterTimeOut(); }, 1000);
}
OK well you changed the question considerably with that little edit :-) If I were you, I'd just do this (like my original second example):
this.initiate = function() {
var instance = this;
setTimeout(function() { instance.afterTimeOut(); }, 1000);
}
Then you don't need any ugly global variables around at all.
edit — Stackoverflow user #Christoph comments that this isn't particularly pretty. One thing that might help would be to use a "bind" facility, as provided by newer browsers natively (as a method on the Function prototype) or by some libraries (Prototype or Functional for example). What "bind" lets you do is create a little wrapper function like I've got above:
this.initiate = function() {
setTimeout(this.afterTimeOut.bind(this), 1000);
}
That call to "bind" returns a function that is effectively the same sort of thing as the little wrapper I coded explicitly in the example.
function ClassExample{
this.afterTimeOut = function() {
alert("Received!");
}; // Don't forget these
setTimeOut(afterTimeOut, 1000); // Don't use () if you're passing the function as an argument
}
var objInstance = new ClassExample(); //instance
That way you don't need the initiate() method.
If you really want the initiate() method, I'd do it like this:
function ClassExample{
var self = this;
self.afterTimeOut = function() {
alert("Received!");
};
self.initiate = function() {
setTimeOut(self.afterTimeOut, 1000);
};
}
var objInstance = new ClassExample(); //instance
objInstance.initiate();
This is how I'd do it to allow timer reuse and minimize the number of closures:
function Timer(timeout, callback) {
this.timeout = timeout;
this.callback = callback;
}
Timer.prototype.run = function(thisArg /*, args... */) {
var argArray = Array.prototype.slice.call(arguments, 1);
var timer = this;
setTimeout(function() {
timer.callback.apply(thisArg, argArray);
}, timer.timeout);
};
var timer = new Timer(1000, alert);
timer.run(null, 'timer fired!');
And just for fun, a golfed version which is functionally equivalent, but replaces the object with a closure:
function delay(func, timeout) {
return function() {
var self = this, args = arguments;
setTimeout(function() { func.apply(self, args); }, timeout);
};
}
delay(alert, 1000).call(null, 'timer fired!');
You are right it is not the optimal way of doing what you are aiming for. however i have to wonder why you need to break the callstack as part of the initiation, it seems very academic.
apart from that if i had to do that, i'd probably use a closure like so:
function ClassExample{
this.initiate = function() {
setTimeOut((function(self) { return function() { self.afterTimeout();}})(this),1000); //using the objects global handle
}
this.afterTimeOut = function() {
alert("Received!");
}
}
var objInstance = new ClassExample(); //instance
objInstance.initiate()
this.initiate = function() {
var instance = this;
setTimeOut(function() {
instance.afterTimeOut();
}, 1000);
};
By saving this to a local variable, you can avoid using the global handle at all. Also this prevent the afterTimeout() from losing it's this.
Building on Znarkus answer...
I really don't know in which environment his code is running but for me the first approach just do not works. I got: 'ReferenceError: afterTimeOut is not defined'...
The second one, nevertheless, is really cool... I just changed setTimeOut for setTimeout (using lowercase 'o') and included parenthesis after the class name definition turning the first line of code into 'function ClassExample(){'; solved my problem.
My snippet of example code:
Oop with private behaviour, intern callback calling and etc.
function MyTry (name){
// keep this object pointer... that's the trick!
var self = this;
// create private variable
var d = new Date()toJSON().slice(0, 10);
// create a private function
function getName(){return name}
// create public access method
self.hello = function(){alert('Hello '+getName()+'!\nToday is: '+d)}
// note instance method hello passed as a callback function!
self.initiate = function(){setTimeout(self.hello, 3000)}
}