这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
bind和apply以及call函数细讲
call()、apply()、bind() 都是用来重定义 this 这个对象的。这三个函数都是定义在函数原型对象上的方法,其主要的用途是改变this的指向,并且调用函数,但是bind会返回一个改变bind后的函数。下面我们将详细聊一下三个函数的作用,以及其内部实现。
零、What this
当一个函数被保存为对象的一个方法时,如果调用表达式包含一个提取属性的动作, 那么它就是被当做一个方法来调用,此时的this被绑定到这个对象。 补充一下,DOM对象绑定事件也属于方法调用模式,因此它绑定的this就是事件源DOM对象。
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
// 点击页面,依次输出:document和window对象
// 解析:点击页面监听click事件属于方法调用,this指向事件源DOM对象,即obj.fn.apply(obj),
// setTimeout内的函数属于回调函数,
// 可以这么理解,f1.call(null,f2),所以this指向window。
一、call和apply
(1)、传入参数不同
call函数可以传入的参数没有限制,第一个参数为this指向的值,第二个参数一直到最后一个参数都是传入原函数的参数。apply需要传入两个参数,第一个参数为this指向的对象,第二个参数是需要传入原函数的参数,是一个数组或者伪数组。
(2)、当第一个参数为null
对于call和apply函数,如果第一个参数为null,那么该函数指向默认宿主对象,在浏览器中会指向window对象。
//在node环境下
function foo(a, b) {<!-- -->
console.log(this)
}
foo.call(null, 10, 20) //global
//在浏览器环境中
foo.call(null, 10, 20) //window
在严格模式下,其中的this都指向null。
function foo(a, b) {<!-- -->
"use strict"
console.log(this)
}
foo.call(null) //null
(3)、 有时候我们使用call,apply其目的不只是改变this,而是调用对象上面的方法,此时我们传入null,就可以表示一个具体的对象。
let nums = Math.max.apply(null, [12,34,56,2,11])
console.log(nums) //56
call 、bind 、 apply 这三个函数的第一个参数都是this的指向对象,第二个参数差别就来了:
call的参数是直接放进去的第二第三第n个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'', ... ,'' )。
apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['', ..., '' ])。
bind 除了返回是函数以外,它的参数和call 一样。
当然,三者的参数不限定是string类型,允许是各种类型,包括函数、object 等等
二、手动实现call函数
//手动实现call
Function.prototype.myCall = function (obj, ...array) {<!-- -->
let fn = this
let thisArg = (obj === undefined || obj === null) ? window : Object(obj)
array = Array.isArray(array) ? array : []
thisArg.fn = fn
let result = thisArg.fn(...array)
delete thisArg.fn
return result
}
三、手动实现apply函数
//手动实现apply
Function.prototype.myApply = function (obj, array) {<!-- -->
let fn = this
let thisArg = (obj === undefined || obj === null) ? window : Object(obj)
thisArg.fn = fn
array = Array.isArray(array) ? array : []
let result = thisArg.fn(...array)
delete thisArg.fn
return result
}
四、手动实现bind函数
bind返回得是一个函数,就是将this改变后的函数。
Function.prototype.myBind = function (obj, ...arg1) {<!-- -->
let fn = this
let thisArg = (obj === undefined || obj === null) ? window : Object(obj)
function proxyFn(...arg2) {<!-- -->
thisArg.fn = fn
let arg3 = [...arg1, ...arg2]
let result = thisArg.fn(...arg3)
delete thisArg.fn
return result
}
return proxyFn
}
五、arguments的应用
arguments是一个伪数组,但是有时候我们需要使用数组的方法,在ES6之前我们可以使用call方法来调用,但是在ES6之后我们可以使用Array.from来将其转换为数组。