1. 手写call
-
思路
- 接受两个参数,第一个参数作为函数调用是绑定的
this对象,第二个参数是函数执行时需要传入的参数 - 第一个参数为
null或者undefined时,this指向全局对象window,值为原始值的指向该原始值的自动包装对象,如String、Number、Boolean - 使用Symbol类型作为唯一值,避免函数名与上下文(context)的属性发生冲突(比如传入的对象有一个
fn属性,此处会有冲突) - 将函数作为传入的上下文(context)属性执行,以对象的形式调用,即改变当前函数的this指向
- 函数执行完成后删除该属性
- 返回执行结果
Function.prototype.myCall = function (context, ...args) { // 新建一个唯一的Symbol变量避免重复 let fn = Symbol() // 判断传入第一个参数是否为null或者undefined context = context == null ? window : Object(context) // 此处的this指的是当前的函数,将其作为第一个参数的属性,然后以对象的形式调用,就达到了绑定this的效果 context[fn] = this // 执行函数 const res = context[fn](...args) // 删除该方法,防止对传入的对象造成污染 delete context[fn] // 返回结果 return res } - 接受两个参数,第一个参数作为函数调用是绑定的
2. 手写apply
-
思路与call一致,唯一不同的是第二个参数的类型必须为数组或者类数组
Function.prototype.myApply = function (context, args = []) { // 新建一个唯一的Symbol变量避免重复 let fn = Symbol() // 判断传入第一个参数是否为null或者undefined context = context == null ? window : Object(context) // 此处的this指的是当前的函数,将其作为第一个参数的属性,然后以对象的形式调用,就达到了绑定this的效果 context[fn] = this // 执行函数 const res = context[fn](...args) // 删除该方法,防止对传入的对象造成污染 delete context[fn] // 返回结果 return res }
3. 手写bind
bind接收两个参数,第一个是作为this,第二个作为当前调用函数的参数,并且返回一个函数,返回的函数也可以传入参数
-
实现调用bind后独立函数调用
Function.prototype.myBind = function (context, ...args) { return (...newArgs) => { // this 是当前调用myBind的函数 return this(...args, ...newArgs) } } -
实现绑定this
Function.prototype.myBind = function (context, ...args) { context = context == null ? window : Object(context) let fn = Symbol() context[fn] = this return (...newArgs) => { return context[fn](...args, ...newArgs) } } -
实现返回的新的函数可以作为构造函数调用
- 注意:此处
context[fn]不可以删除,因为返回的函数可能调用多次
Function.prototype.myBind = function (context, ...args) { context = context == null ? window : Object(context) let fn = Symbol() // 将此处的fn属性设置为不可枚举 Object.defineProperty(context, fn, { configurable: true, enumerable: false, writable: false, value: this }) return function newFn(...newArgs) { // 判断是否作为构造函数调用 if(this instanceof newFn) { return new context[fn](...args, ...newArgs) } return context[fn](...args, ...newArgs) } } - 注意:此处
-
验证
function sum(num1, num2) { console.log(this, num1, num2) } const foo = sum.bind({ name: 'kobe' }, 3) foo(5) // Object 3 5 new foo(5) // sum 3 5 const bar = sum.myBind({ name: 'kobe' }, 3) bar(5) // Object 3 5 new bar(5) // sum 3 5