js 之 手打实现 call apply bind

608 阅读4分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。

this的常见情况

  1. 事件中的this是绑定当前事件的元素;
  2. 自执行函数中的this指向window;
  3. 定时器回调函数中this指向window;
  4. 全局作用域的this指向window;
  5. 方法调用时看方法执行前有没有点,如果有点前面是谁就是谁,没有就是window;
  6. 箭头函数中的this是箭头函数声明时所在作用域中的this;
  7. 构造函数中的this指向当前实例;

Function.prototype 上有三个方法 call, apply, bind 供 Function 的实例用来修改函数中的this指向

call的使用

  • 语法: 函数名.call(xxx,实参1,实参2,.....)
  • 参数: 第一个参数是需要修改的指向this,剩余项是被修改函数实参
  • 作用: 把被修改函数的this, 改为call的第一个参数,并传递被修改函数的若干实参,最后让call执行

apply的使用

  • 语法: 函数名.apply(xxx,[实参集合])
  • 参数: 第一个参数是需要修改的指向this,第二个参数是数组的实参集合
  • 作用: 把被修改函数的this, 改为apply的第一个参数,并传入数组的实参集合,最后让apply执行

call与apply的共性都是修改函数的this指向并传递参数让函数执行,区别就是二者的给函数传递实参方式不同,call是一个一个传,apply是传递一个实参集合

function add(a,b,c,d){
    console.log(this);
    return a+b+c+d
}
let num = add(1,2,3,4); // 10 window
let text = "狗剩";
let num2 = add.call(text,1,2,3,4); // 10 '狗剩'
let ary = [1,2,3,4];
let num3 = add.apply(text,ary); // 10 '狗剩'

bind的使用

  • 语法: 函数名.bind(xxx,实参1,实参2,...)
  • 参数: 第一个需要修改成的指向this,剩余的是函数需要的若干实参,不可以像apply一样传递实参集合,但是可以不传
  • 作用: 把被修改函数的this, 改为bind的第一个参数,并传入若干需要的函数实参,最后返回一个被修改this后的新函数.

bind 与call和apply的区别就是同样修改了 函数的this指向但是 后二者是修改并执行 bind是修改不执行返回新函数需要的时候在执行;

function add(a,b,c,d){
    console.log(this);
    return a+b+c+d
}
let num = add(1,2,3,4); // 10 window
let text = "狗剩";
let num2 = add.call(text,1,2,3,4); // 10 '狗剩'
let ary = [1,2,3,4];
let num3 = add.apply(text,ary); // 10 '狗剩'
<!--let add2 = add.bind(text);-->
<!--let num4 = add2(1,2,3,4);-->
let add2 = add.bind(text,1,2,3,4);// 如果在bind阶段绑定实参,那么在新函数执行时次传递参数就不会被新函数接收
let num4 = add2();

bind 实现可理化函数

function add(a,b,c){
    return a+b+c
}
let num = add(1,2,3);

function add2(a){
    return function (b){
        return function (c){
            return a+b+c
        }
        
    }
}
let num2 = add2(1)(2)(3);

let a1 = add.bind(null,1);
let b2 = a1.bind(null,2);
let c3 = b2.bind(null,3);
let d = c3(); // 6


自己实现一下 以上方法

实现 call

        Function.prototype.myCall = function (context, ...args) {
            context = Object(context) || window;
            const key = Symbol();
            context[key] = this;
            console.log(context, ...args)
            console.log(context[key], this)
            const fun = context[key](...args);
            delete context[key]
            return fun

        }
        console.dir(Function.prototype);
        function add(a, b, c) {
            console.log(this);
            return a + b + c
        }
        let text = "狗剩";
        let num2 = add.myCall(text, 1, 2, 3); // 10 '狗剩'
        console.log(num2);



实现 apply

            Function.prototype.myApply = function (context, args) {
            context = Object(context) || window;
            args = args ? args : []
            const key = Symbol();
            context[key] = this;
            const fun = context[key](...args);
            delete context[key]
            return fun

        }
        console.dir(Function.prototype);
        function add(a, b, c) {
            console.log(this);
            return a + b + c
        }
        let text = "狗剩";
        let ary = [1, 2, 3];
        let num2 = add.myApply(text, ary); // 10 '狗剩'
        let num3 = add.myApply(text, 1,2,3); // 10 '狗剩'
        console.log(num3);

实现 bind


        Function.prototype.myBind = function (context, ...args) {
            args = args ? args : []
            context = context || window;
            const _this = this;
            return function newFn(...newArgs) {
                if (this instanceof newFn) {
                    return new _this(...args, ...newArgs)
                    // new 的时候,this指向new出来的实例,实例的__proto__指向newFn的prototype
                    // new操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象
                    // 1、创建一个空的对象
                    // 2、链接到原型
                    // 3、绑定this指向,执行构造函数
                    // 4、确保返回的是对象

                }
                return _this.myApply(context, [...args, ...newArgs])
            }

        }
        console.dir(Function.prototype);
        function add1(a, b, c) {
            return a + b + c;
        }
        let str = "狗剩";
        let ary = [1, 2, 3];
        let num = add1.myBind(str, 1, 2, 3); // 6 '狗剩'
        console.log(num());