• 主页
  • 个人简介
    • 圣墨 photo

      圣墨

      一个爱折腾,有诗有远方的人

    • Learn More
    • Github
    • Cnblogs
    • Weibo
  • 文章
    • 所有文章
    • 所有标签
  • Html&Css
  • Javascript
  • 设计模式
  • 前端性能优化
  • 原生实现专题
  • 数据结构与算法
  • Book
  • 面试题
  • 前端工具
  • 随记

javaScript中call、apply和bind详解

25 Feb 2019

Reading time ~1 minute

一、前言

在我们学习JavaScript中,不可避免的会遇到call、apply和bind的用法,三者的相似之处?三者的区别?
三者原生实现的方法?这些是本文主要探讨的内容。

二、call、apply和bind相似之处

1、对三者的定义:
    call()方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)。
    apply()方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类数组对象)提供的参数。
    bind()其具有一个指定的this值和提供的参数, 返回一个原函数的拷贝
2、三者相似之处
    * 都是用来改变函数的this对象的指向的。
    * 第一个参数都是this要指向的对象。
    * 都可以利用后续参数传参。

三、call、apply和bind的区别

1、举例说明:

    var x = 0;
    function f(y,z) {
        console.log(this.x + y+ z)
    }
    var obj = {
        x: 1
    }
    f(2,3); // 0+2+3=5
    f.call(obj, 2,3); // 1+2+3=6
    f.apply(obj, [2,3]) // 1+2+3=6
    f.bind(obj,2,3)() // 1+2+3=6
  • f() 直接调用返回的是5 因为直接调用this指向全局对象window,在全局对象中有一个x=0;
  • f.call(obj,2,3)、f.apply(obj,[2,3]) 第一个参数obj,此时this指向的对象是obj,所以this.x = 1(obj.x=1),两者的主要区别在于传递的参数方式不同,call是已列表的方式传参,apply必须是数组的方式
  • f.bind(obj,2,3) 如果后边不加上() 返回的只是f这个函数,加上()后才可调用

四、原生实现的方法

call 实现方法

实现思路:

  • 不传入第一个参数,那么默认为 window
  • 改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?
Function.prototype.myCall = function (context) {
  // ① 如果myCall第一个参数null,此时context是window对象,如果传参就是这个对象,测试中context就是obj这个对象
  var context = context || window; 
  // ② 第二给context这个对象添加一个方法,让这个方法等于this,this的指向问题:谁调用this就指向谁,此时this是f这个方法
  // ③ 现在context数据如下
  /*
    context = {
    x: 1,
    fn: function f(y,z) {
      console.log(this.x + y+ z)
    }
  }
  */
  context.fn = this
  // ④ 截取参数,此时args = [2,3]
  var args = [...arguments].slice(1)
  // ⑤ 调用context对象中fn方法,也就是测试中f的方法
  var result = context.fn(...args)
  // ⑥ 删除 fn
  delete context.fn
  return result
}
// 测试
var x = 0;
function f(y,z) {
    console.log(this.x + y+ z)
}
var obj = {
    x: 1
}
f.myCall(obj, 2,3); // 1+2+3=6

apply 实现方法

  • apply 实现思路和call差不多,只是在处理传入第二参数不同
Function.prototype.myApply = function (context) {
  var context = context || window
  context.fn = this

  var result
  // 需要判断是否存储第二个参数
  // 如果存在,就将第二个参数展开
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }

  delete context.fn
  return result
}

bind 实现方法

Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}


javascript 原生实现  微博  QQ  朋友圈