写在前: 开始这三者的探索之前,我们先了解下arguments以及arguments转换为数组的方法
定义:arguments 是一个对应于传递给函数的参数的类数组对象。
使用场景:只能在函数中使用
举个例子:
typeof arguments // undefined
// arguments 对象只能在函数内使用
function list(param) {
console.log(Object.prototype.toString.call(arguments)) // [object Arguments]
console.log(arguments[0],arguments[1]) // 1 undefined
console.log(typeof arguments[0]) // number
var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null,arguments)
console.log(args) // [1]
}
list(1)
类数组:不是真正的数组,除了有length和下标,没有Array任何的属性,比如pop,shift等
转换数组:类数组可以转化成真正的数组
转换方法:
1、var arr = Array.prototype.slice.call(arguments)
2、var arr = [].slice(arguments)
3、es5:
var arr = Array.from(arguments)
var arr = [...arguments] // 扩展运算符
4、var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null,arguments)
下面进入正题:
call()
定义:call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
原型:Function.prototype.call()
场景1:
var obj = {
name: 'zhangsan',
getName: function () {
return this.name
}
}
obj.getName.call(obj)
// Output: zhangsan
场景2:
var a = '我是全局的'
var obj = {
name: 'zhangsan',
getName: function () {
return this.name
}
}
obj.getName.call()
// Output: 我是全局的
场景三:
var obj = {
name: 'zhangsan',
getName: function (param1,param2) {
return this.name + ':' + param1.age + ' 喜欢颜色:' + param2.color
}
}
obj.getName.call(obj, {age: 26},{color: '红色'})
// Output: zhangsan:26 喜欢颜色:红色
场景四:
var obj = {
name: 'zhangsan',
getName: function (param1,param2) {
return this.name + ':' + param1.age + ' 喜欢颜色:' + param2.color
}
}
obj.getName.call(null, {age: 26},{color: '红色'})
// Output: 我是全局的:26 喜欢颜色:红色
场景5:
Math.max.call(Math, 1,6,5) // 6
Math.min.call(Math, 1,6,5) // 1
Math.max.call(Math, ...[1,6,5]) // 6
场景6:转换数组,验证是否是数组
Array.call(this,1,2,3) // [1,2,3]
Array.prototype.map.call('3444',function (v) {
return v
})
// Output: ["3", "4", "4", "4"]
function list() {
Array.prototype.slice.call(arguments)
}
list(1,2,3)
// Output: [1,2,3]
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
isArray([1,2,3]) // true
apply()
定义:apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
原型:Function.prototype.apply()
使用场景:
只举一个例子:
var obj = [1,2,3]
function getParam (param1,param2,param3) {
console.log(param1) // 1
console.log(param2) // 2
console.log(param3) // 3
}
getParam.apply(null, obj)
使用场景与call类似,只是把参数列表改为数组即可,不做赘述
bind()
定义:创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。
原型:Function.prototype.bind()
使用场景:
function.bind(thisArg[, arg1[, arg2[, ...]]])
第一个参数传递的情况
看个例子:
var module = {
x: 42,
getX: function() {
return this.x;
}
}
var unboundGetX = module.getX;
console.log(unboundGetX()); // 全局调用this指向全局的windows
// expected output: undefined 全局找不到x所以这里返回undefined
var boundGetX = unboundGetX.bind(module); // 把unboundGetX绑定到module原函数,返回副本的拷贝
console.log(boundGetX());
// expected output: 42 调用的时候原函数的module的this指向绑定函数的引用【及unboundGetX = module.getX】上下文 x:42 所以这里输出42
第一个参数为null
var y = 88
var module = {
x: 42,
getX: function() {
return this.x;
}
}
var unboundGetX = module.getX;
console.log(unboundGetX()); // 全局调用: this指向全局的windows这里是y变量
// expected output: 88 全局找到了变量y 输出88
var boundGetX = unboundGetX.bind(null); // 原函数为null不传递
console.log(boundGetX()); // 调用新函数boundGetX,执行绑定的目标函数unboundGetX,目标函数调用全局的window,这里找到y,所以最终输出88
// expected output: 88
再看一个稍微复杂点的例子:
偏函数:
// 定义一个返回真数组的方法 类数组->真正数组的转换
function list() {
return Array.prototype.slice.call(arguments);
}
function addArguments(arg1, arg2) {
return arg1 + arg2
}
var list1 = list(1, 2, 3); // [1, 2, 3]
var result1 = addArguments(1, 2); // 3
// 创建一个函数,它拥有预设参数列表。
var leadingThirtysevenList = list.bind(null, 37); // 返回新函数
// 创建一个函数,它拥有预设的第一个参数
var addThirtySeven = addArguments.bind(null, 37);
var list2 = leadingThirtysevenList(); // 调用函数
// [37]
var list3 = leadingThirtysevenList(1, 2, 3); // 调用函数传递参数列表
// [37, 1, 2, 3]
var result2 = addThirtySeven(5);
// 37 + 5 = 42
var result3 = addThirtySeven(5, 10); // 目标函数addArguments只接收两个函数,所以这里的第二个函数被忽略
// 37 + 5 = 42
改造下:
// 创建一个函数,它拥有预设的第一个参数
var addThirtySeven = addArguments.bind(null);
var result3 = addThirtySeven(5, 10); // 5 + 10 = 15 结果为15
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice);
// ...
function list () {
var a = slice(arguments);
console.log(a) // [1,2,3]
}
list(1,2,3)
等价与 Array.prototype.slice.apply(arguments)
function list () {
return Array.prototype.slice.apply(arguments)
}
list(1,2,3) // [1,2,3]
总结:
不同点:call、apply唯一的区别是传参的类型不同,前者为参数列表,后者为参数列表组成的数组,bind返回新函数,需要再次调用函数,call、apply改变对象的执行上下文,并且立即执行
相同点:改变被调用函数当前this的指向【改变对象执行的上下文】