call、bind、apply
作用
改变函数执行上下文,即函数运行时this的指向。
实质
就是重新给函数绑定参数。
区别
call、apply的区别:接受参数的方式不一样。
-
call 第一个参数是 this 的指向,从第二个参数开始是接收的参数列表
-
apply 第一个参数是要绑定给 this 的值,第二个参数是一个参数数组。当第一个参数为 null、undefined的时候,默认指向window.
-
都是立即执行
bind和call 类似。 第一个参数是 this 的指向,从第二个参数开始是接收的参数列表。区别在于 bind 方法返回值是函数以及 bind 接收的参数列表的使用。
模拟实现call
- 判断当前this是否为函数,防止Function.prototype.myCall() 直接调用
- context 为可选参数,如果不传的话默认上下文为 window
- 为context 创建一个 Symbol(保证不会重名)属性,将当前函数赋值给这个属性
- 处理参数,传入第一个参数后的其余参数
- 调用函数后即删除该Symbol属性
Function.prototype.myCall = function(content = window, ...args) {
if(this === Function.prototype) return undefined; // 判断当前this是否为函数,防止Function.prototype.myCall() 直接调用
if(typeof this !== 'function') throw new TypeError('The this must be a Function'); // 必须为函数
const fn = Symbol();
content[fn] = this;
const result = content[fn](...args);
delete context[fn]
return result;
}
复制代码
模拟实现apply
Function.prototype.myApply = function(content = window, args) {
if(this === Function.prototype) return undefined; // 判断当前this是否为函数,防止Function.prototype.myCall() 直接调用
if(typeof this !== 'function') throw new TypeError('The `this` must be a `Function`'); // 必须为函数
const fn = Symbol();
content[fn] = this;
const result = Array.isArray(args) ? content[fn](...args) : content[fn](); // 参数为数组
delete content[fn]
return result;
}
复制代码
模拟实现bind
- 处理参数,返回一个闭包
- 返回的新函数也可以使用new操作符,所以在新函数内部需要判断是否使用了new操作符, 判断是否为构造函数调用,如果是 则使用new调用当前函数
- 如果不是,使用apply,将context和处理好的参数传入
Function.prototype.myBind = function(content = window, ...args) {
if(this === Function.prototype) return undefined; // 判断当前this是否为函数,防止Function.prototype.myCall() 直接调用
if(typeof this !== 'function') throw new TypeError('The `this` must be a `Function`'); // 必须为函数
const fn = this;
return function F(...otherArgs) {
// 判断是否用于构造函数
if(this instanceof F) return new fn(...args, ...otherArgs);
return fn.apply(content, [...args, ...otherArgs])
}
}
//简洁版的new操作符实现过程
function new(constructor) {
var obj = {}; //第一步:创建一个空对象obj
obj.__proto__ = constructor.prototype; //第二步:将构造函数 constructor的原型对象赋给obj的原型
contructor.apply(obj);//第三步:将构造函数 constructor中的this指向obj,并立即执行构造函数内部的操作
return obj; // 第四步:返回这个对象
}
有人用下面的笨方法,不过可以加深了解这么做的原因。
Function.prototype.myBind2 = function(content = window, ...args) {
const fn = this;
// 如果不进行判断是否使用了new,就会导致 " 将构造函数 constructor中的this指向obj " 这一过程失效
const F = function(...otherArgs) {
return fn.apply(this instanceof T ? this : content, [...args, ...otherArgs])
}
//创建一个中转函数T,让F间接继承目标函数的原型
const T = function(){}
T.prototype = fn.prototype;
F.prototype = new T(); // 最后这里new一下。-_- 返回
// F.prototype= new T()将F.prototype的__poro__指向T.prototype,然后T.prototype = that.prototype,所以此时改变F.prototype并不会影响fn.prototype
return F;
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END