1、三者的区别
call()
方法接受的是参数列表,需要一个一个的传apply()
方法接受的是一个参数数组,bind()
和call()
函数的传参是一样的,但是bind()
返回的是一个function
,不能立即执行。
2、call
call()
方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
call的语法
fun.call(thisArg, arg1, arg2, ...)
参数
-
thisArg
在
fun
函数运行时指定的this
值。if(thisArg == undefined|null) this = window,if(thisArg == number|boolean|string) this == new Number()|new Boolean()| new String()
-
arg1
,arg2
,...
指定的参数列表。
call的应用
使用 call 方法调用父构造函数实现继承
在该例子中,子构造函数Sun()
通过调用父构造函数Father()
的 call
方法来实现继承,此时Sun()
函数中拥有了Father()
函数中的属性。
function Father(name,age){
this.name = name;
this.age = age;
this.book=['数据结构','计算机网络','算法']
console.log('1.执行到Father---名字:'+this.name+'; 年龄:'+this.age+' 书籍:'+this.book);
}
function Sun(name,age){
Father.call(this,name,age);
this.subject = '软件专业';
console.log('2.执行到Sun---专业:'+this.subject);
}
var sun = new Sun('gxm','20');
sun.book.push('javaScript');
console.log('3.改变sun.book:'+sun.book);
var sun2 = new Sun('xz','22');
运行结果
1.执行到Father---名字:gxm; 年龄:20 书籍:数据结构,计算机网络,算法
2.执行到Sun---专业:软件专业
3.改变sun.book:数据结构,计算机网络,算法,javaScript
1.执行到Father---名字:xz; 年龄:22 书籍:数据结构,计算机网络,算法
2.执行到Sun---专业:软件专业
3.查看sun2.book:数据结构,计算机网络,算法
从这个运行结果也可以看出,new出来的实例对其继承而来的属性进行改变,对于原函数来说并没有改变。
使用 call 方法调用函数并且指定上下文的 this
function play() {
var reply = [this.player, '打', this.playName,'打了',this.playTimes].join(' ');
console.log(reply);
}
var obj = {
player: 'gxm',playName: '羽毛球', playTimes: '1小时'
};
play.call(obj); //gxm 打 羽毛球 打了 1小时
如果直接运行play()
函数,结果就只会是打 打了
。但指定了上下文就不一样了,play.call(obj)
就是将play()
函数的上下文从全局的window
指定到obj
对象上了。所以运行出来的结果过成为gxm 打 羽毛球 打了 1小时
。
思考下面的代码之中有几个this:
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
上面代码之中,只有一个this,就是函数foo的this,所以t1、t2、t3都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的this,它们的this其实都是最外层foo函数的this。
使用 call 方法调用函数并且不指定第一个参数(argument)节
在下面的例子中,我们调用了 display
方法,但并没有传递它的第一个参数。如果没有传递第一个参数,this
的值将会被绑定为全局对象。
var sData = 'Wisen';
function display() {
console.log('sData value is %s ', this.sData);
}
display.call(); // sData value is Wisen
注意:在严格模式下,this 的值将会是 undefined。见下文。
'use strict';
var sData = 'Wisen';
function display() {
console.log('sData value is %s ', this.sData);
}
display.call(); // Cannot read the property of 'sData' of undefined
3、apply
apply()
方法调用一个具有给定this
值的函数,以及作为一个数组(或类似数组对象)提供的参数。
apply语法
func.apply(thisArg, [argsArray])
参数
-
thisArg
可选的。在
func
函数运行时使用的this
值。请注意,this
可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为null
或undefined
时会自动替换为指向全局对象,原始值会被包装。 -
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给
func
函数。如果该参数的值为null
或undefined
,则表示不需要传入任何参数。从ECMAScript 5
开始可以使用类数组对象。
apply的应用
apply实现继承
function superType() {
this.color = ['red','blue','yellow']
}
function subType() {
//---继承了superType---
superType.applay(this);
//superType.call(this);
}
var instance1 = new subType();
instance1.color.push('green');
console.log('instance1.color:', instance1.color);
//instance1.color: (4) ["red", "blue", "yellow", "green"]
用 apply 将数组添加到另一个数组
我们可以使用push
将元素追加到数组中。并且,因为push接受可变数量的参数,我们也可以一次推送多个元素。
但是,如果我们传递一个数组来推送,它实际上会将该数组作为单个元素添加,而不是单独添加元素,因此我们最终得到一个数组内的数组。如果那不是我们想要的怎么办?在这种情况下,concat确实具有我们想要的行为,但它实际上并不附加到现有数组,而是创建并返回一个新数组。
但是我们想要附加到我们现有的阵列......那么现在呢? 写一个循环?当然不是吗?
apply来帮你!
var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
3、bind
语法
function.bind(thisArg[, arg1[, arg2[, ...]]])
参数
-
thisArg
调用绑定函数时作为
this
参数传递给目标函数的值。 如果使用new
运算符构造绑定函数,则忽略该值。当使用bind
在setTimeout
中创建一个函数(作为回调提供)时,作为thisArg
传递的任何原始值都将转换为object
。如果bind
函数的参数列表为空,或者thisArg
是null
或undefined
,执行作用域的this
将被视为新函数的thisArg
。 -
arg1, arg2, ...
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
返回值
返回一个原函数的拷贝,并拥有指定的 this
值和初始参数。
4、手撕call、bind、apply
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call(foo); // 1
bar.apply(foo); // 1
let a = bar.bind(foo);
a();
call
Function.prototype._call=function (context, ...arg) {
context = context || window;
let fn = Symbol();
context[fn] = this;//这里的意思是将bar赋给obj.fn属性
let res = context[fn](...arg);
delete context[fn];
return res;
}
apply
Function.prototype._apply=function (context, arg=[]) {
context = context || window;
let fn = Symbol();
context[fn] = this;//这里的意思是将bar赋给obj.fn属性
let res = context[fn](...arg);
delete context[fn];
return res;
}
bind
Function.prototype._bind = function (context, ...arg) {
context = context || window;
let fn = this;//这里的意思是将bar赋给fn属性
return function(...others){
fn.apply(context, [...arg, ...others])
}
}
5、类数组转换成数组的方式
- Array.prototype.slice.call( arguments );
- [...arguments]
- Array.from(arguments)
- 还有一个方法就是第一个方法的延伸,结合了bind方法 slice 是 Function.prototype (en-US) 的 call() 方法的绑定函数,并且将 Array.prototype 的 slice() 方法作为 this 的值。
// 与前一段代码的 "slice" 效果相同
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice);
// ...
slice(arguments);