前端必刷手写题系列 [6]

203 阅读5分钟

这是我参与更文挑战的第 3 天,活动详情查看 更文挑战

这个系列也没啥花头,就是来整平时面试的一些手写函数,考这些简单实现的好处是能看出基本编码水平,且占用时间不长,更全面地看出你的代码实力如何。一般不会出有很多边界条件的问题,那样面试时间不够用,考察不全面。

平时被考到的 api 如果不知道或不清楚,直接问面试官就行, api 怎么用这些 Google 下谁都能马上了解的知识也看不出水平。关键是在实现过程,和你的编码状态习惯思路清晰程度等。

注意是简单实现,不是完整实现,重要的是概念清晰实现思路清晰,建议先解释清除概念 => 写用例 => 写伪代码 => 再实现具体功能,再优化,一步步来。

12. call

是什么

MDN: call() 方法使用一个指定的 this 值单独给出的一个或多个参数调用一个函数

关键点有这几个

  • 函数被执行了
  • 指定 this 值
  • 可以传参

可以说这是一种指定 this 的函数调用方式

语法

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

参数

  • thisArg
    • 可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象原始值会被包装
  • arg1, arg2, ...
    • 指定的参数列表。
  • 返回值
    • 使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。
  • 描述
    • call() 允许为不同的对象分配和调用属于一个对象的函数/方法。
    • call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

如果对 this 指向问题不甚了解,可以先移步这篇 一文说透 JS 中的 this 绑定规则

简单手写实现

实现

  1. 写个测试用例先
var boy = {
    height: 185
};

function say(name) {
    console.log(this.height);
    return {
        height: this.height,
        name: name,
    }
}

// 原生 call 的效果
// 指定 this 为 boy 对象,所以 this.height 打印为 185, 并指定 name 参数
say.call(boy, 'Keal') // 185

// say 方法返回对象,可以打印
console.log(say.call(boy, 'Keal'))
// 185
// { height: 185, name: 'Keal' }

我们可以思考下

  • 我们如果不用 call 调用 ,这个 say 就是个普通方法,那么这里面 this 指向全局。
  • boy 中是不含 say 方法的,但我们又想打印出 boy 对象的 height
  • say.call(boy, 'Keal') say 方法调用中间加了 call,就是指定了 say 方法调用this,即第一个参数 boy,后面的就是其他传递的参数。
  1. 实现主逻辑
Function.prototype.myCall = function (context, ...args) {
    // 如果不传入 上下文对象 浏览器下使用 window 对象
    var context = context || window;

    // 使用 Symbol 是防止原有同名属性的覆盖
    const func = Symbol()

    // 注意你是从 say 上调用的 myCall 所以当前 this 就是指向 say 这个 function
    // console.log(this) // [Function: say]
    // 我们给 指定的 上下文对象新建一个方法 func, 现在 boy 上有 say 方法了
    context.func = this;

    // 用传入的剩余参数调用这个方法,并存储调用结果
    let res = context.func(...args)

    // 调用完后删除该方法
    delete context.func

    // 返回调用结果
    return res;
}

var boy = {
    height: 185
};

function say(name) {
    console.log(this.height);
    return {
        value: this.height,
        name: name,
    }
}

say.myCall(boy, 'Keal') // 185

console.log(say.myCall(boy, 'Keal'))
// 185
// { value: 185, name: 'Keal' }

13. apply

是什么

MDN: apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数

call() 方法的作用和 apply() 方法类似,区别就是 call() 方法接受的是参数列表,而 apply() 方法接受的是一个参数数组

语法

func.apply(thisArg, [argsArray])

参数/不同之处

  • argsArray 可选的一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象

简单手写实现

实现

Function.prototype.myApply = function (context, arrArgs) {
    // 如果不传入 上下文对象 浏览器下使用 window 对象
    var context = context || window;

    // 使用 Symbol 是防止原有同名属性的覆盖
    const func = Symbol()

    // 注意你是从 say 上调用的 myApply 所以当前 this 就是 say 这个 function
    // console.log(this) // [Function: say]
    // 我们给 指定的 上下文对象新建一个方法 func, 现在 boy 上有 say 方法了
    context.func = this;

    // 用传入的剩余参数调用这个方法,并存储调用结果
    let res = arrArgs ? context.func(...arrArgs) : context.func()

    // 调用完后删除该方法
    delete context.func

    // 返回调用结果
    return res;
}

var boy = {
    height: 185
};

function say(name, age) {
    console.log(this.height);
    console.log(name)
    return {
        value: this.height,
        name: name,
        age: age
    }
}

say.myApply(boy, ['Keal', 18]) 
// 185
// Keal 

console.log(say.myApply(boy, ['More', 25]))
// 185
// More
// { value: 185, name: 'Keal' }

今天就到这里,下篇讲讲 bind 的实现。

另外向大家着重推荐下另一个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列 记得点赞哈

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考