来看下 call 的原生表现形式:
var foo = { value: 1, }; function bar() { console.log(this.value); } bar.call(foo); // 1
从上面代码的执行结果,我们可以看到,call 首先改变了 this 的指向,使函数的 this 指向了 foo,然后使 bar 函数执行了。
总结如下:
思考一下:我们如何实现上面的效果呢?代码改造如下:
//将bar函数挂载到foo对象上,使其成为foo的方法,用foo.bar来调用 var foo = { value: 1, bar: function () { console.log(this.value); }, }; foo.bar(); //1
为了模拟 call 方法,我们可以这样做:
代码如下:
Function.prototype.myCall = function (context) { const fn = Symbol('fn'); // 声明一个独有的Symbol属性, 防止fn覆盖已有属性 context = context || window; // 若没有传入this, 默认绑定window对象 context.fn = this; // 将函数挂载到对象的fn属性上 const args = [...arguments].slice(1); // 处理传入的参数 const result = context.fn(...args); // 通过对象的属性调用该方法 delete context.fn; // 删除该属性 return result; }; // 测试 function test(arg1, arg2) { console.log(arg1, arg2); console.log(this.a, this.b); } test.myCall( { a: 'a', b: 'b', }, 1, 2 );
我们看一下上面的代码:
apply 和 call 实现类似,只是传入的参数形式是数组形式,而不是逗号分隔的参数序列。
因此,借助 es6 提供的...运算符,就可以很方便的实现数组和参数序列的转化。
Function.prototype.myApply = function (context) { const fn = Symbol('fn'); // 声明一个独有的Symbol属性, 防止fn覆盖已有属性 context = context || window; // 若没有传入this, 默认绑定window对象 context.fn = this; // 将函数挂载到对象的fn属性上 const args = [...arguments].slice(1); // 处理传入的参数 const result = context.fn(args); // 通过对象的属性调用该方法 delete context.fn; // 删除该属性 return result; }; // 测试 function test(arr) { console.log(arr); console.log(this.a, this.b); } test.myApply( { a: 'a', b: 'b', }, [1, 2] );
在模拟 bind 的实现之前,先看一下 bind 的使用案例:
var obj = { a: 1 }; function bar() { console.log(this.a); } bar.bind(obj)(); //here
bind 函数虽然也能改变 bar 函数的 this,但是改变后,函数并不会执行,只是返回一个新的函数,想执行就得继续调用,仔细观察第五行代码的写法。
根据上面的使用案例,我们先实现一个简单版本的 bind:
Function.prototype.myBind = function (context) { return () => { // 要用箭头函数,否则 this 指向错误 return this.call(context); }; }; var obj = { a: 1 }; function bar() { console.log(this.a); } bar.myBind(obj)();
但是这样比较简陋,函数的参数一多就不能处理了,如下面这种情况:
bar.bind(obj, 2)(2); // or bar.bind(obj)(2, 2);
为了兼容 bind 调用时满足参数传递的不同方式,代码修改如下:
Function.prototype.myBind = function (ctx, ...arg1) { return (...arg2) => { return this.call(ctx, ...arg1, ...arg2); }; }; //测试代码 var obj = { a: 1 }; function bar(b, c) { console.log(this.a + b + c); } bar.myBind(obj)(20, 30); bar.myBind(obj, 20, 30)();
本文作者:前端小毛
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!