2.(ECMAScript)es6完全解读(1)

1. 重点提炼

  • 作用域
  • let 与 var、const的使用与区别
  • 解构赋值的使用 => 数组、对象、字符串等

2. 作用域

对象类型
global/window全局作用域
function函数作用域(局部作用域)
{}块状作用域
this动态作用域

如果一个 变量 或者其他表达式不在 “当前的作用域”,那么JavaScript机制会继续沿着作用域链上查找直到全局作用域(global或浏览器中的window)如果找不到将不可被使用。

2.1 全局作用域

变量在函数或者代码块 {} 外定义,即为全局作用域。不过,在函数或者代码块 {} 内未定义的变量也是拥有全局作用域的(不推荐)。

var course = "es"

// 此处可调用 course 变量
function myFunction() {
    // 函数内可调用 course 变量
}

上述代码中变量 course 就是在函数外定义的,它是拥有全局作用域的。这个变量可以在任意地方被读取或者修改,当然如果变量在函数内没有声明(没有使用 var关键字),该变量依然为全局变量。

// 此处可调用 course 变量

function myFunction() {
    course = "es"
    // 此处可调用 course 变量
}

以上实例中 course在函数内,但是拥有全局作用域,它将作为 global或者 window的属性存在。

注意

在函数内部或代码块中没有定义的变量实际上是作为window/global的属性存在,而不是全局变量。换句话说没有使用 var定义的变量虽然拥有全局作用域,但是它是可以被 delete(只能删除属性)的,而全局变量不可以。


2.2 函数作用域

在函数内部定义的变量,就是局部作用域。函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!

function bar() {
    var testValue = 'inner'
}

console.log(testValue) // 报错:ReferenceError: testValue is not defined

如果想读取函数内的变量,必须借助 return 或者闭包。

function bar(value) {
    var testValue = 'inner'

    return testValue + value
}

console.log(bar('fun')) // "innerfun"

这是借助 return 的方式,下面是闭包的方式:

function bar(value) {
    var testValue = 'inner'

    var rusult = testValue + value

    function innser() {
        return rusult
    }

    return innser()
}

console.log(bar('fun')) // "innerfun"

通俗的讲,return是函数对外交流的出口,而 return可以返回的是函数,根据作用域的规则,函数内部的子函数是可以获取函数作用域内的变量的。

说到这其实大家会想到嵌套函数的作用域问题,如果 inner 函数再嵌套函数呢?这就涉及到另一个概念:作用域链。

仔细观察上图,其实不难理解作用域链是什么,因为你可以按照原型链那样去理解。任何一个作用域链都是一个堆栈,首先先把全局作用域压入栈底,再按照函数的嵌套关系一次压入堆栈。在执行的时候就按照这个作用域链寻找变量。


2.3 块状作用域

块状作用域是很熟悉的概念,但是在JavaScript中不被支持,就像上述知识一样,除了全局作用域就是函数作用域,一直没有自己的块状作用域。在 ES6中已经改变了这个现象,块状作用域得到普及。关于什么是块,只要认识 {} 就可以了。

if (true) {
    let a = 1
    console.log(a)
}

在这个代码中, if 后 {} 就是“块”,这个里面的变量就是拥有这个块状作用域,按照规则, {} 之外是无法访问这个变量的。


2.4 动态作用域

JavaScript中很多伙伴对 this的指向时而清楚时而模糊,其实结合作用域会对 this有一个清晰的理解。不妨先来看下这段代码:

window.a = 3

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

test.bind({
    a: 2
})() // 2
test() // 3

在这里 bind已经把作用域的范围进行了修改指向了 { a: 2 },而 this指向的是当前作用域对象,是不是可以清楚的理解了呢?

接下来我们再思考另一个问题:作用域是在代码编写的时候就已经决定了呢,还是在代码执行的过程中才决定的?

var course = " es"

// 此处可调用 course 变量
function myFunction() {
    // 函数内可调用 course 变量
}

在看看这段代码,写代码的时候就知道 course就是全局作用域,函数内部的用 var定义的变量就是函数作用域。这个也就是专业术语:词法作用域。 通俗的讲变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。 相反,只能在执行阶段才能决定变量的作用域,那就是动态作用域


3. 新的声明方式:let

  • 不属于顶层对象window
  • 不允许重复声明
  • 不存在变量提升
  • 暂时性死区
  • 块级作用域

3.1 代码演示

首先安装xiaodi写的es脚手架 =>

npm i xd-es-cli -g

xd-es-cli init

image-20201118135316584

npm i

image-20201118143002947

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.40
Branch: branch02

commit description:a0.40(es脚手架搭建环境)

tag:a0.40


var和不写var两者作用是不一样的,用var声明一个变量,相当于在当前作用域内声明了一个变量。

不写var,实际是对属性进行赋值 => 相当于在window对象上定义了一个全局属性b

这就意味着,你不可以通过window. 变量名 的方式访问这些变量,而 var声明的全局变量是 window的属性,是可以通过 window. 变量名 的方式访问的。

es-demo\static\1-1.js

// var -> variable
var a = 5
console.log(a)

b = 6
console.log(b)

es-demo\src\index.html

     <script src="static/1-1.js"></script>

image-20201118153445726

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.41
Branch: branch02

commit description:a0.41(var的特性)

tag:a0.41


delete关键字作用是删除某个对象下的属性,不能删除变量。

delete=> 看能否删除ab,就能分清楚谁是对象?谁是属性了?

// var -> variable
var a = 5
console.log(a)
delete a
console.log(a)

b = 6
console.log(b)
delete b
console.log(b)

报错了 => b没有定义。说明b已经被删掉了,所以是获取不到b的。但是删除了a,仍然还可以输出a,证明a才是变量,b是属性。

image-20201118153838509

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.42
Branch: branch02

commit description:a0.42(delete验证是否为属性或为变量)

tag:a0.42


// var -> variable
var a = 5
console.log(a)
console.log(window.a)

b = 6
console.log(b)
console.log(window.b)

我们发现都可以输出window下的ab属性,这是神马情况?

变量不是只是一个变量吗?并不应该在window下面啊? =>

js设计初期的败笔导致的,它把顶层对象的属性和全局变量进行了挂钩,这是js语言最大败笔设计之一。

如果定义了很多全局变量都会挂在window下,这就污染了全局变量。 => let解决这个问题

image-20201118154427536

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.43
Branch: branch02

commit description:a0.43(js的设计败笔之一——污染全局变量)

tag:a0.43


let a = 5
console.log(a)
console.log(window.a)

let弥补了var定义的全局变量与顶层对象挂钩的问题。

即 let定义的变量 => 不属于顶层对象window

image-20201118214029990

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.44
Branch: branch02

commit description:a0.44(let 弥补了var定义的全局变量与顶层对象挂钩的问题)

tag:a0.44


var定义的变量是允许变量名相同的 => 后定义的变量会覆盖掉之前定义的变量

var a = 5
var a = 6
console.log(a)

image-20201118214652026

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.45
Branch: branch02

commit description:a0.45(var 定义的变量是允许变量名相同的)

tag:a0.45


let => 不允许重复声明 => 会报错

let a = 5
let a = 6
console.log(a)

image-20201118215054019

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.46
Branch: branch02

commit description:a0.46(let => 不允许重复声明 )

tag:a0.46


var => 变量提升

console.log(a)
var a = 5

在这里插入图片描述

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.47
Branch: branch02

commit description:a0.47(var => 变量提升)

tag:a0.47


打印一个没有定义变量,实际应该报错的

console.log(a)

image-20201118215732214

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.48
Branch: branch02

commit description:a0.48(打印一个没有定义变量,实际应该报错的)

tag:a0.48


var => 变量提升

以上代码相当于 =>

var a
console.log(a)
a = 5

image-20201118215454570

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.49
Branch: branch02

commit description:a0.49(var => 变量提升原理)

tag:a0.49


let => 不存在变量提升

console.log(a)
let a = 5

报错 => 没有声明的变量。

image-20201118220350678

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.50
Branch: branch02

commit description:a0.50(let => 不存在变量提升)

tag:a0.50


var=> 不存在暂时性死区

var a = 5
if (true) {
    a = 6
    var a
}

没报任何错

image-20201118221525993

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.51
Branch: branch02

commit description:a0.51(var => 不存在暂时性死区)

tag:a0.51


let => 暂时性死区

var a = 5
if (true) {
    a = 6
    let a
}

if后的大括号实际是一个封闭作用域,在这个封闭作用域内凡是在声明之前使用这个变量,则会报错。

上面代码中,存在全局变量 a ,但是块级作用域内 let 又声明了一个局部变量 a ,导致后者绑定这个块级作用域,所以在let声明变量前,对 a 赋值会报错。

ES6 明确规定,如果区块中存在 letconst 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

image-20201118221813116

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.52
Branch: branch02

commit description:a0.52(let => 暂时性死区)

tag:a0.52


但是有些暂时性死区是比较隐蔽的

function foo(a = b, b = 2){
    console.log(a, b)
}
foo()

等号实际就是赋值语句,首先b赋值给a,此时b没有值,代码是从左往右执行的。

image-20201118222450483

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.53
Branch: branch02

commit description:a0.53(有些暂时性死区是比较隐蔽的)

tag:a0.53


function foo(a = 2, b = a){
    console.log(a, b)
}
foo()

如果a已经有值,赋给b就不报错了。

image-20201118222746096

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.54
Branch: branch02

commit description:a0.54(如果a已经有值,覆盖b就不报错了)

tag:a0.54


var => 不存在块级作用域,在es5中只有全局作用域和函数作用域

for (var i = 0; i < 3; i++) {
    console.log('循环内:' + i)
}
console.log('循环外:' + i)

因为var定义的变量并没有块级作用域,因此在循环外依然可以获取到i的值。实际上这种规则对于变量非常不安全,本来就只是想在括号内使用,结果外部也可使用。

image-20201118231111401

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.55
Branch: branch02

commit description:a0.55(var => 不存在块级作用域)

tag:a0.55


let => 存在块级作用域

for (let i = 0; i < 3; i++) {
    console.log('循环内:' + i)
}
console.log('循环外:' + i)

image-20201118231741030

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.56
Branch: branch02

commit description:a0.56(let => 存在块级作用域)

tag:a0.56


再看一个块级作用域例子。

if (false) {
    var a = 5
}
console.log(a)

image-20201118232843530

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.57
Branch: branch02

commit description:a0.57(var => 块级作用域例子)

tag:a0.57


if (false) {
    let a = 5
}
console.log(a)

image-20201118233121737

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.58
Branch: branch02

commit description:a0.58(let => 块级作用域例子)

tag:a0.58


if (true) var a =5;

不报错。

image-20201118234012852

if (true) let a =5;

let报错

image-20201118234110484

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.59
Branch: branch02

commit description:a0.59(let => if块级作用报错)

tag:a0.59


if (true) {
    let a = 5
}

不报错了,因此es6要求if必须带大括号,否则报错。

image-20201118234012852

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.60
Branch: branch02

commit description:a0.60(es6要求if必须带大括号)

tag:a0.60


3.2 笔试题

for (var i = 0; i < 3; i++) {
    setTimeout(function(){
        console.log(i)
    })
}

三个3,因为异步原因

image-20201119000902774

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.61
Branch: branch02

commit description:a0.61(异步笔试题)

tag:a0.61


以上希望得到0、1、2的输出 => 闭包

有一个外部函数,并且有一个内部函数,内部函数会调用外部函数的变量,这样就可以保证外部函数的变量不被释放。

for (var i = 0; i < 3; i++) {
    (function(j){
        setTimeout(function(){
            console.log(j)
        })
    })(i)
}

image-20201119001233158

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.62
Branch: branch02

commit description:a0.62(异步笔试题 => 正常输出利用闭包)

tag:a0.62


以上也可不利用闭包,利用let解决 =>

for (let i = 0; i < 3; i++) {
    setTimeout(function () {
        console.log(i)
    })
}

image-20201119001608187

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.63
Branch: branch02

commit description:a0.63(异步笔试题 => 正常输出利用let)

tag:a0.63


利用babel转译看看 => 还是用闭包解决的

image-20201119001922283

"use strict";

var _loop = function _loop(i) {
  setTimeout(function () {
    console.log(i);
  });
};

for (var i = 0; i < 3; i++) {
  _loop(i);
}

4. 新的声明方式const (常量)

  • 不属于顶层对象window
  • 不允许重复声明
  • 不存在变量提升
  • 暂时性死区
  • 块级作用域

4.1 代码演示

es5定义常量方法

Object.defineProperty =>

第一个参数是在哪个对象上添加属性

第二个参数是属性名称

第三个参数是对象,给其赋值 => writable是否为可写属性

es-demo\static\1-2.js

Object.defineProperty(window, 'PI', {
    value: 3.14,
    writable: false
})
console.log(PI)
PI = 5
console.log(PI)

设置PI不可写,因此再赋值为5也不会生效,但是不会报错。

image-20201119110917740

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.64
Branch: branch02

commit description:a0.64(es5定义常量方法)

tag:a0.64


es6定义常量 =>

const a = 5
a = 6

给常量赋值会报错。

image-20201119111404003

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.65
Branch: branch02

commit description:a0.65(es6定义常量)

tag:a0.65


const a
a = 5

const声明的时候同时赋值,不能先声明后赋值。

image-20201119112153513

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.66
Branch: branch02

commit description:a0.66(const 声明的时候同时赋值,不能先声明后赋值。)

tag:a0.66


const其他特性与let是一样的。

如:块级作用域

if (true) {
    const a = 5
}
console.log(a)

image-20201119114945936

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.67
Branch: branch02

commit description:a0.67(const 块级作用域)

tag:a0.67


const变量也不存在变量提升,存在暂时性死区

if(true){
    console.log(a)
    const a = 5
}

image-20201119115150985

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.68
Branch: branch02

commit description:a0.68(const 变量也不存在变量提升,存在暂时性死区)

tag:a0.68


前面定义的都是基本数据类型,现在定义一些复杂数据类型

const obj = {
    name: 'xd',
    age: 28
}
console.log(obj)
obj.school = 'QH'
console.log(obj)

之前学过const是常量,是不可以被改变的,这里怎么发生变化了呢?

image-20201119121907424

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.69
Branch: branch02

commit description:a0.69(const 复杂类型数据)

tag:a0.69


=> js变量存储

基本数据类型存储在栈内存中,复杂(引用)数据类型(数组、对象)存储在堆内存

image-20210113005140153

针对引用类型 => 在栈内存存的是堆内存地址。

image-20210113005220025

const=> 对于数组,也可以改变其值

const arr = [1, 2, 3]
arr.push(4)
console.log(arr)

image-20201119122427702

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.70
Branch: branch02

commit description:a0.70(const => 对于数组,也可以改变其值)

tag:a0.70


实际是对应const的引用类型变量,不能改变的是栈内存的堆内存地址,堆里的内容是不管的。

const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

那如何让对象内容也为常量不变呢? => freeze函数,注意freeze里只能传对象不能传数组

const obj = {
    name: 'xd',
    age: 28
}
Object.freeze(obj)
console.log(obj)
obj.school = 'QH'
console.log(obj)

image-20201119123042713

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.71
Branch: branch02

commit description:a0.71(const => 对于数组,也可以改变其值)

tag:a0.71


嵌套对象 => Object.freeze浅层冻结,只会对最近一层的对象进行冻结,并不会对深层对象冻结。

const obj = {
    name: 'xd',
    age: 28,
    skill: {
        name: 'code',
        year: 5
    }
}
Object.freeze(obj)
console.log(obj)
obj.school = 'QH'
obj.skill.year = 10
console.log(obj)

嵌套对象的值可以被改变。

image-20201119124854063

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.72
Branch: branch02

commit description:a0.72(Object.freeze 浅层冻结)

tag:a0.72


如果想把skill中值也冻结,还需单独冻结skill。 => 相当于根据嵌套进行递归冻结

const obj = {
    name: 'xd',
    age: 28,
    skill: {
        name: 'code',
        year: 5
    }
}
Object.freeze(obj)
Object.freeze(obj.skill)
console.log(obj)
obj.school = 'QH'
obj.skill.year = 10
console.log(obj)

image-20201119125119271

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.73
Branch: branch02

commit description:a0.73(如果想把skill中值也冻结,还需单独冻结skill)

tag:a0.73


5. 解构赋值

  • 按照一定模式,从数组和对象中提取值,对变量进行赋值
  • 数组解构
  • 对象解构
  • 字符串解构
  • 应用

5.1 代码演示

现在的语法为了兼容各个浏览器,将其写在src目录下,利用webpack进行打包编译。

es-demo\src\index.js

import './1-3'

原始的取数组值的方法 => 挨个索引取,可以用循环遍历即可

es-demo\src\1-3.js

let arr = [1, 2, 3]
let a = arr[0]
let b = arr[1]
let c = arr[2]
console.log(a, b, c)

image-20201119125916751

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.74
Branch: branch02

commit description:a0.74(原始的取数组值)

tag:a0.74


解构取数组值的方法

等号左边形式要与右边匹配。

let arr = [1, 2, 3]
let [a, b, c] = [1, 2, 3]
console.log(a, b, c)

image-20201119130201567

解构赋值重点是在赋值,赋值的元素是要拷贝出来赋值给变量,赋值的元素本身是不会被改变的。

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.75
Branch: branch02

commit description:a0.75(解构取数组值的方法 )

tag:a0.75

赋值元素可以是任意可遍历的对象

赋值的元素不仅是数组,它可以是任意可遍历的对象

let [a, b, c] = "abc" // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3])

左边的变量

被赋值的变量还可以是对象的属性,不局限于单纯的变量。

let user = {}
[user.firstName, user.secondName] = 'Kobe Bryant'.split(' ')

console.log(user.firstName, user.secondName) // Kobe Bryant

循环体

解构赋值在循环体中的应用,可以配合 entries 使用。

let user = {
  name: 'John',
  age: 30
}

// loop over keys-and-values
for (let [key, value] of Object.entries(user)) {
  console.log(`${key}:${value}`) // name:John, then age:30
}

当然,对于 map 对象依然适用

let user = new Map()
user.set('name', 'John')
user.set('age', '30')

for (let [key, value] of user.entries()) {
  console.log(`${key}:${value}`) // name:John, then age:30
}

可以跳过赋值元素

如果想忽略数组的某个元素对变量进行赋值,可以使用逗号来处理。

// second element is not needed
let [name, , title] = ['John', 'Jim', 'Sun', 'Moon']

console.log( title ) // Sun

rest 参数

let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]

console.log(name1) // Julius
console.log(name2) // Caesar

// Note that type of `rest` is Array.
console.log(rest[0]) // Consul
console.log(rest[1]) // of the Roman Republic
console.log(rest.length) // 2

注意

我们可以使用 rest 来接受赋值数组的剩余元素,不过要确保这个 rest 参数是放在被赋值变量的最后一个位置上。

默认值

如果数组的内容少于变量的个数,并不会报错,没有分配到内容的变量会是 undefined。

let [firstName, surname] = []

console.log(firstName) // undefined
console.log(surname) // undefined

当然你也可以给变量赋予默认值,防止 undefined 的情况出现:

// default values
let [name = "Guest", surname = "Anonymous"] = ["Julius"]

console.log(name)    // Julius (from array)
console.log(surname) // Anonymous (default used)

复杂一些的数组解构,只有等号左边结构匹配即可。实际解构赋值就是模式的匹配。

let [a, b, [c, d]] = [1, 2, [3, 4]]
console.log(a, b, c, d)

image-20201119130324039

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.76
Branch: branch02

commit description:a0.76(复杂一些的数组解构,只有等号左边结构匹配即可。 )

tag:a0.76


5.2 笔试题1

let [a, b, [c]] = [1, 2, [3, 4]]
console.log(a, b, c)

c对应的是3,而4并没有对应值,因为等号左边[c]只有一项,因此只能与3对应。

image-20201119130608574

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.77
Branch: branch02

commit description:a0.77(解构赋值-笔试题1 )

tag:a0.77


5.3 笔试题2

let [a, b, c] = [1, 2, [3, 4]]
console.log(a, b, c)

c没有中括号,因此根据模式匹配对应[3, 4]

image-20201119130835288

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.78
Branch: branch02

commit description:a0.78(解构赋值-笔试题2 )

tag:a0.78


5.4 笔试题3

let [a, b, c, d] = [1, 2, [3, 4]]
console.log(a, b, c, d)

等号左边没有对应的模式匹配则为undefined

image-20201119131240396

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.79
Branch: branch02

commit description:a0.79(解构赋值-笔试题3)

tag:a0.79


5.5 笔试题4

let [a, b, c, d = 5] = [1, 2, [3, 4]]
console.log(a, b, c, d)

等号左边的结构可以直接赋值。

解构赋值实际是惰性的,如果等号右边模式对应有值,则取它,否则就根据左边的默认值,没有默认值就是undefined

image-20201119131448662

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.80
Branch: branch02

commit description:a0.80(解构赋值-笔试题4)

tag:a0.80


5.6 笔试题5

let [a, b, c, d = 5] = [1, 2, [3, 4], 6]
console.log(a, b, c, d)

解构 => 惰性取值,右边有值,则直接使用,会忽略默认值。

image-20201119131127887

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a.081
Branch: branch02

commit description:a0.81(解构赋值-笔试题5)

tag:a0.81



对象的普通取值。

解构赋值除了可以应用在 Array,也可以应用在 Object。基本的语法如下:

let user = {
    name: '6xd',
    age: 28
}
let name = user.name
let age = user.age
console.log(name, age);

image-20201119153044403

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.82
Branch: branch02

commit description:a0.82(对象的普通取值)

tag:a0.82


对象结构赋值 =>

大致的意思是我们有一个 Object想把里面的属性分别拿出来而无需通过调用属性的方式赋值给指定的变量。具体的做法是在赋值的左侧声明一个和 Object结构等同的模板,然后把关心属性的 value指定为新的变量即可。

let user = {
    name: '6xd',
    age: 28
}
let {age, name} = user;
console.log(name, age);

因为对象中的key是唯一的,所以对象解构的,是通过key对应的,而不是顺序。而数组解构则是通过顺序进行对应的。

image-20201119153417095

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.83
Branch: branch02

commit description:a0.83(对象结构赋值)

tag:a0.83


比如对象解构完成后,需要赋值给别的变量,如何实现呢?

let user = {
    name: '6xd',
    age: 28
}
let {age: uage, name: uname} = user
console.log(uname, uage)

image-20201119153837565

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.84
Branch: branch02

commit description:a0.84(对象结构赋值2)

tag:a0.84


let user = {
    name: '6xd',
    age: 28
}
let {age: uage, name: uname} = user
uname = 'xm'
uage = 99
console.log(uname, uage)
let {age, name} = user;
console.log(name, age)

对象解构赋值,相当于赋值给新变量,修改新变量的值不会改变原对象的属性值。

image-20201119154114421

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.85
Branch: branch02

commit description:a0.85(对象解构赋值,相当于赋值给新变量)

tag:a0.85

默认值

当然,这个赋值的过程中也是可以指定默认值的,这样做:

let options = {
  title: "Menu"
}

let {width = 100, height = 200, title} = options

console.log(title)  // Menu
console.log(width)  // 100
console.log(height) // 200

rest 运算符

如果我们想象操作数组一样,只关心指定的属性,其他可以暂存到一个变量下,这就要用到 rest运算符了

let options = {
  title: "Menu",
  height: 200,
  width: 100
}

let {title, ...rest} = options

// now title="Menu", rest={height: 200, width: 100}
console.log(rest.height)  // 200
console.log(rest.width)   // 100

嵌套对象

如果一个 Array或者 Object比较复杂,它嵌套了 Array或者 Object,那只要被赋值的结构和右侧赋值的元素一致就好了。

let options = {
  size: {
    width: 100,
    height: 200
  },
  items: ["Cake", "Donut"],
  extra: true    // something extra that we will not destruct
}

// destructuring assignment on multiple lines for clarity
let {
  size: { // put size here
    width,
    height
  },
  items: [item1, item2], // assign items here
  title = 'Menu' // not present in the object (default value is used)
} = options

console.log(title)  // Menu
console.log(width)  // 100
console.log(height) // 200
console.log(item1)  // Cake
console.log(item2)  // Donut

这个原理其实很简单,如果不理解可以看下图:


实际上,解构赋值在项目中用得非常多,如想从后端请求数据,赋值给前端渲染。

es5=> 取字符串中单个字符

let str = 'Github'
for (let i = 0; i < str.length; i++) {
    console.log(str[i])
}

image-20201119154434809

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.86
Branch: branch02

commit description:a0.86(es5 => 取字符串中单个字符)

tag:a0.86


字符串解构赋值 => 实际与数组的解构差不多

let str = 'Github'
let [a, b, c, d, e, f] = str
console.log(a, b, c, d, e, f)

image-20201119155621015

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.87
Branch: branch02

commit description:a0.87(字符串解构赋值)

tag:a0.87


5.7 应用场景1 ——赋默认值

let [a, b, c = 8] = [4, 5, 6]
console.log(a, b, c)

image-20201119160122187

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.88
Branch: branch02

commit description:a0.88(解构赋值应用场景1 ——数组解构赋默认值)

tag:a0.88


let {name, age = 18} = {
    name: '6xiaodi',
    age: 28
}
console.log(name, age)

image-20201119183543842

let {name, age = 18} = {
    name: "6xiaodi",
    // age: 28
}
console.log(name, age)

image-20201119183608493

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.89
Branch: branch02

commit description:a0.89(解构赋值应用场景1 ——对象解构赋默认值)

tag:a0.89


解构 => 遵循惰性赋值规则,等号右边有则忽视左边默认值。

function foo(){
    console.log(123)
}
let [a = foo()] = [1]

image-20201119183918495

function foo(){
    console.log(123)
}
let [a = foo()] = []

右边是空数组,左边默认值是个方法,自动执行

image-20201119184003412

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.90
Branch: branch02

commit description:a0.90(解构赋值应用场景1 ——解构惰性赋值)

tag:a0.90


5.8 应用场景2——函数参数解构赋值

函数数组参数解构赋值 =>

function foo([a, b, c]) {
    console.log(a, b, c)
}
let arr = [1, 2, 3]
foo(arr)

这样就省去了用数组参数接收实参,再解构取。

image-20201119195954418

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.91
Branch: branch02

commit description:a0.91(解构赋值应用场景2——函数数组参数解构赋值)

tag:a0.91


函数对象参数解构赋值 => 解构同时还可提供默认值

function foo({name, age, school = 'beida'}) {
    console.log(name, age, school)
}
let obj = {
    name: 'lisi',
    age: 34,
    school: 'qinghua'
}
foo(obj)

image-20201119200435055

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.92
Branch: branch02

commit description:a0.92(解构赋值应用场景2——函数对象参数解构赋值 => 解构同时还可提供默认值)

tag:a0.92


5.9 应用场景3——函数返回值解构赋值

函数返回值解构 =>

function foo() {
    let obj = {
    name: 'lisi',
    age: 34,
    school: 'qinghua'
    }
    return obj
}
let {name, age} = foo()
console.log(name, age)

image-20201119200739456

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.93
Branch: branch02

commit description:a0.93(解构赋值应用场景3——函数返回值解构赋值)

tag:a0.93


5.10 应用场景4——提取JSON数据

一般前端请求接口,后端会返回JSON字符串。 => 前端一般会先转为JSON对象再决心解构。

let json = '{"a": "hello", "b": "world"}'
let {a, b}= JSON.parse(json)
console.log(a, b)

image-20201119215903110

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a0.94
Branch: branch02

commit description:a0.94(解构赋值应用场景4——提取JSON数据)

tag:a0.94




(后续待补充)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值