概述
call、apply、bind均可以改变函数的this指向,其中call、apply会改变函数的this指向并执行函数,而bind只会改变函数的this指向并不会执行函数。这三个方法的第一个参数同样是指this。如果函数的调用需要传递参数,call的第二个参数,第三个参数……传递的是函数调用的参数;apply的第二个参数是一个数组,包含要传递的全部参数。
fn.call(target, arg1, arg2, ...)
fn.apply(target, [arg1, arg2, ...])
fn.bind(target)
call
Function.prototype.myCall=function(context){
if(typeof this!=='function'){
throw new TypeError('Error');
}
context = context || window;
context.fn = this;
const args = [...arguments].slice(1);
const result = context.fn(...args);
delete context.fn;
return result;
}
复制代码
详解:
-
context = context || window;
在ECMAScript 3和非严格模式中,传入call的第一个参数,如果传入的值为null或者undefined都会被全局对象代替,而其他的原始值则会被相应的包装对象(wrapper object)所替代。所有的引用类型(array/object/function),都具有对象特性,即可*扩展属性(null除外) ,所以除了引用类型和null/undefined外,其他数据类型要转化为相应的包装类型,使其可以扩展属性。
此处可做优化
const type = typeof context; if(context === undefined || context === null){ type = 'window'; } switch(type){ case 'window': context = window; break; case 'number': context = Number(context); break; case 'string': context = String(context); break; case 'boolean': context = Boolean(context); break; default: context = context; } 复制代码
-
context.fn = this;
此处的this是要改变this指向的函数本身,将这个函数作为context的一个属性,可以改变这个函数的this指向。
apply
Function.prototype.myApply = function(context){
if(typeof this !== 'function'){
throw new TypeError('Error');
}
context = context || window;
context.fn = this;
let result;
if(arguments[1]){
result = context.fn(...arguments[1]);
}else{
result = context.fn();
}
delete context.fn;
return result;
}
复制代码
详解:同上。
bind
Function.prototype.myBind = function(context){
if(typeof this !== 'function'){
throw new TypeError('Error');
}
const _this = this;
const argus = [...arguments].slice(1);
return function F(){
//因为返回了一个函数,可以new F(),所以需要判断
if(this instanceof F){
return new _this(...argus,...arguments);
}
return _this.apply(context,argus.concat(...arguments));
}
}
复制代码
详解:
-
其它知识:用new创建构造函数的实例,会经历一下四个步骤:
1).创建一个新对象;
2).将这个构造函数的作用域赋给新对象(因此this就指向了这个新对象)
3).执行构造函数中的代码(为这个新对象添加属性);
4).返回新对象。
-
bind返回的是一个函数(闭包)
-
this instanceof F
和return new _this(...argus,...arguments);
判断this是不是通过new F(),如果是的话相当于执行new fnName()(即new _this()),此时会创建一个该函数的新对象,此时的this指向了这个新对象,并且执行构造函数中的代码(带上参数)。
-
return _this.apply(context,argus.concat(...arguments));
此处执行myBind返回的闭包,原理同apply。