call,apply和bind方法的实现

call,apply和bind方法的实现

在JavaScript中,call,apply和bind方法用于完成this的显式绑定。call和apply将某个函数的this绑定到通过它们的第一个参数指定的对象上并执行这个函数,而bind将返回一个函数,这个函数的this被绑定到了通过bind的第一个参数指定的对象上,且无法通过显式绑定或者隐式绑定修改这个函数的this,只能通过new绑定改变this的指向。

call和apply非常类似,它们的区别在于apply需要以数组的形式提供除第一个参数以外的剩余参数,而call只需要按照正常方式传参。call和apply会把这些“剩余参数”传递给调用它们的函数。

 1 // call方法的实现
 2 Function.prototype.myCall = function (context) {
 3     if (typeof this !== 'function') {
 4         throw new TypeError("The caller of call must be a function");
 5     }
 6 
 7     // 如果没有提供上下文对象,则将上下文对象设置为全局对象
 8     context = context || global;
 9 
10     // 使用Symbol设置属性名,避免属性污染
11     const fn = Symbol();
12 
13     // 将调用call方法的函数设置为上下文对象的属性
14     context[fn] = this;
15 
16     // 获得参数
17     const args = [...arguments].slice(1);
18 
19     // 执行函数
20     const result = context[fn](...args);
21 
22     // 删除属性
23     delete context[fn];
24 
25     return result;
26 }

 

 1 // apply方法的实现
 2 Function.prototype.myApply = function (context) {
 3     if (typeof this !== "function") {
 4         throw new TypeError("The caller of apply must be a function");
 5     }
 6 
 7     // 如果没有提供上下文对象,则将上下文对象设置为全局对象
 8     context = context || global;
 9 
10     // 使用Symbol设置属性名,避免属性污染
11     const fn = Symbol();
12 
13     // 将调用apply方法的函数设置为上下文对象的属性
14     context[fn] = this;
15 
16     // 判断apply方法的第二个参数是否存在,并根据相应执行函数
17     let result;
18     if (arguments[1]) {
19         result = context[fn](...arguments[1]);
20     } else {
21         result = context[fn]();
22     }
23 
24     // 删除属性
25     delete context[fn];
26 
27     return result;
28 }

 

bind的实现更加复杂。首先,我们需要获取bind的this值并保存到一个变量中,这是因为需要返回的函数bound无法访问bind的this值(在内部函数的作用域中无法访问外部函数的this)。其次,我们需要获得curried参数并将其与传递给bound的参数拼接起来。另外还需要注意的是new绑定是唯一可以改变bound函数的this值的方法,因此必须判断何时通过new操作符调用bound,此时我们需要将bound的this值设置为new创建的对象。最后需要注意bound的prototype属性应与调用bind的函数的prototype属性保持一致。

 1 // bind方法的实现
 2 Function.prototype.myBind = function (context) {
 3     if (typeof this !== "function") {
 4         throw new TypeError("The caller of bind must be a function");
 5     }
 6 
 7     // 如果没有提供上下文对象,则将上下文对象设置为全局对象
 8     context = context || global;
 9 
10     // 由于内部函数的作用域无法访问外部函数作用域中的this,因此需要把this用另一个变量保存
11     let func = this;
12 
13     // 获得curried参数
14     let curried = [].slice.call(arguments, 1);
15 
16     // 判断是否通过new调用bind返回的函数
17     let bound = function () {
18         let obj;
19         if (this instanceof bound) {
20             obj = this; // 通过new调用bind返回的函数,需要将obj绑定到new创建的对象上
21         } else {
22             obj = context; // 否则将obj绑定到bind指定的上下文对象上
23         }
24 
25         // 拼接参数,并执行函数
26         return func.apply(obj, curried.concat([].slice.call(arguments)));
27     }
28 
29     // 将bind返回的函数的prototype属性设置为func的prototype
30     bound.prototype = Object.create(func.prototype);
31 
32     return bound;
33 }

 

上一篇:树状数组&pair&离散化


下一篇:Springboot整合mybatis org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)