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()
这个部分的学习不要着急,慢慢理解,这个理解了,对后面的有很多的理解都是有好处的!!!