导读
- 需要先了解 bind/call/apply 函数使用与作用 (作用就是改变 this 的指向)
- 需要先了解 this (this 永远指向最后调用它的那个对象)
- 需要了解原型与原型链
- 需要了解 instanceof
- 带个问题看,bind 只有在第一次的时候绑定的 this 有效,是吗?
简易 bind 实现 (未考虑new的情况)
Function.prototype.myBind = function (that) {
// 获取指向的对象
console.log('that', that); // that {name: 'kevin'}
// 取出所有除绑定目标对象外的全部参数
let params1 = Array.prototype.slice.call(arguments, 1);
console.log('params1', params1); // [1, 2 ,3]
console.log('this', this); // fn1函数
const self = this;
// 返回一个函数
function bound () {
// 获取bind后调用的传入参数
const params2 = Array.prototype.slice.call(arguments);
console.log('params2', params2); // [100, 200]
return self.apply(that, params1.concat(params2));
};
return bound;
};
function fn1 () {
console.log('this', this); // this {name: 'kevin'}
console.log(arguments); // [1, 2 ,3]
return 'this is fn1';
};
const fn2 = fn1.bind({ name: 'kevin'}, 1, 2, 3);
// const fn2 = fn1.myBind({ name: 'kevin'}, 1, 2, 3);
const res = fn2(100, 200);
console.log(res); // this is fn1
bind 实现
Function.prototype.myBind = function (that) {
let params1 = Array.prototype.slice.call(arguments, 1);
const self = this;
// 构建一个干净的函数,用于保存原函数的原型
const nop = function () { };
function bound () {
let params2 = Array.prototype.slice.call(arguments);
// console.log(self) // a 这个函数
// console.log(this) // window
// console.log(this) // new c() // bound {}
// console.log(that) // b 这个对象
return self.apply(
// this instanceof nop, 判断是否使用 new 来调用 bound
// 如果是 new 来调用的话,this的指向就是其实例,
// 如果不是 new 调用的话,就改变 this 指向到指定的对象 that
this instanceof nop ? this : that,
params1.concat(params2)
);
};
// 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
if (this.prototype) {
nop.prototype = this.prototype;
};
// 修改绑定函数的原型指向
bound.prototype = new nop();
return bound;
};
const a = function () {
// // c(100, 200);
// console.log(arguments); // [1, 2, 3, 100, 200]
// console.log(this.name); // kevin
// // new c(300);
console.log(arguments); // [1, 2, 3, 300]
console.log(this.name); // leslie
};
a.prototype.name = 'leslie';
const b = { name: 'kevin' };
// const c = a.bind(b, 1, 2, 3);
const c = a.myBind(b, 1, 2, 3);
// c(100, 200);
new c(300);
call/apply 本质是一样的,都是改变 this 指向后执行函数,区别只是入参不一样而已
Function.prototype.myCall = function (that) {
// 声明一个 Symbol 属性,防止 fn 被占用
const fn = Symbol('fn')
const params = [...arguments].slice(1);
that = that || window;
that[fn] = this;
const result = that[fn](...params);
delete that[fn];
return result;
}
Function.prototype.myApply = function (that) {
const fn = Symbol('fn')
const params = arguments[1];
that[fn] = this;
const result = that[fn](...params);
delete that[fn];
return result;
};
const a = function () {
console.log(this.name, arguments);
};
a.prototype.name = 'a';
const foo = {
name: 'foo'
};
a.myCall(foo, 1, 2, 3); // foo [1, 2, 3]
a.myApply(foo, [1, 2, 3]); // foo [1, 2, 3]
小结
希望看完本篇文章能对你有如下帮助:
- bind 只有在第一次的时候绑定的 this 有效? 答案:是的。
- 了解 bind/call/apply 原理。
- 更多的是为了在实现过程中,熟悉与回顾其他的知识点。
文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞和关注。