美文网首页
如何实现 new 、apply 、call 、bind 的底层逻

如何实现 new 、apply 、call 、bind 的底层逻

作者: PYFang | 来源:发表于2021-09-26 16:21 被阅读0次

    new 原理介绍

    new 关键词的主要作用是执行一个构造函数、返回一个实例对象,根据构造函数的怀况中,来确定是否可以接受参数的传递。

    function Person() {
      this.name = 'Jack'
    }
    let p = new Person()
    console.log(p.name) // Jack
    

    new 在生成实例的过程中进行的步骤:

    • 创建一个新对象
    • 将构造函数的作用域赋给新对象(this指向新对象)
    • 执行构造函数中的代码(为这个新对象添加属性)
    • 返回新对象

    当构造函数中有return一个对象的操作时:

    function Person() {
      this.name = 'Jack'
      return{age:18}
    }
    let p = new Person()
    console.log(p) // {age:18}
    console.log(p.name) // undefined
    console.log(p.age) // 18
    

    上面代码可以看出,当构造函数return一个和this无关的对象时,new会直接返回这个新对象 ,而不是通过new执行步骤生成的this对象 。
    注:构造函数必须返回是一个对象,如果返回不是对象,那么还是按照new的实现步骤返回新生成的对象。

    function Person() {
      this.name = 'Jack'
      return 'tom'
    }
    let p = new Person()
    console.log(p) // {name:'Jack'}
    console.log(p.name) // Jack
    

    new 被调用后大致做了哪几件事情

    • 让实例可以访问到私有属性
    • 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
    • 构造函数返回的最后结果是引用数据类型

    总结:new关键词执行之后总是会返回一个对象,要么是实例对象,要么是return语句指定的对象

    apply & call & bind 原理介绍

    call 、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法必须是一个函数

    语法:

    func.call(thisArg,param1,param2,...)
    func.apply(thisArg,param1,param2,...)
    func.bind(thisArg,param1,param2,...)

    thisArgthis所指向的对象,后面的param1,param2为函数的function的多个参数,如果不需要参数可不写

    相同点:都可改变 function 的 this 指向

    call & apply 区别:
    传参的写法不同

    • apply第二个参数为数组
    • call则是第二个至第N个都是给fcuntion的传参

    bind 与 call & apply区别:

    • bind虽然改变了functionthis指向,但不是马上执行
    • call & apply 是在改变functionthis指向后立即执行
    借用

    A对象有个getName的方法,B 对象也需要临时使用同样的方法,那么这个时候可以借用A 对象的getName方法
    例:

    let a = {
      name: 'jack',
      getName: function(msg) {
        return msg + this.name
      }
    }
    let b = {
      name: 'lily'
    }
    console.log(a.getName('hello~')) // hello~jack
    console.log(a.getName.call(b, 'hello~')) // hello~lily
    console.log(a.getName.apply(b, ['hello~'])) // hello~lily
    let name = a.getName.bind(b, 'hello~')
    console.log(name()) // hello~lily
    

    apply & call & bind 的使用场景

    1、判断数据类型

    Object.prototype.toString几乎可以判断所有类型的数据

    function getType(obj) {
      let type = typeof obj
      if (type !== 'object') {
        return type
      }
      return Object.prototype.toString.call(obj).replace(/^$/, '$1')
    }
    

    判断数据类型就是借用了Object.prototype.toString方法,最后返回用来判断传入的object字符串来确定最后的数据类型

    2、类数组的借用方法

    类数组因为不是真正的数组,所以没有数组类型上自带的种种方法,可以利用一些方法云借用数组的方法。

    var arrayLike = {
      0: 'java',
      1: 'script',
      length: 2
    }
    Array.prototype.push.call(arrayLike, 'jack', 'lily')
    console.log(typeof arrayLike) // object
    console.log(arrayLike) // {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}
    
    3、获取数组的最大 / 最小值

    apply来实现数组中判断最大 / 最小值,apply直接传递数组作为调用方法的参数,也可以减少一步展开数组

    let arr = [13, 6, 10, 11, 16]
    const max = Math.max.apply(Math, arr)
    const min = Math.min.apply(Math, arr)
    console.log(max) // 16
    console.log(min) // 6
    
    继承
    function Parent3() {
      this.name = 'parent3'
      this.play = [1, 2, 3]
    }
    Parent3.prototype.getName = function() {
      return this.name
    }
    function Child3() {
      // 第二次调用 Parent3()
      Parent3.call(this)
      this.type = 'child3'
    }
    Child3.prototype = new Parent3()
    // 手动挂上构造器,指向自己的构造函数
    Child3.prototype.constructor = Child3
    var s3 = new Child3()
    s3.play.push(4)
    console.log(s3.getName()) //parent3
    
    apply 和 call 的实现
    Function.prototype.call = function(context, ...args) {
      var context = context || window
      context.fn = this
      var result = eval('context.fn(...args)')
      delete context.fn
      return result
    }
    Function.prototype.apply = function(context, args) {
      let context = context || window
      context.fn = this
      let result = eval('context.fn(...args)')
      delete context.fn
      return result
    }
    
    

    这两个方法是直接返回执行结果 ,而 bind 方法是返回一个函数,因此这里直接用 eval 执行得到结果

    bind 的实现

    bind 的实现思路基本和 apply 一样,但是在最后实现返回结果这里 bind 不需要直接执行,因此不再需要用 eval 而是需要通过返回一个函数的方式将结果返回之后再通过执行这个结果,得到想要的执行效果

    Function.prototype.bind = function(context, ...args) {
      if (typeof this !== 'function') {
        throw new Error('this must be a function')
      }
      var self = this
      var fbound = function() {
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)))
      }
      if (this.prototype) {
        fbound.prototype = Object.create(this.prototype)
      }
      return fbound
    }
    
    总结
    总结

    相关文章

      网友评论

          本文标题:如何实现 new 、apply 、call 、bind 的底层逻

          本文链接:https://www.haomeiwen.com/subject/wfgueltx.html