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

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

热点面试题: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.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')

文章特殊字符描述


问题标注 Q:(question)答案标注 R:(result)注意事项标准:A:(attention matters)详情描述标注:D:(detail info)总结标注:S:(summary)分析标注:Ana:(analysis)提示标注:T:(tips)

相关文章
|
3月前
|
Web App开发 JavaScript 前端开发
Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念
Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念,包括事件驱动、单线程模型和模块系统;探讨其安装配置、核心模块使用、实战应用如搭建 Web 服务器、文件操作及实时通信;分析项目结构与开发流程,讨论其优势与挑战,并通过案例展示 Node.js 在实际项目中的应用,旨在帮助开发者更好地掌握这一强大工具。
64 1
|
6天前
|
JavaScript 前端开发 容器
this、self、window、top 在 JavaScript 中的区别深入研究
在 JavaScript 开发中,`this`、`self`、`window` 和 `top` 是四个常用的概念。`this` 指向当前执行上下文的对象,其值取决于函数调用方式;`self` 在全局作用域中等同于 `window`,常用于 Web Workers;`window` 代表浏览器窗口,是全局变量的容器;`top` 指向最顶层窗口,用于判断是否在框架中。理解这些概念有助于编写健壮的代码。
18 1
this、self、window、top 在 JavaScript 中的区别深入研究
|
2月前
|
前端开发 Java 程序员
面试官刁钻提问?轻松应对 break、continue 和 return 的巧妙用法
小米,一位技术博主,针对 Java 面试中常见的 `break`、`continue` 和 `return` 关键字进行了详细讲解。通过打怪故事和代码实例,解释了它们的使用场景及注意事项,帮助读者更好地理解和应用这些控制语句。
49 12
面试官刁钻提问?轻松应对 break、continue 和 return 的巧妙用法
|
3月前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
119 1
|
3月前
|
JavaScript 前端开发
js中的bind,call,apply方法的区别以及用法
JavaScript中,`bind`、`call`和`apply`均可改变函数的`this`指向并传递参数。其中,`bind`返回一个新函数,不立即执行;`call`和`apply`则立即执行,且`apply`的参数以数组形式传递。三者在改变`this`指向及传参上功能相似,但在执行时机和参数传递方式上有所区别。
39 1
|
3月前
|
设计模式 JavaScript 前端开发
js中new和object.creat区别
【10月更文挑战第29天】`new` 关键字和 `Object.create()` 方法在创建对象的方式、原型链继承、属性初始化以及适用场景等方面都存在差异。在实际开发中,需要根据具体的需求和设计模式来选择合适的方法来创建对象。
|
6月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
3月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
3月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
3月前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
90 4

热门文章

最新文章