call/apply/bind的方法来源
call/apply/bind方法其实都来自与function.prototype,都属于实例方法
console.log(Function.prototype.hasOwnproperty('call'));
console.log(Function.prototype.hasOwnproperty('apply'));
console.log(Function.prototype.hasOwnproperty('bind'));
//true true true
扩展一下下:
Object.prototype.hasOwnProperty()方法用来判断某个对象是否含有指定的自身属性,返回true/false.
Object.prototype.isPrototypeOf()方法测试一个对象是否存在另一个对象的原型链上,返回true/false.
Function.prototype.call()
函数实例的call方法,可以指定函数this的指向,既函数执行时的作用域。然后在指定的作用域内调用该函数,而且立即执行。
var obj = {
num:123
}
var num = 456;
functon fn(){
console.log(this.num)
}
fn() //456
fn.call() //456
fn.call(null)//456
fn.call(undefined)//456
fn.call(this)//456
fn.call(obj)//123
从上边可以看出fn函数的this在没有操作或者call中的参数为空、null、undefined、this的时候都指向的应该是456。而当参数改变。fn中this的指向改变,指向的是obj。所以是123.
call()方法可以传递两个参数。第一个参数是指定函数内部中this的指向(也就是函数执行时所在的作用域),第二个参数是函数调用时需要传递的参数。
第一个参数是必须的,可以是null,undefined,this,但是不能为空。设置为null,undefined,this表明函数keith此时处于全局作用域。第二个参数中必须一个个添加。而在apply中必须以数组的形式添加。
Function.prototype.apply()
apply方法的作用与call方法类似,也是改变this指向(函数执行时所在的作用域),然后在指定的作用域中,调用该函数。同时也会立即执行该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数。
apply方法的实例应用
找出数组中最大的数
var arr = [1,2,3,4,58,6]
console.log(Math.max.apply(null,arr))
Javascript中是没有提供找出数组中最大值的方法的,结合使用继承自Function.prototype的apply和Math.max方法,就可以返回数组的最大值。
将数组的空元素变为undefined
console.log(Array.apply(null, [1, , 3])); // [1, undefined, 3]
空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined和null。因此,遍历内部元素的时候,会得到不同的结果。
var a = [1, , 3];
a.forEach(function(index) {
console.log(index); //1,3 ,跳过了空元素。
})
Array.apply(null,a).forEach(function(index){
console.log(index); ////1,undefined,3 ,将空元素设置为undefined
})
转换类似数组的对象
利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组。当然,slice方法的一个重要应用,就是将类似数组的对象转为真正的数组。call和apply都可以实现该应用。
console.log(Array.prototype.slice.apply({0:1,length:1})); //[1]
console.log(Array.prototype.slice.call({0:1,length:1})); //[1]
console.log(Array.prototype.slice.apply({0:1,length:2})); //[1,undefined]
console.log(Array.prototype.slice.call({0:1,length:2})); //[1,undefined]
function keith(a,b,c){
return arguments;
}
console.log(Array.prototype.slice.call(keith(2,3,4))); //[2,3,4]
上面代码的call,apply方法的参数都是对象,但是返回结果都是数组,这就起到了将对象转成数组的目的。从上面代码可以看到,这个方法起作用的前提是,被处理的对象必须有length属性,以及相对应的数字键。
Function.prototype.bind()
bind方法用于指定函数内部的this指向(执行时所在的作用域),然后返回一个新函数。bind方法并非立即执行一个函数。
var keith = {
a: 1,
count: function() {
console.log(this.a++);
}
};
keith.count(); //1
keith.count(); //2
keith.count(); //3
上面代码中,如果this.a指向keith对象内部的a属性,如果这个方法赋值给另外一个变量,调用时就会出错。
var keith = {
a: 1,
count: function() {
console.log(this.a++);
}
};
var f = keith.count;
f(); //NaN
上面代码中,如果把count方法赋值给f变量,那么this对象指向不再是keith对象了,而是window对象。而window.a默认为undefined,进行递增运算之后undefined++就等于NaN。
为了解决这个问题,可以使用bind方法,将keith对象里的this绑定到keith对象上,或者是直接调用。
var f = keith.count.bind(keith);
f(); //1
f(); //2
f(); //3
keith.count.bind(keith)() //1
keith.count.bind(keith)() //2
keith.count.bind(keith)() //3
当然,this也可以绑定到其他对象上。
var obj = {
a: 100
};
var f = keith.count.bind(obj);
f(); //100
f(); //101
f(); //102
同样,我们也可以给bind方法传递参数,第一个参数如果为null或者undefined或者this,会将函数内部的this对象指向全局环境;第二个为调用时需要的参数,并且传递参数的形式与call方法相同。
function keith(a, b) {
return a + b;
}
console.log(keith.apply(null,[1,4])); //5
console.log(keith.call(null,1,4)); //5
console.log(keith.bind(null, 1, 4)); //keith()
console.log(keith.bind(null, 1, 4)()); //5
绑定回调函数的对象
var o = {
f: function() {
console.log(this === o);
}
}
$('#button').on('click', function() {
o.f.apply(o);
//或者 o.f.call(o);
//或者 o.f.bind(o)();
});