拉勾大前端的学习笔记,仅作为学习记录
概述
ECMAScript是JavaScript的语言规范,ES只是提供了最基本的语法,是语言层面的东西。
ES和JS之间的关系
- 在浏览器环境中的JavaScrip包括ES还有浏览器提供的BOM和DOM
- 在Node环境中的JavaScript包括ES和node提供的api,例如fs,net,etc.
JavaScript中语言本身值得就是ECMAScript
ES2015的一些变化
- 解决原有语法上的不足,例如const,let等
- 原有语法的增强,例如解构,展开,参数默认值,模板字符串等
- 全新的对象,全新的方法和功能,例如Promise,Proxy
- 全新的数据类型和数据结构,例如Symbol,Set,Map
Let与块级作用域
- 声明for循环嵌套的计数器
- 闭包也是利用函数作用域摆脱全局作用域的影响
- let声明不会有变量提升
Const
- 可以生成一个只读的常量
- 变量生成后就不能去修改
- 声明的同时就必须赋值
- 注意:const成员不能被修改,只是不允许声明后重新指向新的内存地址,并不是不允许修改恒量中的属性成员
const obj = {}
obj.name = 'hello'
// 以上情况是被允许的,只是修改了内存变量中的值,并没有修改内存指向
数组解构
- 根据位置去提取
// 获取数组每一项
const arr = [100,200,300]
const [foo,bar,baz] = arr
// 获取数组的第三项,并赋值给baz
const [,,baz] = arr
//...提取当前位置往后的所有成员
const [foo,...rest] = arr
//提取成员设置默认值
const[foo="default value",...rest] = arr
对象解构
- 根据属性名去提取
const obj = { name: 'poppy',age:18 }
// 提取obj中name放在name变量中
const { name } = obj
// 重命名并且加默认值
const {name: objName = ”default value“ } = obj
模板字符串
- 支持多行字符串
- 支持插值表达式${name}的方式嵌入数值
const name = 'yaping'
const str = `hello,${name}`
带标签的模板字符串
const name = 'tom'
const gender = true
function myTagFunc = (strings,name,gender){
const sex = gender ? 'man' : 'woman'
return strings[0] + name + strings[1] + sex + strings[2]
}
const result = myFunc(`hey,${name} is a ${gendar}`)
console.log(result)
// hey, tom is a man
字符串的拓展方法
- includes // 判断字符串中是否包含某个值
- startsWith // 判断字符串中是否以某个值开始
- endsWith //判断字符串是否以某个值结束
const message = 'Error:foo is not defined.'
console.log(startsWith('Error')) // true
参数默认值
function foo(enable = true){
console.log(enable)
}
剩余参数
- 出现在形参的最后一位,只可以使用一次
function foo(...args){
console.log(args) // [1,2,3,4]
}
foo(1,2,3,4)
...操作符的用处
const arr = [1,2,3,4]
// 展开数组
console.log(...arr) // 1 2 3 4
箭头函数
- 语法
// 语法
const inc = n => n +1
// 其实为
function inc(n){
return n + 1
}
- this指向
const person = {
name: 'tom'
sayHi: function (){
console.log(`hi, my name is ${this.name}`) // hi, my name is tom
}
sayHello: () =>console.log(`hello, my name is ${this.name}`) // hello, my name is undefined
}
对象字面量的增强
- 如果对象属性名和变量名相同,可以直接简写成变量名
const bar = 123
const obj = {
foo: '123',
bar, // 等同于 bar: bar
}
- 添加属性方法可以直接省略function
const bar = 123
const obj = {
method(){
console.log('hello')
},
// 等同于
method : function (){
console.log('hello')
}
}
- 可以用表达式的返回值作为属性值
const bar = 123
const obj = {
[Math.random()] : 123 // 这种使用方法称之为 计算属性名
}
Object拓展方法
- Object.assign(target, source1, source2) 将多个源对象中的属性复制到一个目标对象中
- Object.is() 判断两个值是否相等
解决 +0 === -0 为true
解决 NaN === NaN 为false
Proxy代理对象
- 参数1 :需要代理的对象
- 参数2 :代理的处理对象
const person = {
name: 'zce'
age: 20
}
const personProxy = new Proxy(person, {
get(target, property){
return property in target ? target[property] : 'default'
},
set(target, property, value){
if(property === 'age'){
if(!Number,isInteger(value)){
throw new TypeError(`${value} is not a init`)
}
target[property] = value
}
}
})
Proxy和Object.defineProperty()对比
- defineProperty只能监听对象的读取或写入
- Proxy能监听到更多的对象操作,例如delete操作和对于对象中方法的操作等等
const person = {
name: 'zce',
age: 10
}
const personProxy = new Proxy(person, {
deleteProperty(target, property){
console.log('delete', property)
delete target[property]
}
})
delete personProxy.age
console.log(person)
// 打印结果为
// delete age
// { name : 'zce' }
- Proxy更好的支持对数组对象的操作
const list = []
const listProxy = new Proxy(list, {
set(target, property, value) {
console.log('set', property, value)
target[property] = value
return true // 表示设置成功
}
})
listProxy.push(100)
- Proxy 是以非侵入的方式监管对象的读写,也就是定义好的对象,不需要对对象本身进行操作,就可以监听对象的读写
Reflect 统一的对象操作API
- 一个静态类,只能去调用静态类中的静态方法
- Reflect内部封装了一系列对对象的底层操作 一共是14个,其中有一个被废弃了,还剩13个
- Reflect成员方法就是Proxy处理对象的默认实现
const obj = {
foo: 123
}
const proxy = new Proxy(obj,{
// 当我们没有定义get方法的时候,一下代码为默认get方法
get (target,property){
return Reflect.get(target,property)
}
})
- Reflect提供了统一的一套用于操作对象的API
console.log('name' in obj)
console.log(delete name)
console.log(Object.keys(obj))
// 以上的对象操作和下面的操作结果相同
Reflect.has(obj,'name')
Reflect.deleteProperty(obj,'age')
Reflect.ownKeys(obj)
Promise
主要解决回调地狱问题,具体内容可以参考之前写的文章 异步编程 ,手写Promise
Class类
- class之前想构建一个类,要声明一个函数作为整个类的构造函数,通过property为构造函数加共享方法
function Person(name){
this.name = name
}
Person.property.say = function () {
console.log(`hi,my name is ${this.name}`)
}
- 利用class的语法来创建构造函数
class Person {
// 当前类型的构造函数
constructor(name){
this.name = name
}
// 为类型定义实例方法
say(){
console.log(`hi,my name is ${this.name}`)
}
static create(name){
return new Person(name)
}
}
const p = new Person('tom')
const tom = Person.create('tom')
tom.say()
- 类型中的方法
实例方法
需要通过类型构造的实例对象去调用
静态方法
直接通过类型本身去调用, 创建的静态方法关键词 static- 因为静态方法挂载到类型本身的,所以他的this就不会指向某个实例对象,而是指向当前的类型
类的继承(extends)---面向对象重要特性
class Student extends Person{
constructor(name,number){
super(name) //super始终指向父类,调用它就是调用父类的构造函数
this.number = number
}
hello (){
super.say()
console.log(`my school number is ${this.number}`)
}
}
const s = new Student('jack',10)
s.hello()
Set数据结构
- 理解为集合,与传统数组类似,Set成员是不允许重复的
- 是一个类型,通过类型构造的实例可以用来存放不同的数据
- 利用实例的add方法往集合中添加数据,由于add对象会返回集合对象本身,所以add可以链式调用
- 如果添加了已经存在的值,所添加的值就会被忽略掉
const s = new Set()
s.add(1).add(2).add(3)
s.size // 获取集合的长度和length类似
s.has() // 判断集合中是否存在某个指定的值
s.delete() // 删除集合中指定的值,删除成功返回true
s.clear() // 清空集合中的元素
- Set集合常用在数组去重
const arr = [1,2,3,3,4,5]
const result = Array.form(new Set(arr))
const result = [...new Set(arr)]
Map数据结构
- 与对象类似,本质上都是键值对集合
- 对象的键值只能是字符串
const obj = {}
obj[true] = 'val'
obj[123] = 123
obj[{a:1}] = 999
console.log(Object.keys(obj))
// ['123', 'true', '[Object Object]']
// obj中的键都被转化为了字符串
- Map可以用任意类型的数据作为键
const m = new Map()
const tom = { name: 'tom' }
m.set(tom,90)
console.log(m) // Map{ {name:"tom"} => 90 }
console.log(m.get(tom)) // 90
// Map内部还有些方法供于操作对象
m.has() // 判断集合中是否存在某个指定的值
m.delete()
m.clear()
Symbol 一种全新的原始数据类型
es2015之前,对象的键值都是字符串,全局的对象添加属性容易出现明明冲突
// shard.js=============
const cache = {}
// a.js=================
cache['foo'] = Math.random()
// b.js
chche['foo'] = 123
console.log(chche) // { foo : 123 }
- Symbal的特点就是独一无二,我们通过Symbol创建的每一值都是唯一的
const s = Symbol()
console.log( typeof s) // Symbol
console.log(Symbol() === Symbol()) // false
- ES2015之后可以使用Symbol作为属性值
- 除了解决对象属性名冲突还可以借助此特性模拟对象的私有成员
- 如果想得到相同的Symbo,需要借助Symbol内部的for
const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true
// 需要注意一点
Symbol.for(true) === Symbol.for(true)
- 使用Symbol作为对象的属性值,这种属性值通过传统的for in 是无法拿到的,Object.keys()也获取不到,JSON.stringfy 也拿不到
- 需要通过Object.getOwnPropertySymbols(obj)获取全是Symbol的属性名
for...of循环
最基本的遍历有几种方式
- 基本的for循环,遍历普通的数组
- for...in 遍历键值对
- for...of是可以遍历所有数据结构的统一方式
for...of 可以拿到数组的每一个元素,可以用break终止
Set 和 Map对象进行for...of 循环
// for...of遍历Map的时候
const m = new Map()
m.set('foo',123)
m.set('bar',456)
for(const item of m){
console.log(item) // 返回一个数组,分别是键和值['foo',123]
}
for...of遍历普通的对象
const obj = {foo:123,bar:456}
for(const item of obj){
console.log(item)
}
// 会报错显示 obj is not iterable
// ES中能够表示有结构的数据类型越来越多,为了给各种数据结构提供一种统一的遍历方式,ES2015提出了Iterable接口,意为可迭代的
// 实现Iterable接口就是for...of的前提、
// 之前for...of可遍历的接口,都是在内部实现了Iterable接口
// Iterable规定我们遍历的对象内部必须要挂载iterator方法
总结
所有可以被for...of循环的对象,都必须要去实现叫Iterable的接口,也就是内部就必须挂载iterator方法,这个方法必须要返回带有next的方法的对象,不断调用next就能实现对内部的遍历
实现Iterable接口
const obj = {
store: ['foo','bar','baz']
[Symbol.interator]: function(){
const index = 0
const self = this
return {
next: function(){
const result = {
value: self.store[index],
done: index >= this.store[index].length
}
index++
return result
}
}
}
}
迭代器设计模式
- 场景:ab协同开发一个任务清单应用
//a的代码=========== 设计用于存放所有任务的对象
const todo = {
life: {'吃饭','睡觉','打豆豆'},
learn: {'语文','数学','科学'}
[Symbol.iterator]: function (){
const all = [...this.life,...this.learn]
let index = 0
return {
next: function (){
value: all[index],
done: index++ >= all.length
}
}
}
}
//b的代码=========== 把a设计的对象中的任务罗列到界面上
for(item of todos){
console.log(item)
}
Generator的使用案例
- 创建一个发号器
function createIdMaker(){
let id = 1
while(true){
yield index++
}
}
const idMaker = createIdMaker()
console.log(idMaker.next())
- 实现iterator方法
const todo = {
life: {'吃饭','睡觉','打豆豆'},
learn: {'语文','数学','科学'}
[Symbol.iterator]: function * (){
const all = [...this.life,...this.learn]
return {
for(const item of all){
yield item
}
}
}
}
- 最重要的还是解决js中回调嵌套过深的问题,可以看之前的文 异步编程
ES Modules 语言层面的模块化标准
- 在模块化开发的课程中详细介绍
ES2016
仅包含两个小功能
- 数组实例对象的includs方法,检查数组中是否包含某个元素
- 指数运算符
// js
console.log(Math.pow(2,10))
// es
console.log(2 ** 10)
ES201
- Object.values
- Object.entries 以数组的形式返回对象中所有的键值对
// 通过object.entries 让for...of遍历对象
for(const [key, value] of Object.entries(obj)){
console.log(key,value)
}
//将普通对象转化为Map对象
new Map(Object.entries(obj))
- Object.getOwnPropertyDescriptors 获取对象中属性的完整描述信息
配合get,set去使用,因为Object.assign不会复制对象中get,set属性
const p1 = {
firstName: 'Lei',
lastName: 'Wang'
get fullNanme(){
return this.firstName + ' ' + this.lastName
}
}
const p2 = Object.assign({},p1)
p2.firstName = 'zce'
console.log(p2.fullName) // Lei Wang
// 解决办法
const descriptors = Object.getOwnPropertyDescriptors(p1)
const p2 = Object.defineProperties({},descriptors)
p2.firstName = 'zce'
console.log(p2.fullName) // zce Wang
- 新增了两个字符串填充方法 String.property.padStart / String.property.padEnd
用给定字符串去填充字符串的开始或结束位置,直到字符串达到指定长度为止
const str = '12'
str.padStart(3, '0') // 012