Function.prototype.bind = function() {
var $this = arguments[0];
return this.apply($this, Array.prototype.slice.call(arguments, 1));
};
Is it good enough to use in real-world application?
No. There are a few things I don't like about this code, and a few reasons why it won't work.
First, most people don't assign arguments that way. It takes up extra space for no extra effect. Only use the arguments variable if the variable names should change depending on the number of arguments/type of arguments. To assign $this you should do..
Function.prototype.bind = function($this) {
Second, bind should return a function. Your's returns whatever this returns. Your function acts more like a Function:call then a Function:bind.
What you need to do to fix it, is make it return a function that when run will return whatever the function returns.
Try this:
Function.prototype.bind = function($this) {
// `this` changes inside the function, so we have to give it a safe name.
var self = this;
return function () {
return self.apply($this, Array.prototype.slice.call(arguments, 1));
}
};
Also, more modern browsers have the ECMAScript 5 standard of this function built in. The function is written in plain JavaScript, so for older browsers, just include this code suggested by Mozilla:
if ( !Function.prototype.bind ) {
Function.prototype.bind = function( obj ) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () {},
bound = function () {
return self.apply( this instanceof nop ? this : ( obj || {} ), args.concat( slice.call(arguments) ) );
};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;
};
}
Related
I try to realize a function e.g. MyFn() with some features as follows:
1. MyFn('Id') > It must be result the value of document.getElementById('Id');
2. MyFn('Id').MyMethode(); > It must be result the performing of a function.
Below this is realized by means of "Object.prototype" as follows:
Object.prototype.MyFn =function(param1){ return document.getElementById(param1); };
alert( MyFn('mydiv1') );
MyFn('mydiv1').posi = function() { alert("Hello, I'm the function posi!"); };
MyFn('mydiv1').posi();
alert( MyFn('mydiv1') );
Just the above example is what I'm trying to realize. But I don't want to use Object.prototype or jQuery.
Below is my wrong approach (it is maybe helpfully what I'm trying to say or to do):
var MyObj = {
method: function(args, callback) {
if(typeof callback == "function") {
callback();
}
return 123;
}
}
MyFn = function(sId) {
return MyObj;
};
alert( MyFn("mydiv1").method() ); // This is ok, because it calls the method: MyObj.method() as it was expected.
alert( MyFn("mydiv1") ); // <-- But here I like to get document.getElementById("mydiv1").
Note: The syntax of code (how the functions are to call) is important! The functions are to call as follows: MyFn('Element-Id') or MyFn('Element-Id').posi(), but not something as follows: MyObj.MyMethode()
Do you have any idea how can I it realize? Thanks in advance.
You could try something like:
var MyObj = {
method: function(args, callback) {
if(typeof callback == "function") {
callback();
}
return 123;
}
}
var MyFn = function(sId) {
this.elem = document.getElementById(sId);
this.MyObj = MyObj;
return this;
};
alert( MyFn("mydiv1").MyObj.method() );
alert( MyFn("mydiv1").elem );
This returns a reference to the function, after the function executes, so offers syntax much like C# extension methods for example.
Should be rather straight forward, seeing as functions are objects as well.
The way it's usually done, and the way jQuery does it, is to return a new instance of the function, which is done with a simple check
function MyFn(selector, context) {
if ( !(this instanceof MyFn) ) { // not an instance
return new MyFn(arguments); // calls itself with the "new" keyword
} else { // now it is an instance
context = context || document;
this[0] = context.getElementById(id);
}
return this;
}
Now building on that, we can add methods, but that requires prototyping them, which is the correct way to do this anyway
MyFn.prototype.width = function() {
return this[0].style.width;
}
and even make those methods chainable
MyFn.prototype.width = function(width) {
if ( width ) {
this[0].style.width = width + 'px';
return this;
} else {
return this[0].style.width;
}
}
FIDDLE
Ugly, not recomended by almost all design patern, but should work :
MyFn = function(sId) {
var obj = document.getElementById(param1);
obj.method = function(args, callback) {
if(typeof callback == "function") {
callback();
}
return 123;
}
return MyObj;
};
Basicly you add the function manualy to the object.
It's not a good desing patern as someone external won't know in advance that the object has an extra method.
This is a bit hacky solution:
var MyObj = function (id) {
var obj = document.getElementById(id);
// attach functions here
obj.myFun = function () {
// ...
}
// ...
return obj;
}
You get the object, attach your own functions to the object (hopefully without redefining existing ones), then return it.
function func() {
return this == myObject;
}
var boundFunc = func.bind(myObject, "arg1", "arg2");
boundFunc("arg3", "arg4");
Function.prototype.bind = function() {
var fn = this;
var args = Array.prototype.slice.call(arguments);
var object = args.shift();
return function() {
var allArgs = args.concat(Array.prototype.slice.call(arguments))
return fn.apply(object, allArgs);
};
}
I need some help with the above snippet of code. I think I understand it except for one thing.
I don't understand how this:
return function() {
var allArgs = args.concat(Array.prototype.slice.call(arguments))
return fn.apply(object, allArgs);
};
refers to this:
boundFunc("arg3", "arg4");
I know that when bind gets called args gets the arguments (myObject, arg1, arg2). But I don't understand how return function() is referring to boundfunc in order to concatenate arg3 and arg4 with args. Does that make sense? Can anyone shed some light on the subject for me please?
This is what happens if you disassemble the code:
var boundFunc = function() {
var allArgs = ["arg1", "arg2"].concat([].slice.call(arguments))
return func.apply(myObject, allArgs);
};
The arguments in .bind() are kept and a proxy function is returned that will call func with a combination of the earlier arguments and the new ones that are passed in the call.
I'm trying to understand the difference between curry vs bind.
The implementation of bind is :
/*1*/ Function.prototype.bind = function ()
/*2*/ {
/*3*/ var fn = this,
/*4*/ args = Array.prototype.slice.call(arguments);
/*5*/ var object = args.shift();
/*6*/ return function ()
/*7*/ {
/*8*/ return fn.apply(object,
/*9*/ args.concat(Array.prototype.slice.call(arguments)))
/*10*/ };
/*11*/ }
The implementation of curry is :
/*1*/ Function.prototype.curry = function ()
/*2*/ {
/*3*/ var fn = this,
/*4*/ args = Array.prototype.slice.call(arguments);
/*5*/ return function ()
/*6*/ {
/*7*/ return fn.apply(this,
/*8*/ args.concat(Array.prototype.slice.call(arguments)));
/*9*/ };
/*10*/ };
I already know that curry is not an internal function (unlike bind which is in IE9+). But still:
Why do I hear people keep talking about curry , While they can simply use bind operation ?
The only difference is the context which is actually found only at the bind function.
differences
Example :
Let's say I have this function :
function add(x,y,z)
{
return x+y+z;
}
I could do it with curry :
alert(add.curry(2).curry(1)(4)) //7
But I could also do it with :
alert(add.bind(undefined,2).bind(undefined,1)(4)) //7
I don't understand why this curry term function exists while it is possible to add a dummy context to the bind function.
What am I missing ?
bind forces you to attach a context to the function, while by using curry, you can delay the specification of function context until invoking the curried function, useful in many cases.
consider the following example (not the perfect one, just to illustrate the idea):
function Query(sessionKey, dataBuilder) {
this.sessionKey = sessionKey;
this.url = "http://www.example.com/search";
this.dataBuilder = dataBuilder
this.search = function (term) {
$.ajax({
type: "POST",
url: this.url,
data: this.dataBuilder(term);
})
}
}
function dataBuilder(entity, query) {
var payload = JSON.stringify({
'entity': entity,
'searchTerm': query
'session': this.sessionKey // will be always undefined if bind(undefined,...) is used
});
return payload
}
var bindEx= dataBuilder.bind(undefined, "username");
var curryEx= dataBuilder.curry("username");
var usernameQuery = new Query("id1234",bindEx); // won't work, this.sessionKey will be undefined
usernameQuery = new Query("id1234",curryEx); // will work, this.sessionKey will be id1234 in the DataBuilder
There is a difference in intention.
Currying is to reduce the number of arguments, usually to avoid calling a function a lot with the same initial arguments. For example:
var celsiusToKelvin = add.curry(273.15);
bind() is to make sure that a function is attached to an object. It also happens to offer a currying facility, so yes you can use bind() to curry(), but if you want to curry, curry() has fewer arguments and shows your intention.
I'd think it has something to do with compatibility with older browsers as bind is only available since ECMAScript 5.
See this for a list of .bind() support: http://kangax.github.io/es5-compat-table/
Also from what I've heard, most people still use curry because it looks cleaner as it doesn't need that extra undefined in the arguments.
I am looking for var self = this alternative plan.
var Animal = function(name){
this.name = name;
this.arr = [1,2,3,4];
this.inc = function(num){
return num + 1;
};
this.fireArr = function(){
var self = this;
this.arr.forEach(function(item){
console.log(self.inc(item));
});
};
};
var dog = new Animal("dog");
console.log(dog.fireArr());
My fiddle is here.
http://jsfiddle.net/haradashinya/TtYpc/
Do you have any idea?
Thanks in advance.
You can set the second argument to forEach, which is the this value.
this.arr.forEach(function(item){
console.log(this.inc(item));
}, this);
You can use .bind() to make sure the function is called with the right this value:
function fireArr() {
this.arr.forEach(function(item){
console.log(this.inc(item));
}.bind(this));
}
But imho the self (that, _this) variable is easier to understand, because it directly states that not the normal this value is used, although one would expect it (e.g. in an event handler, or jQuery's each()). Especially on long functions, where you don't see the bind() in the end, this is of importance. Also, some ancient browsers do not support bind() and you would need to shim it.
So, for any in-place function expressions I recommend the use of a dereferencing variable.
But it can be of great use when you have a method defined somewhere, normally using this to point to the current object as it is common in that context, and then the method should be used somewhere else. Instead of a var self-wrapper, you can and should use bind for simplicity and clarity. Your example offers quite a good demo (assuming the inc method used the this keyword):
this.arr.forEach( this.inc.bind(this) );
(although forEach() allows us to pass a custom this argument - event attachers for example don't)
In your example, the inc function doesn't use the this value, so it doesn't need to be a method. You can define it as a local function:
var Animal = function ( name ) {
this.name = name;
this.arr = [ 1, 2, 3, 4 ];
var inc = function ( num ) {
return num + 1;
};
this.fireArr = function () {
this.arr.forEach(function ( item ) {
console.log( inc( item ) );
});
};
};
I read this, and I understand it, but, is there a way to attach a function to always execute a function alias on a specific scope, without the user of the function alias have to use the sort of clunky .apply stuff?
You can use the new ECMAScript 5 .bind() method. An alternative implementation for browsers that don't support it (taken from the MDC documentation I linked to):
// Function.prototype.bind polyfill
if ( !Function.prototype.bind ) {
Function.prototype.bind = function( obj ) {
if(typeof this !== 'function') // closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () {},
bound = function () {
return self.apply( this instanceof nop ? this : ( obj || {} ),
args.concat( slice.call(arguments) ) );
};
bound.prototype = this.prototype;
return bound;
};
}