call()、apply() 和 bind()——浅析

总述

call(); apply(); bind(); 这三个方法是定义在Function.prototype中的.
因为js中的所有函数,都可以看成是对象, 都是由Function构造函数实例化出来的.

console.dir(Function.prototype)

在这里插入图片描述

call()

我们先看下面代码,并思考一个问题,如何改变test方法的this指向呢?从而让其可以打印出‘张三’

      let obj1 = {
        name: '张三'
      }
      let obj2 = {
        name: '李四',
        test: function () {
          console.log(this.name)
        }
      }
      obj2.test()//分别打印:'李四'、'老六'

call()语法: 方法.call(newThis,a1,a2……)
newThis,就是你要指向的新的对象,后面则是正常的参数

    let obj1 = {
        name: '张三'
      }
      let obj2 = {
        name: '李四',
        test: function (a) {
          console.log(this.name)
          console.log(a)
        }
      }
      // obj2.test('老六')//分别打印:'李四'、'老六'
      obj2.test.call(obj1, '老八') //37行打印——'张三',38行打印——'老八'

利用call(),将一个伪数组转换为真数组

      let obj = {
        0: '张三',
        1: '李四',
        2: '王五',
        3: '赵六',
        4: '冯七',
        length: 5
      }
      //将数组原型对象的方法--slice 的this指向改为obj,截取从下标0开始到最后的元素,返回一个真数组:arr
      let arr = Array.prototype.slice.call(obj, 0)
      console.log(arr)

结果:
在这里插入图片描述

万能数据类型检测

//Object.prototype.toString() 会返回一个 "[object type]",其中 type 是被检测数据的类型
      console.log(Object.prototype.toString.call(null))
      console.log(Object.prototype.toString.call(undefined))
      console.log(Object.prototype.toString.call(12))
      console.log(Object.prototype.toString.call('床前明月光'))
      console.log(Object.prototype.toString.call([]))
      console.log(Object.prototype.toString.call({}))
      console.log(Object.prototype.toString.call(Symbol()))
      console.log(Object.prototype.toString.call(true))
      console.log(Object.prototype.toString.call(() => {}))

在这里插入图片描述

apply()

语法: 函数名.apply(this的新指向,数组或者伪数组);

      function sum (num1, num2) {
        console.log(this)
        console.log(num1 + num2)
      }
      let obj = {
        name: '刀客'
      }
      sum.apply(obj, [1, 10])

此时打印结果如下:
在这里插入图片描述

利用apply(),将一个伪数组转换为真数组

      let obj = {
        0: '张三',
        1: '李四',
        2: '王五',
        3: '赵六',
        4: '冯七',
        length: 5
      }
      let arr = []
      arr.push.apply(arr, obj)
      console.log(arr)

在这里插入图片描述

求数组最大值

      let arr = [1, 2, 3, 4, 5, 6, 7, 8]
      //老方法如下
      // let num = -Infinity
      // let arr2 = []
      // for (key in arr) {
      //   if (arr[key] > num) {
      //     num = arr[key]
      //   }
      // }
      let max = Math.max.apply(Math, arr)
      console.log(max) // 8

bind()

语法: 函数名.bind(this的新指向,可选参数);

      let obj1 = {
        name: '张三'
      }
      let obj2 = {
        name: '李四',
        test: function (a, b) {
          console.log(this.name)
          console.log(a + ',' + b)
        }
      }
      let func = obj2.test.bind(obj1, '行到水穷处', '坐看云起时')
      // 上面的和下面的都可以传参,上面的优先级高
      func()

修改定时器中函数里的this指向

// bind()不会立即执行函数,而是会返回一个改了this指向,但是函数体是一样的这么一个函数回来. 
     let obj1 = {
        name: '张三'
      }
     setTimeout(
        function fn () {
          console.log(this)
          console.log('定时器')
        }.bind(obj1),
        1000
      )

在这里插入图片描述
如上所示,将thiswindow转变为了obj1

三个方法的异同

call()特点

  • 会立即执行函数
  • 可传入多个参数
  • 可改变this指向

apply() 特点

  • 会立即执行函数
  • apply只有2个参数,第一个是this的新指向,第二个是数组或者伪数组.
  • 可改变this指向

bind() 特点

  • 不会立即执行函数,而是会返回一个改了this指向,但是函数体是一样的这么一个函数回来.
  • apply第2个参数可选
  • 可改变this指向

后述

若使用函数上下文调用模式修改this,this的新指向不是一个对象,而是一个普通类型,那this指向谁?

      function test () {
        console.log(this)
      }

      function fn () {}
      let arr = []
      let obj = {}
      let nul = null

      test.call(123) //this指向基本包装类型Number对象
      test.call(NaN) //this指向基本包装类型Number对象
      test.call('abc') //this指向基本包装类型String对象
      test.call(true) //this指向基本包装类型Boolean对象
      test.call(null) //this指向window对象
      test.call(undefined) //this指向window对象
      console.log(typeof fn)
      console.log(typeof arr)
      console.log(typeof obj)
      console.log(typeof nul)

      console.log(arr.__proto__.constructor)
      console.log(obj.__proto__.constructor)
      console.log(obj.__proto__.__proto__ == nul)
      console.log(nul instanceof Array)
      console.log(obj instanceof Object)

打印结果如下图:
在这里插入图片描述

后记——继承

      //父亲构造函数(姓氏,汽车)
      function Father (xing, car) {
        this.xing = xing
        this.car = car
      }

      //儿子构造函数
      function Son (xing, car, job) {
        // this.xing = xing;
        // this.car = car;
        // 上面这两句话,在Father构造函数里已经写过了.那我这里就不想重复的去写.
        // 就想继承
        // Father(xing,car); //window.Father(xing,car)
        // 上面这句话不行,因为这样的话执行Father函数,函数体的this是window..那就相当于是给window对象加了xing和car
        // 而我们现在希望调用Father的时候,他里面this是new关键字创建出来的对象
        // 方法1
        // Person.call(this,name,age)
        // 方法2
        // Person.apply(this,[name,age])
        // 方法3
        // Father.bind(this) 会返回一个新的函数,所以,可以在调用是传入参数
        Father.bind(this)(xing, car)

        this.job = job
      }
      //实例化学生对象
      let s1 = new Son('欧阳', '五菱', '码农')
      console.log(s1)

在这里插入图片描述

组合继承
      //人构造函数
      function Person (name, age) {
        this.name = name
        this.age = age
      }
      //人构造函数对应的原型
      Person.prototype.sayHi = function () {
        console.log('你好,我的名字是' + this.name)
      }

      //---------------------------------------------
      //学生构造函数
      function Student (name, age, score) {
        //借用构造函数继承
        Person.call(this, name, age)

        this.score = score
      }
      //每一个学生也有打招呼的方法.可以替换原型继承
      Student.prototype = new Person()
      //每一个学生也有学习的方法.往原型中添加学习的方法
      Student.prototype.study = function () {
        console.log(`我是学生,我的名字是${this.name}我在学习`)
      }

      //实例化学生对象
      let s1 = new Student('张三', 18, 100)
      console.log(s1)
      s1.sayHi()
      s1.study()

在这里插入图片描述

问题

上面的代码中:

Student.prototype = new Person()

为什么不能改成:

Student.prototype = Person.prototype;

原因:
改了后,Student的原型对象中也有了sayHi方法,同时,Person的原型对象中也有了study方法,这样不合逻辑,继承就是,我有你的,你没我的;现在是你中有我,我中有你。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值