面试官:手写 call、apply 及 bind 函数

call 函数

简介

call 函数的用法

js 复制代码
    fn.call(obj,1,2);
  • call() 函数中的第一个参数表示:想让 this 指向的对象 (obj);
  • call() 函数中的第二及以后参数表示:传进去的实参;

call 函数的功能:

  • 让函数立执行;
  • 可改变 this 的指向;
  • 可实现继承问题;

call 函数的调用

js 复制代码
const user1 = {
    name : '张三',
    age : 10
}
const user2 = {
    name: '李四',
    age: 20
}

function say(){
    console.log('name:', this.name)
    const sum = [...arguments].reduce((res, current)=> {
        res = res + current
        return res
    })
    console.log(sum);
}

say.call(user1, 1,2,3)
say.call(user2, 4,2,3)

以上是一个 call 函数调用的示例

  • 定义了两个对象分别是 user1 与 user2
  • 定义say()函数,输出当前调用的 name 与剩余参数的求和结果
  • 通过 call 函数执行 say 函数,分别传参 user1 与 user2

分别将当前 this 绑定至 user1 与 user2 ,输出结果为:

手写 call 函数

下面我们自己来实现 call 函数:

js 复制代码
const user1 = {
    name : '张三',
    age : 10
}
const user2 = {
    name: '李四',
    age: 20
}

function say(){
    console.log('name:', this.name)
    const sum = [...arguments].reduce((res, current)=> {
        res = res + current
        return res
    })
    console.log(sum);
}

Function.prototype.myCall = function(ctx, ...args){
    ctx = (ctx===null || undefined)? globalThis : Object(ctx)
    const key = Symbol('temp');
    Object.defineProperty(ctx, key, {
        enumerable: false,
        value: this
    })
    const result = ctx[key](...args)
    delete ctx[key]
    return result

}
say.myCall(user1,1,2,3)
  • 首先还是先定义对象 obj 与 obj2,函数 say()
  • 在函数原型上新增 myCall方法,接收两个参数,ctx 为必填参数表示 this 的指向,...args接收剩余参数
  • 判断 ctx 的是否为 null 或者 undefined,如果是的话,当前 this 指向 globalThis, (globalThis旨在通过定义一个标准的全局属性来整合日益分散的访问全局对象的方法。该提案目前处于第四阶段,这意味着它已经准备好被纳入ES2020标准。所有流行的浏览器,包括Chrome 71+、Firefox 65+和Safari 12.1+,都已经支持这项功能。你也可以在Node.js 12+中使用它。)
  • 需要给ctx 定义个属性将剩余参数传入进去,为了避免与绑定的 ctx 参数重复,使用 Symbol()定义 key 值

  • 定义的 key 值通过 Object.defineProperty(obj,key,decriptor)方法传入,obj为目标对象,key 为 属性 值,descriptor是我们操作属性的具体描述,这里我们使用enumerable不可被枚举,具体包含的参数有:

    • value:被定义的属性的值,默认值为undefind
    • writable:是否可以被重写,也就是value属性是否可以被赋值,当writable的值为true的时候,可以被重写,反之不可以。
    • enumerable:该属性是否可以被枚举,值为true可以被枚举,反之则不可以。
    • configurable:该属性是否可以被删除,或者是否可以被修改,值为true可以被删除或者被修改,反之则不可以。
  • 将剩余参数传递给定义好的函数

  • 将定义的属性删除,return 结果出去

  • 最后,使用 say().myCall(user1,1,2,3)调用,输出的结果为:

总结

使用 globalThis 可以在浏览器以及 node 环境上运行,Symbol定义唯一 key 值等操作

apply函数

简介

apply 函数跟 call 函数的区别在于穿参形式不一样,apply 函数的第二个参数是一个数组,示例:

js 复制代码
const user1 = {
    name : '张三',
    age : 10
}
const user2 = {
    name: '李四',
    age: 20
}

function say(){
    console.log('name:', this.name)
    console.log('args:',[...arguments])
    const sum = [...arguments].reduce((res, current)=> {
        res = res + current
        return res
    })
    console.log('sum:',sum);
}


say.apply(user1, [1,2,3])
say.apply(user2, [2,3,4])

运行结果:

手写 apply 函数

将 myCall 函数改造 下

js 复制代码
const user1 = {
    name : '张三',
    age : 10
}
const user2 = {
    name: '李四',
    age: 20
}

function say(){
    console.log('name:', this.name)
    console.log('args:',[...arguments])
    const sum = [...arguments].reduce((res, current)=> {
        res = res + current
        return res
    })
    console.log('sum:',sum);
}

Function.prototype.myApply = function(ctx, ...args){
    ctx = (ctx===null || undefined)? globalThis : ctx
    const key = Symbol('temp');
    Object.defineProperty(ctx, key, {
        enumerable: false,
        value: this
    })
    console.log(...args)
    // 注意该处的传参形式
    const result = ctx[key](...args[0])
    delete ctx[key]
    return result

}
say.myApply(user2,[2,3,4])

bind 函数

相关推荐
一颗花生米。3 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐013 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19953 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&4 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈4 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水5 小时前
简洁之道 - React Hook Form
前端
正小安7 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
小飞猪Jay9 小时前
C++面试速通宝典——13
jvm·c++·面试
_.Switch9 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j