web高级课程-1 ES6标准入门-基础

201 阅读8分钟

1.1 let和const

上课时间:2021.09.26

课堂内容

标题varletconst
重复声明YNN
作用域函数级块级块级
变量提升声明-初始化-执行-赋值声明-执行-初始化-赋值同let

问题思考

let深入理解-变量提升和暂时性死区:let有变量提升吗

1.2 块级作用域

上课时间:2021.10.17

课堂内容

标题varletconst
重复声明YNN
作用域函数级块级块级
变量提升声明-初始化-执行-赋值声明-执行-初始化-赋值同let

1.3 解构赋值

上课时间:2021.10.19

课堂内容

可以结构赋值的对象: 对象 数组 字符串等 iterable 对象

1.4 展开运算符

上课时间:2021.10.19

课堂内容

...是一层深拷贝

扩展运算符的底层使用 for...of 实现的循环

1.5 Set 和 WeakSet

上课时间:2021.10.19

课堂内容

Set类似数组,但是成员的值都是唯一的,没有重复

Set是一个构造函数 用于生成Set数据结构

属性:size

方法:clear(),delete(),add(),has()

遍历操作:keys(), values(), entries(),forEach()

set的遍历是有序的,遍历顺序就是插入顺序。这个特性非常有用,比如用Set保存一个回调函数列表,调用时就可以保证按照顺序调用

Set结构的实例默认可遍历,这意味着可以省略keys和values方法,直接用for...of遍历Set

WeakSet与Set类似,但是有两个区别:1.WeakSet的成员只能是对象 2.WeakSet中的对象都是弱引用,不计入垃圾回收机制。因此不能用于遍历,适合临时存放对象和存放跟对象绑定的信息。

问题思考

Set内部如何判断两个值相等? 使用算法 Same-value equality,类似 === 运算符,与===的区别是NaN在此处等于自身。

1.6 Map 和 WeakMap

上课时间:2021.10.22

课堂内容

Map类似对象,也是键值对的集合。但是键的值不仅限于字符串。是一种更完善的Hash结构实现。

Map的键实际上是和内存地址绑定的,内存地址不一样就视为两个键,除了NaN

属性:size

方法:clear(),delete(),has(),set(),get()

遍历操作:keys(), values(), entries(),forEach()

与其他类型的转换

  • Map转数组 [...testMap.vlaues()] [...testMap.keys()]
  • 数组转Map new Map([testArray])
  • Map转对象 键都是字符串 循环转换
  • 对象转Map 循环转换
  • Map转为JSON 转为对象或数组再转json
  • JSON转为Map 转为对象或数组再转map

WeakMap与Map类似,但是有两个区别:1.WeakMap只接受对象作为键名 2.WeakMap中的键都是弱引用,不计入垃圾回收机制。因此不能用于遍历,它的键适合绑定将来有可能消失的对象,比如Dom节点。

1.7 函数新增内容

上课时间:2021.10.27

课堂内容

函数参数默认值

如果参数默认值是变量,则变量所处的作用域先是当前函数作用域,然后才是全局作用域 如果函数参数的默认值是个函数,那么参数的作用域是其声明时的作用域,如下🌰

var x=1;
function f(x, y=x) {
  console.log(y)
}
f(2) // 2

let foo = 'outer'
function bar(func = x => foo) {
    let foo = 'inner'
    console.log(func())
}
bar() // outer
// 匿名函数声明时 bar的作用域还没有形成 因此foo指向外部作用域

rest参数

Es6用 ...变量名 的方式替代 arguments 参数

扩展运算符 ...

扩展运算符调用的是数据结构的Iterator接口,有 Iterator接口的对象都可以用...变为真正的数组。如Set Map string等。如果没有Iterator接口,可以先调用Array.from转换为数组,再使用扩展运算符。

一些实际应用: 合并数组,将字符串转为数组等。

箭头函数

  • 箭头函数内部的this对象,就是定义时的this对象,不是使用时的this对象
  • 不可以当做构造函数
  • 不可以使用arguments对象
  • 不可以使用yield命令

函数绑定

ES7提出函数绑定 :: 用于替代call apply bind的调用

:: 左边是一个对象,右边是一个函数,该运算符自动将左边的对象,作为上下文环境(this)绑定到右边函数上面

foo::bar
// 等同于
::foo.bar
// 等同于
bar.bind(foo)
  • 由于::返回的还是原来的对象,因此可以链式调用

尾调用优化

尾调用是函数式编程的一个重要概念,是指某一个函数的 最后一步 是调用另一个函数

每个函数调用会在内存中形成一个调用帧,函数返回帧才会消失。调用帧组成一个调用栈(call stack)。在严格模式下,ES6使用尾调用,可以只保留最后一个调用帧,从而大大节省内存。不会发生栈溢出错误。

非严格模式下,可以自己实现尾调用优化:将递归改为循环,每次返回自己的另一个版本。

function trampoline(fn) {
    while(fn && fn instanceof Function) {
        fn = fn()
    }
    return fn
    
}
// fn符合递归条件 每次返回自己的另一个版本

问题思考

这些this的面试题,你都会嘛 7道关于this的面试题

1.8 数组新增方法

上课时间:2021.11.14

课堂内容

数组方法分为 Array构造函数原型上的方法和数组对象方法

Array构造函数上的方法

  • Aarry.from: 将类数组(有下标,有length)转化为数组 用法: Array Array.from(lis[, mapFn[, thisArg]])([]在手册中代表可选参数)
  • Array.of 将参数转为数组 用法:Array Array.of(element0[, element1[, ...[, elementN]])
  • Array.isArray

1.9 数组新增方法

上课时间:2021.11.14

数组对象方法

  • includes,startWith,endWith
  • find, findIndex

1.10 数组扁平化flat和includes

上课时间:2021.11.14

  • flat 用法:Array Array.flat([, number])

1.11 字符串新增方法

上课时间:2021.11.15

课堂内容

  • ES6为字符串新增了遍历器接口,字符串可以由for...of循环遍历
  • includes,startWith,endsWith 返回Boolean
  • repeat 返回新的字符串
  • padStart, padEnd
    字符串自动补全长度 String String.padStart(number[, padString]) padString默认为空格

1.12 模板字符串

上课时间:2021.11.15

课堂内容

${}格式表示字符串

  • 标签模板:紧跟在一个函数后面,该函数将被调用来处理这个模板字符串 如果模板字符串中有变量,模板字符串将被处理为多个参数,再调用。

标签模板的一个重要应用是过滤html字符串,防止用户输入恶意内容;另一个应用是多语言转换(国际化处理)

String.raw()

String.raw方法往往用于充当模板字符串的处理函数,它会将所有的变量都替换,并将反斜线都转义。

String.raw `hhh\n${2+3}` // 'hhh\\n5'

1.13 对象的新增方法

上课时间:2021.11.15

课堂内容

属性的简洁表示法

hello:function() {... === hello() {...

属性名表达式

ES6允许将表达式作为对象的属性名 obj['a'+'bc'] = 123;

方法的name属性

函数的name属性返回函数名,基础用法是 person.sayName.name = 'sayName'

如果对象的方法使用了取值函数get, 则name属性不在该方法上面,而是在该方法属性的描述对象的get属性上面

const obj = {
    get foo() {},
    set foo(x) {}
}

obj.foo.name // undefined
const des = Object.getOwnPropertyDescription(obj, 'foo')
des.get.name // 'get foo'

Object.is()

ES5比较二者是否相等: == 的缺点: 自动转换数据类型

=== 的缺点:NaN不等于NaN +0 === -0

Object.is是严格相等 NaN等于NaN, +0不等于-0

Object.assign()

将源对象所有的可枚举属性复制到目标对象,第一个参数是目标对象,其他都是源对象

只有一个参数:

  • 如果只有一个对象,则直接返回该参数
  • 如果上面的参数不是对象,会先转成对象再复制。不可转为对象则报错

如果非对象参数出现在源对象的位置,那么这些参数都会转为对象,如果是字符串,将被转为数组,其他不可转为对象的则跳过。

因为只有字符串的包装对象会产生可枚举属性,所以字符串会被Object.assign转为数组。

Object.assign是浅拷贝。如果源对象的某个属性的值是对象,那么目标对象复制的是这个对象的引用。

属性的可枚举性

描述对象的 enumerable 被称为“可枚举性”

以下方法会忽略enumerable为false的属性:

  • for...in 只遍历自身的和继承的可枚举属性
  • Object.keys 返回对象自身的可枚举属性
  • JSON.stringify 只格式化自身的可枚举属性
  • Object.assign 只复制自身的可枚举属性

ES6规定 所有Class原型的方法都是不可枚举的 使用 for...in 会引入继承的属性,让问题复杂化,因此应尽量用Object.keys代替for...in

属性的遍历

  • for...in 循环遍历自身继承可枚举属性
  • Object.keys [] 返回自身可枚举属性
  • Object.getOwnPropertyNames [] 返回自身的可枚举和不可枚举属性,不含Symbol
  • Object.getOwnPropertySymbols [] 返回自身的所有Symbol属性
  • Reflect.ownKeys [] 返回自身所有属性

proto, Object.setPrototypeOf(), Object.getPrototypeOf()

  • __proto__的值是对象原型
  • setPrototyeof作用与 __proto__相同,用来设置一个对象的prototype对象,ES6推荐使用
  • getPrototypeof 读取一个对象的prototype对象

Object.keys(), Object.values(), Object.entries()

分别返回属性,键值,键值对组成的数组

  • 都是返回自身可枚举的值
  • 顺序不可控

扩展运算符

  • 结构赋值是浅拷贝, 如果一个键的值是复合类型的值(数组,对象,函数),那么解构赋值的是这个值的引用,而不是这个值的副本。
  • 解构赋值不会复制继承来的对象属性

Object.getOwnPropertyDescriptors()

返回对象所有自身属性(非继承属性)的描述对象

主要是为了解决Object.assign不能正确复制 get和set的问题

const source = {
set foo(value) { console.log(value) }
}

const object1 = {}
const object2 = {}

Object.assign(object1, source)
Object.getOwnPropertyDescriptors(object1, 'foo')
// {value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }

Object.defineProperties(object2, Object.getOwnPropertyDescriptors(source))
Object.getOwnPropertyDescriptors(object2, 'foo')
// {getundefined,
// set: [Function: foo],
// enumerable: true,
// configurable: true }

Null传导运算符?.

a?.b 如果a是 null或者undefined 不产生任何效果

问题思考

怎么手动实现深拷贝?