JavaScript ES6前言准备知识 函数中this绑定 箭头函数

171 阅读14分钟

JavaScript ES6前言准备知识 函数中this绑定 箭头函数

JavaScript 函数中的this指向

注意一点的是:在我们的 JavaScript 中,函数默认的返回值是 undefined

返回值为空也是返回的是 undefined



JavaScript 中的 this 机制是什么呐???

在函数实现调用的时候,JavaScript 会实现默认的绑定一个 this

函数中的 this 实现的绑定和我们的定义的位置 是没有关系的

函数中的 this 绑定和调用的方式以及调用的位置 是有关系的

函数中的 this 实现绑定是在运行的时候 才实现的绑定



JavaScript 中的 this绑定规则是什么呐???

  • 绑定一:默认绑定
    • 就是我们的函数直接实现调用

    • 我们看待默认绑定的时候,首先需要先确定运行环境是什么,

      如果是 浏览器环境 中运行的话,那么绑定的就是 window全局对象

      如果是 nodejs 环境 中运行的话,那么就是绑定的就是 global全局对象

      但是无论如何绑定的都是我们的 全局对象

    • 但是需要注意的一点是,当开启了我们的严格模式后,这个时候,就是指向的是 undefined "use strict";

    • function foo() {
          console.log("foo函数的this指向为:", this);
      }
      foo()
      
    • var obj = {
          name: "76433",
          foo: function () {
              console.log("函数中的this指向是:", this)
          }
      }
      ​
      var foo = obj.foo
      foo()
      
    • var obj = {
          name: "76433",
          foo: function () {
              console.log("函数中的this指向是:", this)
          }
      }
      ​
      /**
       * 隐式调用方式二:
       * @param {function} fn
       */
      function foo01(fn) {
          fn()
      }
      foo01(obj.foo)
      
    • /**
       * foo01 函数
       */
      function foo01() {
          console.log("foo01函数中的this指向:", this)
      }
      ​
      /**
       * foo02 函数
       */
      function foo02() {
          console.log("foo02函数中的this指向:", this)
          foo01()
      }
      ​
      /**
       * foo03 函数
       */
      function foo03() {
          console.log("foo03函数中的this指向:", this)
          foo02()
      }
      ​
      foo03()
      
  • 绑定二:隐式绑定
    • 这个时候,我们函数的调用发起者是谁,那么这个时候函数中的 this 就是指向谁
    • function foo() {
          console.log("foo函数中的this指向:", this)
      }
      ​
      var obj01 = {
          name: "juwenzhang",
          func: foo
      }
      ​
      var obj02 = {
          name: "76433",
          func: foo
      }
      ​
      var obj03 = {
          name: "水逆信封",
          func: foo
      }
      ​
      obj01.func()
      obj02.func()
      obj03.func()
      
  • 绑定三:new绑定
    • 我们实现定义的函数是可以通过使用我们的 new 操作符实现调用的

    • new 操作符可以实现的功能含有

        • 创建一个新的空对象
        • 将函数中的 this 指向这个空对象
        • 执行函数功能体中的代码
        • 同时产生原型链
    • function Foo() {
          console.log("foo函数中的this指向:", this)
      }
      ​
      new Foo()  // foo函数中的this指向: Foo {}
      
    • function Foo() {
          console.log("foo函数中的this指向:", this)
          this.name = "Foo"
      }
      ​
      console.log(new Foo().name)  // Foo
      
  • 绑定四:显式绑定


JavaScript 中的显式绑定

  • 显式绑定就是我们常说的,你知道几种改变对象 this 的方法呐???
    • 这个时候就涉及到了我们的三种改变函数调用的时候改变 this 的绑定的方法了

      • apply
      • call
      • bind
    • function foo() {
          console.log("foo函数中的this指向:", this)
          return this
      }
      ​
      var obj = {
          name: "76433"
      }
      ​
      foo.call()
      foo.call(obj)
      console.log(foo.call(obj).name)  // 76433
      
  • 显式绑定方法(一) —— apply
    • apply 方法具有的作用:

      • 1.实现改变函数中的 this 指向
      • 2.同时实现立即执行函数
    • apply 方法中的参数

      • 1.需要让函数 this 绑定的对象或者具有包装类的数据类型值
      • 2.对于含有参数的函数来说,传递参数需要需要使用列表形式实现传递
    • /**
       * 没有参数的函数调用 apply 方法
       * @returns undefined
       */
      function foo() {
          console.log("foo函数中的this指向:", this)
      }
      ​
      /**
       * 含有参数的函数调用 apply 方法
       * @param {Number} age
       * @param {String} name
       * @returns undefined
       */
      function bar(age, name) {
          console.log("bar函数中的this指向:", this)
      }
      ​
      var obj = {
          name: "76433"
      }
      ​
      ​
      /**
       * apply 方法具有的作用:
       *  1.实现改变函数中的 this 指向
       *  2.同时实现立即执行函数
       * 
       * apply 方法中的参数
       *  1.需要让函数 this 绑定的对象或者具有包装类的数据类型值
       *  2.对于含有参数的函数来说,传递参数需要需要使用列表形式实现传递
       */
      ​
      ​
      /**
       * 情形一: apply 中什么都不传入,并且函数没有参数
       * 下面的调用方法就等价于我们的: foo.apply(undefined)
       * 这个时候,函数 this 的绑定就是 全局对象
       */
      foo.apply()
      ​
      ​
      /**
       * 情形二: apply 中传入一个对象或者说具有包装类的数据,并且函数没有参数
       * 这个时候,函数中的 this 指向就是我们传入的对象或者对应的包装类
       */
      foo.apply(obj)
      foo.apply(123)
      foo.apply("string")
      ​
      ​
      /**
       * 情形三: apply 中什么都不传入传入(但是为了好操作,所以说只能传入一个 undefined | null,但是函数具有参数
       * apply 实现传入参数的时候,是使用的数组形式传入参数
       * 这个时候函数中的 this 指向就是我们的 全局对象
       */
      bar.apply(undefined, [18, "76433"])
      ​
      ​
      /**
       * 情形四: apply 中传入对象并且传入一个具有包装类的数据,并且函数具有参数
       * 这个时候,函数中的 this 指向就是传入的对象或者对应的包装类
       * 参数还是用的数组进行包裹
       */
      bar.apply(obj, [18, "juwenzhang"])
      bar.apply(123, [18, "juwenzhang"])
      bar.apply("string", [18, "juwenzhang"])
      
  • 显式绑定方法(二)—— call
    • call 方法具有的作用:

      • 1.实现改变函数中的 this 指向
      • 2.同时实现立即执行函数
    • call 方法中的参数

      • 1.需要让函数 this 绑定的对象或者具有包装类的数据类型值
      • 2.对于含有参数的函数来说,传递参数直接以多参数的形式实现传递即可
    • /**
       * 没有参数的函数调用 call 方法
       * @returns undefined
       */
      function foo() {
          console.log("foo函数中的this指向:", this)
      }
      ​
      /**
       * 含有参数的函数调用 call 方法
       * @param {Number} age
       * @param {String} name
       * @returns undefined
       */
      function bar(age, name) {
          console.log("bar函数中的this指向:", this)
      }
      ​
      var obj = {
          name: "76433"
      }
      ​
      ​
      /**
       * call 方法具有的作用:
       *  1.实现改变函数中的 this 指向
       *  2.同时实现立即执行函数
       *
       * call 方法中的参数
       *  1.需要让函数 this 绑定的对象或者具有包装类的数据类型值
       *  2.对于含有参数的函数来说,传递参数直接以多参数的形式实现传递即可
       */
      ​
      ​
      /**
       * 情形一: call 中什么都不传入,并且函数没有参数
       * 下面的调用方法就等价于我们的: foo.call(undefined)
       * 这个时候,函数 this 的绑定就是 全局对象
       */
      foo.call()
      ​
      ​
      /**
       * 情形二: call 中传入一个对象或者说具有包装类的数据,并且函数没有参数
       * 这个时候,函数中的 this 指向就是我们传入的对象或者对应的包装类
       */
      foo.call(obj)
      foo.call(123)
      foo.call("string")
      ​
      ​
      /**
       * 情形三: call 中什么都不传入传入(但是为了好操作,所以说只能传入一个 undefined | null,但是函数具有参数
       * apply 实现传入参数的时候,是使用多参数的形式传入参数
       * 这个时候函数中的 this 指向就是我们的 全局对象
       */
      bar.call(undefined, 18, "76433")
      ​
      ​
      /**
       * 情形四: call 中传入对象并且传入一个具有包装类的数据,并且函数具有参数
       * 这个时候,函数中的 this 指向就是传入的对象或者对应的包装类
       * 参数还是用的多参数形式传入
       */
      bar.call(obj, 18, "juwenzhang")
      bar.call(123, 18, "juwenzhang")
      bar.call("string", 18, "juwenzhang")
      
  • 显式绑定方法(三)—— bind
    • bind 的使用场景

      • 实现改变我们函数的 this 指向的时候,是不会立即执行函数的
      • 这个函数实现的场景是: 在后续的函数的调用中,不管我们函数的执行上下文是什么,总是绑定是同一个 this 指
      • 也就是说,这个函数方法的使用,就是为了实现函数调用的时候一直绑定相同的 this 指向
    • bind 函数的作用

      • 实现绑定函数执行时候的 this
      • 但是不会立即执行函数,这个会实现创建一个新的函数实现返回,这个函数是绑定函数(bound function
    • bind 函数的参数

      • 需要进行绑定的对象或者对应包装类的数据值
      • 使用数组的形式为含有参数的函数传递值
    • /**
       * 用来演示没有参数时候的 bind 函数的使用
       * @returns undefined
       */
      function foo() {
          console.log("foo的 this 指向为:", this)
      }
      ​
      /**
       * 含有参数的函数调用 bind 方法
       * @param {Number} age
       * @param {String} name
       * @returns undefined
       */
      function bar(age, name) {
          console.log("bar函数中的this指向:", this)
      }
      ​
      var obj = {
          name: "水逆信封"
      }
      ​
      /**
       * 默认绑定的是全局对象,等价于绑定 undefined
       */
      var fooClone01 = foo.bind()
      fooClone01()
      ​
      ​
      /**
       * 实现绑定我们的 obj
       */
      var fooClone02 = foo.bind(obj)
      fooClone02()
      ​
      ​
      /**
       * 带有形参的函默认绑定
       */
      var barClone01 = bar.bind(undefined, 18, "76433")
      barClone01()
      ​
      /**
       * 带有参数的且指定对象以及包装类实现绑定
       */
      var barClone02 = foo.bind(obj, 18, "水逆信封")
      barClone02()
      


实现绑定 this 时候,我们的绑定的优先级是:

  • 显式绑定 > 隐式绑定 > 默认绑定

  • new 绑定 > 隐式绑定 > 默认绑定

  • new 绑定 不可以和 显式绑定call/apply一起使用, 但是是可以和bind函数一起使用

    • 从上面的讲解中我们是可以发现, bind 绑定是实现了返回了一个新的函数的,所以说这个时候就可以实现绑定
  • bind 优先级高于 apply/call

JavaScript 箭头函数(Arrow Function)

JavaScript 箭头函数介绍

箭头函数是ES6 之后出现的一种编写函数的新方法,实现了我们书写函数的简介性

  • 箭头函数的规则

    • 箭头函数不会绑定 this , arguments 属性(但是还是有这两个属性的)
    • 箭头函数不可以作为构造函数来使用(就是不可以和我们的 new 操作符一起使用

JavaScript 中的函数书写方式

  • 使用定义函数的形式书写函数

    • function func_name(paramsters) {}
  • 使用函数表达式形式书写函数

    • var|let|const func_name = function(paramsters) {}
  • 现在的箭头函数的书写方式

    • var|let|const func_name = (paramsters) => {}
/**
 * 函数声明式写法
 * @returns undefined
 */
function foo01() {
    console.log("我是函数声明类型的JavaScript函数的书写方法...")
}
​
​
/**
 * 带有参数的函数声明式写法
 * @param {string} name
 * @param {number} age
 * @returns undefined
 */
function foo001(name, age) {
    console.log("我是带有参数的函数声明...")
}
​
​
/**
 * 函数表达式书写方式
 * @returns undefined
 */
var foo02 = function () {
    console.log("我是函数表达式的书写方式...")
}
​
​
/**
 * 带有参数的函数表达式写法
 * @param {string} name
 * @param {number} age
 * @returns undefined
 */
var foo002 = function (name, age) {
    console.log("我是带有参数的函数表达式的写法...")
}
​
​
/**
 * 箭头函数的书写方式
 * @returns undefined
 */
var foo03 = () => {
    console.log("函数的箭头函数书写方式...") 
}
​
/**
 * 带有参数的箭头函数书写方式
 * @param {string} name
 * @param {number} age
 * @returns undefined
 */
var foo003 = (name, age) => {
    console.log("我是带有参数的箭头函数书写方式...")
}

JavaScript 箭头函数的书写规则

讨论箭头函数的书写规则主要就是来讨论 () 和 {} 的书写 , 从而实现箭头函数的简写

箭头函数 简写情形(一)

当我们的箭头函数的参数只有一个的时候,可以省略 ()

var arr = ["juwenzhang", "76433", "水逆信封"]
var arr01 = ["小菊", "小橘", "小鞠", "橘子"]
​
/**
 * 带有多个参数的时候书写的方式
 * 顺便复习:
 *   1.forEach 方法第一个参数是需要进行遍历的每一个元素
 *   2.forEach 方法第二个参数是元素对应的下标index
 *   3.arr 自动指代的就是参与遍历的数组
 */
arr.forEach((item, index, arr) => {
    console.log(item, index, arr)
})
​
​
/**
 * 当我们只需要获取每一个元素信息的时候
 * 这个时候就可以实现省略 () 书写
 */
arr.forEach(item => {
    console.log(item)
})
箭头函数 简写情形(二)

当箭头函数的执行体只有一行执行语句的时候,{} 可以省略(除了含有return 的单行执行语句)

var arr01 = ["小菊", "小橘", "小鞠", "橘子"]
​
arr01.forEach((item, index) => console.log(`下标${index} 对应的元素值为 ${item}`))
箭头函数 简写形式(三)

如果我们的执行体里面只是一个数据或者说对象,那么这个时候就是实现的时自动添加 return 进行返回的操作

var arr01 = ["小菊", "小橘", "小鞠", "橘子"]
​
/**
 * 开始实现我们的forEach 实现遍历的时候,直接返回元素的箭头函数的写法
 */
var arr02 = arr01.map(item => item + "学姐")
console.log(arr02)
​
/**
 * 开始实现我们的链式调用准则
 */
arr01.map(item => item + "学姐").forEach(item => console.log(item))
箭头函数 简写形式(四)

如果默认的返回值是一个对象,那么这个时候对象必须加()

var obj = {
    name: "76433",
    age: 18
}
​
/**
 * 这种直接在前面实现了定义了的,就直接写即可,啥问题
 * @returns {{name: string, age: number}}
 */
var getObj = () => obj;
console.log(getObj());
​
/**
 * 这个时候如果直接书写我们的对象的时候,为了JavaScript 可以实现区分到底是代码块还是对象,就需要添加 () 来进行区分
 * 函数的默认返回值是我们的 undefined ,如果没有 () 来实现区分操作的话,那就是返回的 undefined
 * @returns {{name: string, age: number}}
 */
var getInitObj = () => ({name: "juwenzhang", age: 18});
console.log(getInitObj());
​
​
var arr = [1, 2, 3, 4, 5]
/**
 * 开始实现我们的数组方法的链式调用
 * 为什么可以实现链式调用,是因为每一个方法的底层源码中都是实现了返回了一个新数组的,所以说可以链式调用
 */
var sumFilMultiValue = arr.filter(item => item > 1)
    .map(item => item * item)
    .reduce((preValue, item) => preValue + item, 0)
console.log(sumFilMultiValue)

推荐了解数组中一些使用方法链接

JavaScript 箭头函数中的 this 使用

箭头函数中的 this 的使用实际上是一种查找机制,本身是没有 this 的,但是通过这种查找机制,就实现了在箭头函数任然可以使用

前面说过的,我们的箭头函数中是不需要绑定 this 的,但是还是可以实现使用 this 的
var foo = () => {
    console.log(this)
}

foo()

var obj = {
    name: "76433",
    foo: foo
}

obj.foo()

/**
 * 我们的箭头函数中的 this 不管是我们的
 * this 默认绑定,还是隐式绑定,还是显式绑定,还是什么绑定,但是这个 this 都是实现的是指向一个空对象
 *
 * 实际上的话,箭头函数中没有 this 这一个选项的,但是只不过,在箭头函数中 this 的使用实际上是一种作用域的查找流程
 * 通过不断的上行一层实现查找,就可以实现寻找到箭头函数中的 this 了,但是还是那句话,箭头函数中是没有 this 的
 */
所以说,我们的箭头函数的利用场景之一就有一个:当我们需要实现和 this 不相关的功能的时候,就可以使用 箭头函数
var obj = {
    name: "76433",
    foo: function () {
        var bar = () => {
            console.log(this)
        }
        return bar
    }
}

/**
 * 这个时候箭头函数中会出现 this 耶???
 *  就是因为我们的箭头函数的话中的 this 是实现的是我们的一种查找规则,本级作用域中没有 this
 *  那么就会像上一层级寻找 this 的指向
 *  在这个例子中的话,我我们的箭头函数的上一层级就是 function
 *  function 是具有 this 的,这个给时候 function 的this 指向的是谁,那么内部的箭头函数就是指向的谁
 */
obj.foo()();  // { name: '76433', foo: [Function: foo] }


/**
 * 同样的道理: 我们的箭头函数的指向一直依赖于我们的 foo 函数的指向, foo 函数中的指向是谁,那么箭头函数中的 this 就是指向谁
 * @type {bar}
 */
var barFunc = obj.foo();
barFunc()  // { name: '76433', foo: [Function: foo] }
上面的例子可能还不是十分的清楚,下面再来一个更加清晰的
function foo() {
    var bar = () => {
        console.log("bar 函数的 this 指向是:", this);
    }
    return bar;
}
​
var obj = {
    name: "juwenzhang",
    foo: foo
}
​
/**
 * 首先先在我们的全局默认的调用 foo 函数来看看 bar 箭头函数会指向谁???
 * 这个时候就是实现指向的是我们的 全局对象
 * 在浏览器环境中运行代码的话,那么函数中的 this 就是指向的是 window
 * 在 nodejs 环境中实现的运行代码的话,那就是指向的是 global
 */
foo()()
​
​
/**
 * 开始实现在通过我们的 对象间接的调用我们的 foo 函数
 * 这个时候 foo 函数的 this 绑定就是我们的 obj 对象
 */
obj.foo()()
​
​
/**
 * 开始实现通过我们的显式改变 foo 函数的 this
 * 这个时候实就实现了将我们的 foo 函数的 this 绑定为我们指定绑定的 this
 * 那么箭头函数通过他的查找规则就可以实现查找得到 this
 */
foo.call(Number)();
​
​
/**
 * 开始实现通过 new 操作符实现改变 this 指向
 */
var Foo = new foo()
Foo()

这个部分的学习不要着急,慢慢理解,这个理解了,对后面的有很多的理解都是有好处的!!!