关于前端:热点面试题JS-中-call-apply-bind-概念用法区别及实现

34次阅读

共计 4068 个字符,预计需要花费 11 分钟才能阅读完成。

前言

极度投入,深度沉迷,边界清晰

前端小菜鸡一枚,分享的文章纯属个人见解,若有不正确或可待探讨点可随便评论,与各位同学一起学习~

欢送关注 『前端进阶圈』 公众号,一起摸索学习前端技术 ……

公众号回复 加群 扫码, 即可退出前端交流学习群,长期交流学习 ……

公众号回复 加好友,即可添加为好友

热点面试题:JS 中 call, apply, bind 概念、用法、区别及实现?

概念:

  • function.call(thisArg, arg1, arg2, ...)
  • function.apply(thisArg, [arg1, arg2, ...])
  • function.bind(thisArg, arg1, arg2, ...)
  • 三者都是扭转 this 指向,通过一个参数或多个参数来调用一个函数的。

用法:

let obj = {
    name: "哈哈",
    sayName: function () {console.log("sayName", this.name);
        return this.name;
    },
    eat: function (food1, food2) {console.log("eat", food1, food2);
    },
};

let obj2 = {name: "是的",};

obj.sayName.call(obj2); // sayName 是的
obj.eat.call(obj2, "鱼", "肉"); // eat 鱼 肉

obj.eat.apply(obj2, ["鱼", "肉"]); // e at 鱼 肉

obj.eat.bind(obj2, "鱼", "肉"); // 不会调用,须要一个后果来接管
let res = obj.eat.bind(obj2, "鱼", "肉");
res(); // eat 鱼 肉

区别:

  • call 与 bind 的区别?

    • call 会间接调用,而 bind 会创立一个新的函数作为一个返回值进行调用, 而其余参数将作为新函数的参数,供调用时应用
  • call 与 apply 的区别?

    • 次要区别在第二个参数中,call 承受的是一个参数列表,也就是一个个参数,而 apply 承受的是一个蕴含多个参数的数组

实现:

  • function.call(thisArg, arg1, arg2, ...)

    Function.prototype.myCall = function (context, ...args) {
    // 条件判断,判断以后调用的对象是否为函数,if (Object.prototype.toString.call(this).slice(8, -1) != "Function")
        throw new Error("type error");
    // 判断传入上下文对象是否存在,如果不存在,则设置为 window
    if (!context || context === null) context = window;
    // 创立惟一的 key 值,作为构建的 context 外部办法名
    let fn = Symbol();
    // 将 this 指向调用的 call 函数
    context[fn] = this;
    // 执行函数并返回后果 === 把本身作为传入的 context 的办法进行调用
    return context[fn](...args);
    };
    let obj = {
    name: "哈哈",
    sayName: function () {console.log("sayName", this.name);
        return this.name;
    },
    eat: function (food1, food2) {console.log("eat", food1, food2);
    },
    };
    let obj2 = {name: "是的",};
    obj.sayName.myCall(obj2);
  • function.apply(thisArg, [arg1, arg2, ...])

    Function.prototype.MyApply = function (context, args) {
    // 条件判断,判断以后调用的对象是否为函数,if (Object.prototype.toString.call(this).slice(8, -1) != "Function")
        throw new Error("type error");
    // 判断传入上下文对象是否存在,如果不存在,则设置为 window
    if (!context || context === null) context = window;
    // 创立惟一的 key 值,作为构建的 context 外部办法名
    let fn = Symbol();
    // 将 this 指向调用的 call 函数
    context[fn] = this;
    // 执行函数并返回后果 === 把本身作为传入的 context 的办法进行调用
    return context[fn](...args);
    };
    let obj = {
    name: "哈哈",
    sayName: function () {console.log("sayName", this.name);
        return this.name;
    },
    eat: function (food1, food2) {console.log("eat", food1, food2);
    },
    };
    let obj2 = {name: "是的",};
    obj.sayName.MyApply(obj2, []);
  • function.bind(thisArg, arg1, arg2, ...)

    Function.prototype.myBind = function (context, ...args) {if (!context || context === null) {context = window;}
    // 发明惟一的 key 值  作为咱们结构的 context 外部办法名
    let fn = Symbol();
    context[fn] = this;
    let _this = this;
    //  bind 状况要简单一点
    const result = function (...innerArgs) {
        // 第一种状况: 若是将 bind 绑定之后的函数当作构造函数,通过 new 操作符应用,则不绑定传入的 this,而是将 this 指向实例化进去的对象
        // 此时因为 new 操作符作用  this 指向 result 实例对象  而 result 又继承自传入的_this 依据原型链常识可得出以下论断
        // this.__proto__ === result.prototype   //this instanceof result =>true
        // this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype; //this instanceof _this =>true
        if (this instanceof _this === true) {
            // 此时 this 指向指向 result 的实例  这时候不须要扭转 this 指向
            this[fn] = _this;
            this[fn](...[...args, ...innerArgs]); // 这里应用 es6 的办法让 bind 反对参数合并
        } else {
            // 如果只是作为一般函数调用  那就很简略了 间接扭转 this 指向为传入的 context
            context[fn](...[...args, ...innerArgs]);
        }
    };
    // 如果绑定的是构造函数 那么须要继承构造函数原型属性和办法
    // 实现继承的形式: 应用 Object.create
    result.prototype = Object.create(this.prototype);
    return result;
    };
    // 用法如下
    function Person(name, age) {console.log(name); //'我是参数传进来的 name'
    console.log(age); //'我是参数传进来的 age'
    console.log(this); // 构造函数 this 指向实例对象
    }
    // 构造函数原型的办法
    Person.prototype.say = function () {console.log(123);
    };
    let obj = {
    objName: "我是 obj 传进来的 name",
    objAge: "我是 obj 传进来的 age",
    };
    // 一般函数
    function normalFun(name, age) {console.log(name); //'我是参数传进来的 name'
    console.log(age); //'我是参数传进来的 age'
    console.log(this); // 一般函数 this 指向绑定 bind 的第一个参数 也就是例子中的 obj
    console.log(this.objName); //'我是 obj 传进来的 name'
    console.log(this.objAge); //'我是 obj 传进来的 age'
    }
    // 先测试作为结构函数调用
    let bindFun = Person.myBind(obj, "我是参数传进来的 name");
    let a = new bindFun("我是参数传进来的 age");
    a.say(); //123
    // 再测试作为一般函数调用
    // let bindFun = normalFun.myBind(obj, '我是参数传进来的 name')
    //  bindFun('我是参数传进来的 age')

文章特殊字符形容:

  1. 问题标注 Q:(question)
  2. 答案标注 R:(result)
  3. 注意事项规范:A:(attention matters)
  4. 详情形容标注:D:(detail info)
  5. 总结标注:S:(summary)
  6. 剖析标注:Ana:(analysis)
  7. 提醒标注:T:(tips)

往期回顾:

  • 热点面试题:Virtual DOM 相干问题?
  • 热点面试题:什么是粘包 / 半包问题,该如何解决?
  • 热点面试题:console.log()同异步问题?
  • 热点面试题:过程系列问题?
  • 热点面试题:Node.js 中的垃圾回收机制?
  • 热点面试题:简述 http3.0~http1.0 别离有什么改良?
  • JavaScript 中的 AMD 和 CMD 标准
  • Vue 数据监听 Object.definedProperty()办法的实现

最初:

  • 欢送关注 『前端进阶圈』 公众号,一起摸索学习前端技术 ……
  • 公众号回复 加群 扫码, 即可退出前端交流学习群,长期交流学习 ……
  • 公众号回复 加好友,即可添加为好友

正文完
 0