本文已参与「新人创作礼」活动,一起开启掘金创作之路。
call、apply、bind
是什么?
这三个函数的存在意义是什么?
改变函数执行时的上下文,再具体一点就是改变函数执行时的this指向- call/apply改变了函数的this上下文后马上
执行该函数 - bind则是
返回改变了上下文后的函数,不执行该函数
语法
/**
* thisArg(可选): foo的this指向thisArg对象
* param1,param2(可选): 传给 foo的参数
*/
foo.call(thisArg, param1, param2, ...) // 返回foo函数执行的结果
foo.apply(thisArg, [param1, param2, ...]) // 返回foo函数执行的结果
foo.bind(thisArg, param1, param2, ...) // 返回foo函数的拷贝,并拥有指定的this值和初始参数
需要注意的是,是否严格模式对this的影响,以call为例:
严格模式下,this一定指向thisArg中的值
"use strict"
function foo() {
console.log(this, '[][][]')
}
var obj = { a: 1 };
foo.call() // undefined
foo.call(undefined) // undefined
foo.call(null) // null
foo.call(obj) // {a: 1}
非严格模式下,this指向thisArg对象,但是如果thisArg是null、undefined,fun中的this指向window对象
function foo() {
console.log(this, '[][][]')
}
var obj = { a: 1 };
foo.call() // window
foo.call(undefined) // window
foo.call(null) // window
foo.call(obj) // {a: 1}
从哪来?
这三个函数从哪来?
- 继承自Function.prototype
正因为这三个方法在Function的实例原型上,所以
调用call/apply/bind的必须是个函数
- call与apply的唯一区别
- 传参不同;call传给foo函数的参数是第2~n的参数,apply的第2个参数是一个数组,里面的每一项值是foo函数的参数
- call/apply与bind的区别
- call/apply改变了函数的this上下文后马上
执行该函数 - bind则是
返回改变了上下文后的拷贝函数,不执行该函数
到哪去?
自己实现这3个方法
- 实现call,代码如下 容易理解版本,未考虑过多情况
Function.prototype.myCall = function (thisArg, ...args) {
// 获取需要被执行的函数
var fn = this;
// 传入的为 null 和 undefined,就自动指向全局对象(浏览器中为window),否则转为对象
// 对thisArg转成对象类型(防止传入的是非对象类型)
if (thisArg === null || thisArg === undefined) {
thisArg = window
} else {
thisArg = Object(thisArg)
}
// 改变函数的this指向
thisArg.fn = fn
// 调用需要执行的函数
var result = thisArg.fn(...args)
delete thisArg.fn
// 返回函数执行结果
return result
}
考虑边界稍微多点的情况
Function.prototype.myCall = function (thisArg, ...args) {
// 传入的为 null 和 undefined,就自动指向全局对象(浏览器中为window),否则转为对象
// 对thisArg转成对象类型(防止传入的是非对象类型)
if (thisArg === null || thisArg === undefined) {
thisArg = window
} else {
thisArg = Object(thisArg)
}
// 设置唯一的key,避免和传递过来的key一样
var temporaryKey = Symbol('fn')
// 改变函数的this指向,获取需要被执行的函数
thisArg[temporaryKey] = this
// 调用需要执行的函数
var result = thisArg[temporaryKey](...args)
delete thisArg[temporaryKey]
// 返回函数执行结果
return result
}
- 实现apply,代码如下:
Function.prototype.myApply = function (thisArg, args) {
// 传入的为 null 和 undefined,就自动指向全局对象(浏览器中为window),否则转为对象
// 对thisArg转成对象类型(防止传入的是非对象类型)
if (thisArg === null || thisArg === undefined) {
thisArg = window
} else {
thisArg = Object(thisArg)
}
// 设置唯一的key,避免和传递过来的key一样
var temporaryKey = Symbol('fn')
// 改变函数的this指向,获取需要被执行的函数
thisArg[temporaryKey] = this
// 调用需要执行的函数
// 是否传参了
args = args ? args : [];
var result = thisArg[temporaryKey](...args)
delete thisArg[temporaryKey]
// 返回函数执行结果
return result
}
- 实现bind,代码如下(未仔细考虑边界情况)
Function.prototype.myBind = function (thisArg, ...args) {
// 传入的为 null 和 undefined,就自动指向全局对象(浏览器中为window),否则转为对象
// 对thisArg转成对象类型(防止传入的是非对象类型)
thisArg = [null, undefined].includes(thisArg) ? window : Object(thisArg);
function proxyFn(...proxyArgs) {
// 将函数放到thisArg中进行调用
thisArg.fn = fn
var finalArgs = [...args, ...proxyArgs]
var result = thisArg.fn(...finalArgs)
delete thisArg.fn
// 返回结果
return result
}
// 返回函数果
return proxyFn
}