apply 和 call 方法的作用:
这两个方法的用途是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值
格式:
call(对象,参数1,参数2,...)
apply(对象,[数组]) 注:数组也可以是arguments
- 实例一
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
// 传入arguments对象
return sum.apply(this, arguments);
}
function callSum2(num1, num2){
// 传入数组
return sum.apply(this, [num1, num2]);
}
//20
alert(callSum1(10,10));
//40
alert(callSum2(20,20));
- 实例二
function fun1(...args) {
console.log(1)
console.log(...args)
}
function fun2() {
console.log(2)
fun1.call(this, 2, 3)
fun1.apply(this, [4, 5])
}
fun2()
/*
2
1
2 3
1
4 5
*/
- 实例三
// 它的目的不在于添加传递参数,真正强大的地方是能够扩充函数依赖以运行的作用域
window.color = 'red'
let obj = { color: "blue" }
function sayColor() {
console.log(this.color)
}
sayColor() // red
sayColor.call(this) // red
sayColor.call(window) // red
sayColor.call(obj) // blue
bind
bind 函数会创建一个新绑定函数(bound function,BF)。绑定函数是一个exotic function object(怪异函数对象,ECMAScript 2015中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。
bind 方法创建一个新的函数,在调用时设置 this 关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。
语法
function.bind(thisArg[, arg1[, arg2[, ...]]])
- thisArg
调用绑定函数时作为this参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用bind在setTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果bind函数的参数列表为空,执行作用域的this将被视为新函数的thisArg。 - arg1, arg2, …
当目标函数被调用时,预先添加到绑定函数的参数列表中的参数。
let writen = document.write
writen('hello')// error
writen.bind(document)('hello')// right
创建绑定函数
var module = {
x: 42,
getX: function() {
return this.x;
}
}
var unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined
var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
包装 Array.prototype.slice 方法
function fun() {
console.log(Array.prototype.slice.call(arguments)) // (3) [1, 2, 3]
let slice = arg => Array.prototype.slice.apply(arg)
console.log(slice(arguments)) // (3) [1, 2, 3]
// 利用 bind 并不先执行,可以包装 Array.prototype.slice 方法
let unboundSlice = Array.prototype.slice;
let slice1 = Function.prototype.apply.bind(unboundSlice);
console.log(slice1(arguments)) // (3) [1, 2, 3]
}
fun(1, 2, 3)
更多实例参见:bind
// 手写 bind
Function.prototype.myBind = function (oThis, ...args) {
if (typeof oThis !== 'object') return
let [_self, func] = [this, function () {}]
let fBind = function () {
return _self.apply(this instanceof _self ? this : oThis, [...args, ...arguments])
}
if(_self.prototype){ // 维护原型关系
func.prototype = _self.prototype
fBind.prototype = new func()
}
return fBind
}
function add (...args) {
console.log(this.join(' '), args.reduce((a, b) => a + b))
}
let obj = ['hello', 'world']
let fun = add.myBind(obj, 1, 2, 3, 4)
fun(5) // hello 15
// vue 提供重写 bind
function polyfillBind(fn, ctx) {
function boundFn(a) {
var l = arguments.length;
/**
* 如果参数不存在,直接绑定作用域调用该函数fn.call(ctx)
* 如果存在且只有一个,那么调用fn.call(ctx, a), a是入参
* 如果存在且不止一个,那么调用fn.apply(ctx, arguments)
*/
return l
? l > 1
? fn.apply(ctx, arguments)
: fn.call(ctx, a)
: fn.call(ctx)
}
boundFn._length = fn.length;
return boundFn
}
function nativeBind(fn, ctx) {
return fn.bind(ctx)
}
var bind = Function.prototype.bind
? nativeBind
: polyfillBind;
bind、apply 和 call 三者区别
apply 和 call 的区别就是传参形式不同:
- apply 仅接收两个参数,目标对象和数组,数组就是该函数的传参
- call 接收多个参数,第一个为目标对象,后面的数值都是函数的传参
bind 与前两者的区别:
- bind 方法会返回执行上下文被改变的函数而不会立即执行,而前两者是直接执行该函数。他的参数形式和 call 相同。
function fun (...args) {
console.log(args)
}
let obj = {}
fun.call(obj, 1, 2, 3)// (3) [1, 2, 3]
fun.apply(obj, [4, 5, 6])// (3) [4, 5, 6]
let save = fun.bind(obj, 7, 8, 9)// 在这里并不会立即执行 fun 函数
save()// (3) [7, 8, 9]
真数组和伪数组互相转换
真数组:var arr=[1,2,3,4];
伪数组:var obj={0:'onj',1:'hello',length:2}
(注:最后的length不可少)
/*真数组转换伪数组方法*/
var ar=[1,3,4,5,6];
var ob={};
/*
1、通过[].push找到数组中的push方法
2、通过apply(ob)将找到的push方法内部的this修改为自定义的对象
3、将传入数组中的元素依次取出,传递给形参ob
*/
[].push.apply(ob,ar);//推荐
console.log(ob);/*{0: 1, 1: 3, 2: 4, 3: 5, 4: 6, length: 5}*/
/*伪数组转换真数组方法*/
//系统自带伪数组
var res=document.querySelectorAll('div');
//自定义伪数组
var obj={0:'lng',1:'af',length:2};
//伪数组转真数组
var arr=[].slice.call(obj);//推荐
var arr1=[].slice.call(res);
/*
var arr=[];
[].push.apply(arr,res);//也可以 但不兼容IE8及以下
*/
console.log(arr1);//(3) [div, div, div]
console.log(arr);//(2) ["lng", "af"]
var arr3=[1,2,3,4];
console.log(arr3);//(4) [1, 2, 3, 4]
// vue 中伪数组转真数组
function toArray(list, start) {
start = start || 0;
var i = list.length - start;
var ret = new Array(i);
while (i--) {
ret[i] = list[i + start];
}
return ret
}
函数柯里化
function curry(fn) {
let args = Array.prototype.slice.call(arguments, 1)
return function () {
let innerArgs = Array.prototype.slice.call(arguments)
let finalArgs = args.concat(innerArgs)
return fn.apply(null, finalArgs)
}
}
function add(num1, num2) {
return num1 + num2
}
console.log(curry(add, 5)(4)); // 9