call、apply、bind的区别和用法

作用:这三个函数的作用是改变函数执行时的上下文,说的再具体一点就是改变函数运行时的this指向。

    function Person(name) {
      this.name = name
    }

    Person.prototype = {
      showName: function () {
        console.log(this.name)
      }
    }

    var person = new Person('xiaogang')
    person.showName() // xiaogang

    var animal = {
      name: 'dog'
    }
    person.showName.call(animal) // dog 此时执行showName时this指向animal
    person.showName.apply(animal) // dog 此时执行showName时this指向animal
    person.showName.bind(animal)() // dog 此时执行showName时this指向animal

animal这个对象本身并没有showName方法,我们可以通过call、apply、bind来拿person这个实例的showName方法,并动态改变其上下文来帮自己输出信息,实现了复用

区别

call()

call() 方法在使用一个指定的this值和若干个指定的参数值得前提下调用某个函数或方法

当调用一个函数时,可以赋值一个不同的this对象。this引用当前对象,即call方法的第一个参数。

通过call方法,你可以在一个对象上借用另一个对象的方法,比如

Object.prototype.toString.call([]),就是一个Array对象借用了Object对象上的方法

语法 fun.call(thisArg[,arg1[,arg2[,...]])

thisArg

在fun函数运行时指定的this值。需要注意的是下面几种情况

  1. 不传,或者传null,undefined,函数中的this指向window对象
  2. 传递另一个函数的函数名,函数中的this指向这个函数的引用,并不一定是该函数执行时真正的this值
  3. 值为原始值(数字,字符串,布尔值)的this会指向该原始值得自动包装对象,如String、Number、Boolean
  4. 传递一个对象,函数中的this指向这个对象
    function a() {
      console.log(this)
    }
    function b() { }

    var c = { name: '小红' }

    a.call() // window
    a.call(null) // window
    a.call(undefined) // window
    a.call(1) // Number
    a.call('') // String
    a.call(false) // Boolean
    a.call(b) //  function b(){}
    a.call(c) // {name: '小红'}

使用call方法调用匿名函数

在下例中的for循环体内,我们创建了一个匿名函数,然后通过调用该函数的call方法,将每个数组元素作为指定的this值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个print方法,这个print方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为this值传入那个匿名函数(普通函数就可以),目的是为了演示call的用法。

    var animals = [
      { species: 'Lion', name: 'King' },
      { species: 'Whale', name: 'Fail' }
    ]

    for (var i = 0; i < animals.length; i++) {
      (function (i) {
        this.print = function () {
          console.log('#' + i + ' ' + this.species + ':' + this.name)
        }
        this.print()
      }).call(animals[i], i)
    }
    // #0 Lion:King
    // #1 Whale:Fail

使用call方法调用函数传参数

    var a = {
      name: 'JSLite.io',
      say: function () {
        console.log("Hi,I'm function a")
      }
    }
    function b(name,test) {
      console.log('post params:' + name + ' params1:'+ test)
      console.log("I'm" + this.name)
      this.say()
    }
    b.call(a, 'test','test1')
    // post params:test params1:test1
    // I'mJSLite.io
    // Hi,I'm function a

apply()

语法与call()方法的语法几乎完全相同,唯一的区别在于,apply的第二个参数必须是一个包含多个参数的数组(或类数组对象)。apply的这个特性很重要,在调用一个存在的函数时,你可以为其指定一个this对象。this指当前对象,也就是正在调用这个函数的对象。使用apply,你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

语法: fun.apply(thisArg[,argsArray])

注意:chrome 14以及 IE9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常

thisArg

同上 call 的 thisArg 参数

argsArray

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

    function jsy(x, y, z) {
      console.log(x, y, z)
      console.log(this.name)
    }
    var a = { name: '678' }
    jsy.apply(a, [1, 2])
    // 1 2 undefined
    // 678

使用apply来链接构造器的例子

你可以使用apply来给一个对象链接构造器,类似于Java。在接下来的例子中我们会创建一个叫做construct的全局Function函数,来使你能够在构造器中使用一个类数组对象而非参数列表

    Function.prototype.construct = function (aArgs) {
      var fConstructor = this
      var fNewConstr = function () {
        fConstructor.apply(this, aArgs)
      }
      fNewConstr.prototype = fConstructor.prototype
      return new fNewConstr()
    }
    function MyConstructor() {
      for (var nProp = 0; nProp < arguments.length; nProp++) {
        this['property' + nProp] = arguments[nProp]
      }
    }
    var myArray = [4,'Hello world', false]
    var myInstance = MyConstructor.construct(myArray)
    console.log(myInstance.property1) // Hello wold
    console.log(myInstance instanceof MyConstructor) // true
    console.log(myInstance.constructor) // f Myconstructor

使用apply和内置函数

聪明的apply用法允许你在某些本来需要写成遍历数组变量的任务中使用内建的函数。在接下来的例子中我们会使用Math.max/Math.min来找出一个数组中的最大/最小值

    var numbers = [5, 6, 2, 3, 7]
    var max = Math.max.apply(null, numbers)
    var min = Math.min.apply(null, numbers)
    console.log(max) // 7
    console.log(min) // 2

    //通常情况下我们会这样来找数组的最大或最小值
    max = -Infinity, min = +Infinity
    for (var i = 0; i < numbers.length; i++) {
      if (numbers[i] > max) {
        max = numbers[i]
      }
      if (numbers[i] < min) {
        min = numbers[i]
      }
    }
    // 或者使用最新的扩展语句spread operator
    var arr = [1, 2, 3]
    var max = Math.max(...arr)
    console.log(max) // 3

参数数组切块后循环传入

    function minOfArray(arr) {
      var min = Infinity
      var QUANTUM = 32768

      for (var i = 0; i < arr.length; i += QUANTUM) {
        var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, arr.length)))
        console.log(submin, min)
        min = Math.min(submin, min)
      }
      return min
    }

    var min = minOfArray([5, 6, 2, 3, 7])
    console.log(min) // 2

bind

bind() 函数会创建一个新函数(称为绑定函数)

  • bind是ES5新增的一个方法
  • 传参和call或apply类似
  • 不会执行对应的函数,call或apply会自动执行对应的函数
  • 返回对函数的引用
语法 fun.bind(thisArg[,arg1[,arg2[,...])

下面例子:当点击网页时,EventClick被触发执行,输出JSLite.io p1 p2,说明EventClick中的this被bind改变成了obj对象。如果你将EventClick.bind(obj,'p1','p2')变成EventClick.call(obj,'p1','p2') 的话,页面会直接输出JSLite.io p1 p2

    var obj = { name: 'JSLite.io' }
    document.addEventListener('click', EventClick.bind(obj, 'p1', 'p2'), false)

    function EventClick(a, b) {
      console.log(this.name, a, b)
    }
    // JSLite.io p1 p2

call、apply与bind的差别

call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数

call、apply的区别

他们俩之间的差别在于参数的区别,call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。

    fn.call(obj, arg1, arg2, arg3...)
    fn.apply(obj, [arg1,arg2,arg3...])

应用

知道了怎么使用和他们之间的区别,接下来我们来了解一下通过call、apply、bind常见应用场景。

继承

    function Animal(name, weight) {
      this.name = name
      this.weight = weight
    }

    function Cat(arr) {
      // 在call中将this作为thisArgs参数传递
      // Animal方法中的this就指向了Cat中的this
      // 所以Animal中的this指向的就是cat对象
      // 在Animal中定义了name和weight属性,就相当于在cat中定义了这些属性
      // cat对象便拥有了Animal中定义的属性,从而达到了继承的目的
      // Animal.apply(this, arr)
      Animal.call(this, ...arr)
      this.say = function () {
        console.log('I am ' + this.name + ', my weight is ' + this.weight)
      }
    }
    // 当通过new运算符产生了cat时,Cat中的this就指向了cat对象
    var cat = new Cat(['doudou', '40'])
    cat.say()
    // I am doudou, my weight is 40

原型拓展

在原型函数上扩展和自定义方法,从而不污染原生函数。例如:我们在Array上扩展一个forEach

    function test() {
      console.log(arguments instanceof Array, Array.isArray(arguments)) // false false
      console.log(arguments.forEach) // undefined
      Array.prototype.forEach.call(arguments, function (item) {
        console.log(item)
      })
    }
    test(1, 2, 3, 4)
    // 1
    // 2
    // 3
    // 4

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值