动手实现一下 JavaScript 中的 call, apply 和 bind

用户头像
Verlime
关注
发布于: 2020 年 07 月 21 日
动手实现一下 JavaScript 中的 call, apply 和 bind

在 JavaScript 中 call、apply、bind 都有些类似,但又有些不同,它们都用于改变 this 的指向,其中 call 和 apply 会直接执行函数,而 bind 会返回一个新函数,另外 call 和 apply 的传参方式也有些不同,下面就来分别模拟实现一下。

call

首先看下 call 的使用



var foo = {
name: 'mofang',
};
function bar() {
console.log(this.name);
console.log(...arguments);
return 123;
}
bar.call(foo);
// mofang
bar.call(foo, 1, 'abc');
// mofang
// 1 abc
// 改变 bar 中的 this 指向,修改为 foo,因此在 bar 内部的 this.name 等价于 foo.name



然后我们来实现一下



Function.prototype._call = function(context) {
// 参数如果为空则指向 window
context = context || window;
const args = [...arguments].slice(1);
// 将调用 call 的对象添加到 context 上
context.fn = this;
// 执行 fn,此时 fn 内的 this 指向 context,同时传入参数...args
const ret = context.fn(...args);
delete context.fn;
// 返回函数执行结果
return ret;
};
bar._call(foo);
bar._call(foo, 1, '2');



apply



再来看一下 apply 的使用,apply 的使用与 call 类似,只不过第二个参数是传入一个数组



var foo = {
name: 'mofang',
};
function bar() {
console.log(this.name);
console.log(...arguments);
}
bar.apply(foo);
// mofang
bar.apply(foo, [1, 'abc']);
// mofang
// 1 abc



接下来模拟实现一下:



Function.prototype._apply = function(context) {
context = context || window;
// 获取参数列表,如果没有参数就传入一个空数组
const args = arguments[1] || [];
// 把调用 apply 的对象挂载在 context 上
context.fn = this;
// 执行 context.fn,fn 内部的 this 指向 context, 这样 fn 就拥有了获取 context 内部属性的能力
const ret = context.fn(...args);
// 删掉多余的 fn
delete context.fn;
// 返回 fn 执行的结果
return ret;
};



bind

最后来看一下 bind 是如何实现的,首先需要知道的是 bind 和 new 同时使用时,new 的优先级要高。



// var foo = bar._bind(obj);
Function.prototype._bind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('_bind must called on a function');
}
// 获取参数列表
var args = [].slice.call(arguments, 1);
// 存储调用函数(bar),方便后边使用 apply
var fToBind = this;
// 构造一个纯净的函数,用于存储原函数的原型
var fNop = function() {};
// 绑定的函数,还需要拿出去调用,这里再封装一个函数
var fBound = function() {
// 这里需要判断是否使用 new 调用了 bound,如果是则 this 指向实例,否则指向 context
return fToBind.apply(
this instanceof fToBind ? this : context,
args.concat([].slice.call(arguments))
);
};
fNop.prototype = this.prototype;
fBound.prototype = new fNop();
return fBound;
};



然后测试一下:



var obj = {};
function bar(name) {
this.name = name;
}
var foo = bar._bind(obj);
foo('mofang');
console.log(obj.name); // mofang
var w = new foo('walker');
// 通过 new 调用,内部 this 指向实例
console.log(obj.name); // mofang
console.log(w.name); // walker



Photo by Rinck Content Studio on Unsplash

发布于: 2020 年 07 月 21 日 阅读数: 31
用户头像

Verlime

关注

一个前端工程师 2018.01.01 加入

每天进步一点点ヽ(•̀ω•́ )ゝ

评论

发布
暂无评论
动手实现一下 JavaScript 中的 call, apply 和 bind