call
、apply
、bind
有一个共同作用,绑定函数的this
,对于this
的绑定,小黄书(《你不知道的JavaScript》)介绍了4种绑定方式。
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
这3个函数就属于第3种情况。
先来看看调用原生call
时第一个参数不是对象的时候 this
的绑定情况
function test(){
console.log('this',this)
}
test.call() //this Window
test.call(undefined) //this Window
test.call(null) //this Window
test.call(123) //this Number{123}
test.call('abc') //this String{"abc"}
test.call(true) //this Boolean{true}
复制代码
可以看出,第一个参数传undefined
或null
时,this
指向全局对象,浏览器环境下为window
对象。第一个参数传其他基本类型(原始类型)时会自动装箱使其变为对象。这一点很重要。
call、apply
为将基本类型转为对应的引用类型,需要利用到它的构造函数
const a = 1;
const aObj = new a.constructor(a);
console.log(aObj); // Number{1}
复制代码
其他基本类型也是如此。
call
函数实现如下。
Function.prototype.myCall = function(context, ...args){
if(typeof this !== 'function'){
throw Error('caller must be a function!')
}
//待绑定的this上下文
let _context = context;
//传入this为undefined或null,待绑定的this上下文指向全局对象
if(_context === undefined || _context === null){
_context = globalThis;
}
//传入this为基本类型时,将其转为对应的引用类型
if(typeof _context !== 'object'){
_context = new _context.constructor(context)
}
_context.func = this;
//调用函数并绑定this
const res = _context.func(...args);
delete _context.func;
//返回执行结果
return res;
}
复制代码
apply
和call
很相似,区别在于参数传递方式
func.call(obj,arg1,agr2,...);
func.apply(obj,[arg1,arg2,...])
复制代码
apply
函数实现如下。
Function.prototype.myApply = function(context, args){
if(typeof this !== 'function'){
throw Error('caller must be a function!')
}
//待绑定的this上下文
let _context = context;
//传入this为undefined或null,待绑定的this上下文指向全局对象
if(_context === undefined || _context === null){
_context = globalThis;
}
//传入this为基本类型时,将其转为对应的引用类型
if(typeof _context !== 'object'){
_context = new _context.constructor(context)
}
let _args = args;
if(_args === undefined || _args === null){
_args = []
} else if(!Array.isArray(args)){
throw new TypeError("CreateListFromArrayLike called on non-object")
}
_context.func = this;
//调用函数并绑定this
const res = _context.func(..._args);
delete _context.func;
//返回执行结果
return res;
}
复制代码
bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用
Function.prototype.myBind = function (context,...args) {
if (typeof this !== 'function') {
throw new TypeError('caller must be a function!');
}
//待绑定的函数
let toBindFunc = this
// 返回一个函数
return function boundFunction() {
// 因为返回了一个函数,可能会通过new调用
if (this instanceof boundFunction) {
//传入的参数覆盖默认参数
return new toBindFunc(...args, ...arguments)
}
//传入的参数覆盖默认参数
return toBindFunc.apply(context, args.concat(...arguments))
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END