• 【详解】如何手写实现call、apply、bind


    前言:

            面试的时候如果遇到需要手写代码,手写实现apply,call,bind也是问的比较多的,今天好好学习一下,怎么手写实现。

    用法:

    1.apply

    apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入

    改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

    语法:

    func.apply(thisArg, [argsArray])

    thisArg:函数运行时this指向的对象,必填
    argsArray:函数运行时传递的参数,数组形式,选填

    例子:

    1. function fn (a,b,c) {
    2. console.log(this.name)
    3. console.log(a, b, c)
    4. }
    5. let obj = {name: '码上游'}
    6. fn.apply(obj, [1,2,3])
    7. // 输出 码上游 1 2 3

    可以看到,fn执行时指向了obj,并且把传入的数组拆开,当作函数传入的参数。

    2.call

    apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

    区别在于:apply传入的参数必须是数组形式,call没有这个限制。

    语法:

    func.call(thisArg[, args1, args2, args3,....])

    thisArg:函数运行时this指向的对象,必填
    args1等:函数运行时传递的参数,选填

    例子:

    1. function fn (a,b,c) {
    2. console.log(this.name)
    3. console.log(a, b, c)
    4. }
    5. let obj = {name: '码上游'}
    6. fn.call(obj, 1, 2, 3)
    7. // 输出 码上游 1 2 3

    可以看到,fn执行时指向了obj,并且收集所有的参数,当作函数传入的参数。

    3.bind

    bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)

    改变this指向后不会立即执行,而是返回一个永久改变this指向的函数

    语法:

    func.bind(thisArg[, args1, args2, args3,....])

    例子:

    1. function fn (a, b, c, d) {
    2. console.log(this.name)
    3. console.log(a, b, c, d)
    4. }
    5. let obj = {name: '码上游'}
    6. let bindFn = fn.bind(obj, 1, 2, 3)
    7. bindFn('bind') // 输出 码上游 1 2 3 'bind'

    由例子可以看出 bind返回一个新函数,执行新函数的的时候this指向obj,然后把bind时传入的参数和调用新函数时传入的参数合并,一起传给新函数。

    手写实现:

    通过上面的apply,call,bind用法可以得知:

    1. apply,call,bind都是可以改变this的指向
    2. apply,call会执行调用的函数,bind返回一个新函数。
    3. apply第二个参数要求是数组,callbind则没有限制数据类型,它会把剩余的参数一起传给函数,bind还会把新函数调用时传入的参数一起合并,传给新函数。
    4. 他们都是绑定在Functionprototype上。

    下面来看看怎么手写实现。 因为它们都是绑定在Functionprototype上.

    1.apply

    1. Function.prototype.apply = function (context, args) {
    2. // 不传默认是全局,window
    3. context = context || window
    4. // args不传时默认是空数组,防止下面用spread操作符时报错
    5. args = args ? args : []
    6. // 把this存到context.fn,这里的this是调用的函数
    7. context.fn = this
    8. // 执行调用的函数,this指向context,参数用spread操作符扩展
    9. const res = context.fn(...args)
    10. // 删除,不污染context
    11. delete context.fn
    12. // 返回res
    13. return res
    14. }

    验证

    1. function fn (a,b,c) {
    2. console.log(this.name)
    3. console.log(a, b, c)
    4. }
    5. let obj = {name: '码上游'}
    6. fn.apply(obj, [1,2,3])
    7. // 输出 码上游 1 2 3

    2.call

    call和apply一样,主要是参数和apply不一样,小改一下就行;

    代码如下:

    1. Function.prototype.call = function (context, ...args) {
    2. // 不传默认是全局,window
    3. context = context || window
    4. // args不传时默认是空数组,防止下面用spread操作符时报错
    5. args = args ? args : []
    6. // 把this存到context.fn,这里的this是调用的函数
    7. context.fn = this
    8. // 执行调用的函数,this指向context,参数用spread操作符扩展
    9. const res = context.fn(...args)
    10. // 删除,不污染context
    11. delete context.fn
    12. // 返回res
    13. return res
    14. }

    主要是在call函数第二个参数,获取args时,使用了spread操作符,这样可以把剩余参数都获取到args中,其它的都一样。

    验证

    1. function fn (a,b,c) {
    2. console.log(this.name)
    3. console.log(a, b, c)
    4. }
    5. let obj = {name: '码上游'}
    6. fn.call(obj, 1, 2, 3)
    7. // 输出 码上游 1 2 3

    3.bind

    bind是不一样的,它要返回一个新函数,这个新函数可以被调用,也可以被当作构造函数,使用new操作符,所以这里要做区分。

    1. Function.prototype.bind = function (context, ...args) {
    2. // 不传默认是全局,window
    3. context = context || window
    4. // 把this存到fn,这里的this是调用的函数
    5. let fn = this
    6. return function newFn (...fnArgs) {
    7. let res
    8. // 要考虑新函数是不是会当作构造函数
    9. if (this instanceof newFn) {
    10. // 如果是构造函数则调用new 并且合并参数args,fnArgs
    11. res = new fn(...args, ...fnArgs)
    12. } else {
    13. // 当作普通函数调用 也可以用上面定义的_call
    14. res = fn.call(context, ...args, ...fnArgs)
    15. }
    16. return res
    17. }
    18. }

    验证

    1. function fn (a, b, c, d) {
    2. console.log(this.name)
    3. console.log(a, b, c, d)
    4. }
    5. let obj = {name: '码上游'}
    6. let bindFn = fn.bind(obj, 1, 2, 3)
    7. bindFn('bind') // 输出 码上游 1 2 3 'bind'
    1. let bindFn = fn.bind(obj, 1, 2, 3)
    2. let instance = new bindFn()
    3. instance.constructor === fn // true

    以上便是this绑定规则中显式绑定中的call、bind、apply总结,如果觉得有帮助的话,点个赞再走吧

  • 相关阅读:
    LeetCode 算法:LRU 缓存 c++
    性能测试监控建模之记录Tomcat性能调优
    Scala基础教程--13--函数进阶
    element-plus关于表单数据自定义参数校验
    华为堆叠技术讲解
    Mysql 随机字符串函数
    MySQL日期时间函数
    Qt——常用控件详解
    微服务线上问题排查困难?不知道问题出在哪一环?那是你还不会分布式链路追踪
    数分-工具-Pandas1-预备知识
  • 原文地址:https://blog.csdn.net/pakerder/article/details/127121988