call、apply、bind应用的介绍
call,apply,bind三者都是改变当前this的指向,但具体的使用方式,都存在着一定的区别,下面将从使用方式与源码两个角度来简单的阐述一下之间的区别。
使用简介
call
call() 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。
注意:该方法的作用和 `apply()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。
语法:
fun.call(thisArg[, arg1[, arg2[, ...]]])
参数:
-
thisArg- 在 fun 函数运行时指定的 this 值
- 如果指定了 null 或者 undefined 则内部 this 指向 window
-
arg1, arg2, ...- 指定的参数列表
apply
apply() 方法调用一个函数, 其具有一个指定的 this 值,以及作为一个数组(或类似数组的对象)提供的参数。
注意:该方法的作用和 `call()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。
语法:
fun.apply(thisArg, [argsArray])
参数:
thisArgargsArray
apply() 与 call() 非常相似,不同之处在于提供参数的方式。
apply() 使用参数数组而不是一组参数列表。例如:
fun.apply(this, ['eat', 'bananas'])
**call 与apply的区别:**
apply、call 的区别
对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:
var func = function(arg1, arg2) {
};
就可以通过如下方式来调用:
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。
bind
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。 当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。 一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
语法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数:
-
thisArg
- 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
-
arg1, arg2, ...
- 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
返回值:
返回由指定的this值和初始化参数改造的原函数拷贝。
示例:
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom(); // 一秒钟后, 调用'declare'方法
小结
-
call 和 apply 特性一样
- 都是用来调用函数,而且是立即调用
- 但是可以在调用函数的同时,通过第一个参数指定函数内部
this的指向 - call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
- apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
- 如果第一个参数指定了
null或者undefined则内部 this 指向 window
-
bind
- 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
- 它和 call、apply 最大的区别是:bind 不会调用
- bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
-
- 在 bind 的同时,以参数列表的形式进行传递
-
- 在调用的时候,以参数列表的形式进行传递
- 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
- 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部
-
源码介绍
call源码
call的使用
- call可以改变前面函数中的this指向,指向call方法中的
第一个参数 - 给方法传递参数,可以
依次进行传值 - call的第1个参数是
基本数据类型,this指向创建这个基本数据类型的构造器 - call的第1个参数可以是函数
简单call原理
没有参数传递的原理, x.cal(y) :在y 上挂载一个 x , 在y内部调用 x ,最终x内的x的指向y,
//定义一个调用者
function f(a, b, c) {
return a + b + c
}
// 自定义call方法
Function.prototype.call = function (context) {
// 将当前的调用者挂到第一个参数上
context.fn = this;
console.log(arguments);
// 将传递的参数存放到一个数组里面
let args = []
for (let i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
// eval:可以执行里面的js代码
let r = eval('context.fn(' + args + ')')
// 删挂载上的那个函数
delete context.fn;
return r;
}
// 测试
let obj = { name: 'wangcai' };
let result = f.call(obj, 1, 2, 3)
console.log(result);
call没有参数的问题,防止出错
当call没有参数的时候,程序可能会报错
function f(){
....
}
Function.prototype.call = function(context){
context = context ? Object(context) : window;
....
}
let result = f.call()
....
注意:
特别提醒:运行环境是在浏览器 在node中不支持
apply源码
apply
apply与call的原理一直,就是传递参数的方式不一样,此处不过多的进行阐述
function f(a,b,c,d) {
console.log("f.....");
console.log(this);
return a+b+c+d
}
Function.prototype.apply = function (context) {
//将调用者挂载到参数上
context.fn = this;
// 获取到传递的参数
let args =arguments[1]
// eval:内的js代码会直接执行
let r = eval("context.fn(" + args + ')')
delete context.fn;
return r;
}
// 测试
let obj = { name: 'wangcai' }
let res = f.apply(obj,[1,2,3,4])
console.log(res);
bind源码
bind原理
- bind可以改变前面函数中的this指向,指向call方法中的第一个参数
- 这点与call一样,但是bind返回一个绑定后的函数
- bind需要自己进行调用,不会自己调用
- bind中绑定时传递的参数的优先级比调用时传递的参数的优先级高
- 优先使用绑定的参数,后续使用调用传递的参数
简单的bind原理
bind主要是借助于
call或者bind进行数据的绑定,且进行对应的数据参数的传递
// 定义一个普通的函数
function f() {
console.log("f..");
console.log(this);
}
// 重写bind的这个方法
Function.prototype.bind = function (context) {
let that = this;
// 获取绑定时的参数
let newArr = Array.prototype.slice.call(arguments, 1);
return function () {
// 获取调用时的参数 将arguments伪数组转成真实的数组
let newArr2 = Array.prototype.slice.call(arguments)
return that.apply(context, newArr.concat(newArr2))
}
}
// 测试
let obj = { name: 'wangcai' }
let newF = f.bind(obj, 55, 66)
newF(1, 2, 3);
注意:
当绑定的时候进行传值与新函数调用时传值
同时有的时候,绑定的值优先与新函数的值, 整个的参数个数是:绑定参数+新函数参数