从绑定机制到手写call、apply、bind

197 阅读3分钟

前言

本篇文章小编会分享一下call,apply,bind的具体实现方法,都是围绕this的方法,所以先提及一下this的绑定机制。


this绑定机制

this包含四种绑定机制:默认绑定、隐式绑定、显式绑定、new绑定。对于绑定优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定。

默认绑定

通俗来讲就是定义变量即为默认绑定,this默认指向window,要注意的是如果用let定义则有自己的单独作用域而不是定义到了window上,以及严格模式下this默认指向的是undefined。

var num = 1;
console.log(this.num); // 1

'use strict'
var num1 = 2;
console.log(this.num); // 报错

//setTimeout的比较特殊
//严格模式和非严格模式下都是window
setTimeout(function () {
    console.log(this);
});

隐式绑定

对象调用方法造成this值的绑定

var a = 2
function b() {
    console.log(this.a);
}
var obj = {
    a: 1,
    b: b
}
obj.b();

显式绑定

call、apply、bind方法对this进行的绑定

new绑定

new绑定我们最常见的就是构造对象,new会为构造的对象内默认定义一个空的this对象

function Person(name) {
    this.name = name
}
var person1 = new Person('司尘');
/* person1 = {
    var this = {}
    再根据构造函数往this对象中去赋值
}
*/

箭头函数

箭头函数this指向在哪里声明this函数中this就指向谁


手写call、apply、bind

其实我们从他们三者的实现很容易去理解,我们最常去实现的就是通过他们去调用对象内方法,根据这个原理我们就可以很方便的去手写实现

我们先定义一个对象供后面使用

let Person = {
    name: 'jack',
    say(age, sex) {
        // 该this指向的是
        console.log(this)
        console.log(`我叫${this.name},我今年${age}岁,性别${sex}`)
    }
}

let Person1 = {
        name: 'rose',
    }

call实现

直接上代码,解释注释都在代码之中

// 这边apply代表剩余参数
Function.prototype.myCall = function (context, ...args) {
    //这里默认不传就是给window
    context = context || window
    
    //给context新增一个独一无二的属性以免覆盖原有属性
    const key = Symbol()
    
    // 这边this即为调用myCall的方法
    console.log(this)
    context[key] = this
    
    //通过隐式绑定的方式调用函数
    const result = context[key](...args)
    
    //删除添加的属性
    delete context[key]
    //返回函数调用的返回值
    return result
}

Person.say.myCall(Person1, 18, '女');

apply实现

apply实现只是传递参数与call不同实现还是一样,就不做注释了

Function.prototype.myApply = function (context, args) {
    context = context || window
    const key = Symbol()
    context[key] = this
    const result = context[key](...args)
    delete context[key]
    return result
}
console.log('这里是手写myappply实现');
Person.say.myApply(Person1, [19, '女'] // 我叫Tom1,我今年19岁,性别女

bind实现

bind的实现简单来讲就是返回的是一个函数而不是直接执行,同时我们也要考虑到bind返回的函数同样可以传递参数进去.

Function.prototype.myBind = function (context, ...args) {
    const fn = this
    
    return function newFn(...newFnArgs) {
        return fn.apply(context, [...args,...newFnArgs])
    }
}

let fn = Person.say.myBind(Person1 ,10);

fn('男'); // 我叫Tom1,我今年10岁,性别男

总结

这部分涉及到this的绑定机制较为重要,理解了这一部分后面的手写这三种方法其实都是照着模子刻

ending...