在js中,call、apply和bind是为了动态改变this而出现的,当一个object没有某个方法(本栗子中other没有say方法),但是其他的有(本栗子中person有say方法),我们可以借助call、apply或bind用其它对象的方法来操作。由此可见,这三种方法都是用来改变函数的this对象的指向的。有共同相似点:1、都是用来改变函数的this对象的指向的2、第一个参数都是this要指向的对象3、都可以继续传递参数
- 无传参的情况
var person = {
name: '小菜',
gender: '男',
say: function() {
console.log(this.name + ',' + this.gender);
}
}
var other = {
name: 'hathy',
gender: '女',
}
person.say(); // hathy,女
//如果我们想要用xb的say()方法输出other的数据,要怎么办呢?
//其实call和apply方法,都是对函数的直接调用,但是bind()方法需要加上()来执行
person.say.call(other); //hathy,女
person.say.apply(other); //hathy,女
person.say.bind(other)(); //hathy,女
- 带传参的情况,调用参数的形式有区别。标准格式参考:1、func.call(this, arg1, arg2);2、func.apply(this, [arg1, arg2]);3、func.bind(this, arg1, arg2)();
var person = {
name: '小菜',
gender: '男',
say: function(age, school) {
console.log(this.name + ',' + this.gender + '---' + age + ',' + school);
}
}
var other = {
name: 'hathy',
gender: '女',
}
//call()和apply()函数的用法其实是一样的,只是传参的形式不一样,这是他们最大的差别
//bind()跟call()传参很像,返回的其实还是一个函数,并没有执行
person.say.call(other, 28, '清华大学'); //hathy,女---28,清华大学
person.say.apply(other, [27, '北京大学']); //hathy,女---27,北京大学
person.say.bind(other, 28, '清华大学')(); //hathy,女---28,清华大学
//定义全局变量
var name = 'xx';
var gender = '男';
//不指定上下文对象,用null代表将执行上下文交回给全局作用域
person.say.call(null, 28, '清华大学'); //xx,男---28,清华大学
- 某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用call。而不确定的时候用apply,然后把参数push进数组传递进去。当参数数量不确定时,函数内部也可以通过arguments这个数组来遍历所有的参数。
//数组之间追加
var array1 = [12 , "foo" , {name: "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
console.log(Array.prototype.push.apply(array1, array2)); //7 == array1.length
//获取数组中的最大值和最小值
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers1 = Math.max.apply(Math, numbers), //458
maxInNumbers2 = Math.max.call(Math,5, 458 , 120 , -215); //458
//验证是否是数组(前提是toString()方法没有被重写过)
function isArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}
console.log(isArray(array1)); //true
//类(伪)数组使用数组方法
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
//自定义日志方法,内部代理console.log
function myLog(){
var args = Array.prototype.slice.call(arguments);
args.unshift('(tips)');
console.log.apply(console, args);
}
myLog(1,2); //(tips) 1 2
- bind函数的理解
//单体模式中,通常我们会使用_this,that,self等保存this这样我们可以在改变了上下文之后继续引用到它
var foo = {
bar: 1,
eventBind: function() {
var _this = this;
document.body.onclick = function() {
console.log(_this.bar); //1 注意需要设html,body的height为100%
}
}
}
foo.eventBind();
//使用bind()可以更加优雅的解决这个问题
var foo = {
bar: 1,
eventBind: function() {
document.body.onclick = function() {
/* Act on the event */
console.log(this.bar); //1
}.bind(this);
}
}
foo.eventBind();
//当回调函数被执行的时候,this便指向foo对象。再来一个简单的栗子:
var other = function() {
console.log(this.bar);
}
other(); // undefined
var func = other.bind(foo);
func(); // 1
//在Javascript中,多次bind()是无效的。更深层次的原因,bind()的实现,相当于使用函数在内部包了一个call/apply,第二次bind()相当于再包住第一次bind(),故第二次以后的bind是无法生效的
var bar = function() {
console.log(this.x);
}
var foo = {
x: 3
}
var sed = {
x: 4
}
var func = bar.bind(foo).bind(sed);
func(); //3
var fiv = {
x: 5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //3
- 附上bind实现的源码实现,其本质是内部调用apply
// the .bind method from prototype.js
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments),object = args.shift();
return function(){
return fn.apply(object,args.concat(Array.prototype.slice.call(arguments)));
}
}