# 模拟apply/call/bind
call/apply/bind的用法在全面认识Function可以看到,这里模拟以下这三个方法的实现
本身这个三个方法都是用来修改函数的this指向的,call和apply就是传参不一样,但是两个都是会在调用的时候同时执行调用的的函数,但是bind则会返回一个绑定了this的函数。
# this指向
普通函数的this指向是在函数调用的时候确定下来的,this指向大致分为5中
1. 默认绑定
function test() {
// 严格模式下是undefined
// 非严格模式下是window
console.log(this);
}
setTimeout(function () {
// setTimeout的比较特殊
// 严格模式和非严格模式下都是window
console.log(this);
});
arr.forEach(function () {
// 严格模式下是undefined
// 非严格模式下是window
console.log(this);
});
2. 隐式绑定
通俗的讲就是谁调用指向谁
const obj = {
name:'joy',
getName(){
console.log(this); //obj
console.log(this.name); //joy
}
};
obj.getName();
3. 显式绑定
call/apply/bind
const obj1 = {
name: 'joy',
getName() {
console.log(this);
console.log(this.name);
}
};
const obj2 = {
name: 'sam'
};
obj1.getName.call(obj2); // obj2 sam
obj1.getName.apply(obj2); // obj2 sam
const fn = obj1.getName.bind(obj2);
fn(); // obj2 sam
4. new 绑定
function Vehicle() {
this.a = 2
console.log(this);
}
new Vehicle(); // this指向Vehicle这个new出来的对象
5. es6箭头函数
es6的箭头函数比较特殊,箭头函数this为父作用域的this,不是调用时的this。
前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来.比较符合js的词法作用域吧。
window.name = 'win';
const obj = {
name: 'joy',
age: 12,
getName: () => {
console.log(this); // 其父作用域this是window,所以就是window
console.log(this.name); // win
},
getAge: function () {
// 通过obj.getAge调用,这里面this是指向obj
setTimeout(() => {
//所以这里this也是指向obj 所以结果是12
console.log(this.age);
});
}
};
obj.getName();
obj.getAge();
上面5中this绑定的优先级:
箭头函数 > new 绑定 > 显式绑定call/bind/apply > 隐式绑定 > 默认绑定
可以用隐式绑定的原理去修改默认绑定
# 模拟实现apply
Function.prototype._apply = function(context, arg) {
const thisArg = context || window;
const params = arg ? arg : [];
const key = Symbol();
thisArg[key] = this;
const result = thisArg[key](...params);
delete thisArg[key];
return result;
}
# 模拟实现call
Function.prototype._call = function(context, ...arg) {
const thisArg = context || window;
const params = arg ? arg : [];
const key = Symbol();
thisArg[key] = this;
const result = thisArg[key](...params);
delete thisArg[key];
return result;
}
# 模拟实现bind
bind和apply的区别在于:bind是返回一个绑定好的函数,apply是直接调用。
其实想一想实现也很简单,就是返回一个函数,里面执行了apply上述的操作而已。
不过有一个需要判断的点,因为返回新的函数,要考虑到使用new去调用,并且new的优先级比较高,所以需要判断new的调用。
还有一个特点就是bind调用的时候可以传参,调用之后生成的新的函数也可以传参,这两次传的参数需要拼接起来,一起传递给原始函数,所以这一块也要做处理。
因为上面已经实现了apply,这里就借用一下,实际上不借用就是把代码copy过来
Function.prototype._bind = function(context, ...arg) {
const fn = this;
const args = arg ? arg: [];
return function newFn(...params) {
// 使用new的时候,this指向实例,实例继承自该函数,用这个判断是new调用
if(this instanceof newFn) {
return new fn(...args, ...params);
};
const result = fn._apply(context, [...args, ...params]);
return result;
}
}