首先要区分语言和平台之间的关系,语言本身是指ECMAScript,平台是指浏览器或者node,在平时我们浏览器开发里js就是ECMAScript。
浏览器的组成部分
node.js的组成部分
在ES5.1之后版本我们统称为ES6
主要说明如下:
**• let 和 const
• Arrow functions
• Classes(类)
• Generators
• Map 和 Set
• for ... of 循环
• Symbol
• Modules
• Template literals(模板字⾯量)
• Default parameters(默认参数)
• Enhanced object literals(对象字⾯量增强)
• Destructuring assignments(解构分配)
• Spread operator(展开操作符)
• Proxy和Reflect**
这些新增API和对象主要是进行:
**• 对原有语法进⾏增强
• 解决原有语法上的⼀些问题或者缺陷
• 全新的对象、全新的⽅法、全新的功能
• 全新的数据类型和数据结构**
let和const
let 声明的成员只会在所声明的块中生效
if (true) {
let foo = 'zce'
console.log(foo)
}
let经典应用场景
var elements = [{}, {}, {}]
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = function () {
console.log(i)
}
}
因为产生自己的循环体的块级作用域,所以可以代替闭包。
我们看下面例子来解析一下运行
for (let i = 0; i < 3; i++) {
let i = 'foo'
console.log(i)
}
如下:
let i = 0
if (i < 3) {
let i = 'foo'
console.log(i)
}
i++
if (i < 3) {
let i = 'foo'
console.log(i)
}
i++
if (i < 3) {
let i = 'foo'
console.log(i)
}
i++
其实是这样的,所以我们也就可以解释在这其中for 循环会产生两层作用域。
let 还修复了变量声明提升现象。
const 整体和let类似,
- 恒量声明过后不允许重新赋值
- 恒量要求声明同时赋值
const name
name = 'zce' //会报错
- 恒量只是要求内层指向不允许被修改
- 对于对象数据成员的修改是没有问题的
Arrow functions
箭头函数的 简易写法,可以去看下阮一峰老师的es6,这里主要标明几个箭头函数的特征。
- 箭头函数绑定父作用域的this
- 箭头函数this不可修改
- 箭头函数不能作为构造函数,不能使用new
- 箭头函数没有arguments, caller, callee
- 箭头函数没有原型属性//prototype 为undefined
- 箭头函数在ES6 class中声明的方法为实例方法,不是原型方法
class Super{
sayName =()=>{
//do some thing here
}
}
var a = new Super()
var b = new Super()
a.sayName === b.sayName //false
class
es6里实现class关键字,来代替es5里的构造函数的方式生成类。
es6的extends关键词来进行继承,底层是使用es5寄生组合式继承的封装。
使用extends来继承,constructor中使用this必须要先实例化父类,使用super关键词实例化,其他方法里也可通过super调用父类成员。
class里的constructor相当于es5的实例化,然后之外定义的属性函数,相当于定义在原型上(不包含箭头函数)
static关键词 静态属性,可以不实例化类可直接访问,里面的this指向类本身,不是实例化后的对象。
// extends 继承
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(`hi, my name is ${this.name}`)
}
}
class Student extends Person {
constructor (name, number) {
super(name) // 父类构造函数
this.number = number
}
hello () {
super.say() // 调用父类成员
console.log(`my school number is ${this.number}`)
}
}
const s = new Student('jack', '100')
s.hello()
Generators
Generators函数调用会返回一个iterator对象,可以继续调用next()方法来依次返回Generators函数yield关键词之后的表达式结果,结果是个对象{value:'结果值',done:布尔值(是否结束true/false)},yield可以暂停当前函数,然后再通过外面iterator调用next方法继续往下走。
代码如下:
function * createIdMaker () {
let id = 1
while (true) {
yield id++
}
}
const idMaker = createIdMaker()
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
Map和Set
Map
Map
对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map
可以接受一个数组作为参数。
map和object区别
- 一个
Object
的键只能是字符串或者Symbols
,但一个Map
的键可以是任意值。 Map
中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。Map
的键值对个数可以从 size 属性获取,而Object
的键值对个数只能手动计算。Map
支持for of 循环,obj需要自己添加Symbol.iterator
常用api
- set(key, val): 向Map中添加新元素
- get(key): 通过键值查找特定的数值并返回
- has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false
- delete(key): 通过键值从Map中移除对应的数据
- clear(): 将这个Map中的所有元素删除
- size:返回Map对象中所包含的键值对个数
遍历方法
keys()
:返回键名的遍历器values()
:返回键值的遍历器entries()
:返回键值对的遍历器forEach()
:使用回调函数遍历每个成员
map支持 for of 遍历,内部的Symbol.iterator是entries
Set
Set
对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
本身是一个构造函数,用来生成Set
数据结构。Set
函数可以接受一个数组(或者具有 iterable 接口(迭代器)的其他数据结构)作为参数,用来初始化(带有Symbol.iterator)。Set
对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:
- +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复
- undefined 与 undefined 是恒等的,所以不重复
- NaN 与 NaN 是不恒等的,但是在 Set 中认为NaN与NaN相等,所有只能存在一个,不重复。
set常用方法
add(value)
:添加某个值,返回 Set 结构本身(可以链式调用)。delete(value)
:删除某个值,删除成功返回true
,否则返回false
。has(value)
:返回一个布尔值,表示该值是否为Set的成员。clear()
:清除所有成员,没有返回值。- size:返回Set实例的成员总数。
遍历方法
keys()
:返回键名的遍历器。values()
:返回键值的遍历器。entries()
:返回键值对的遍历器。forEach()
:使用回调函数遍历每个成员。
由于Set
结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
for...of循环
for of 循环本意是统一 js中循环的标准,我们查看可以使用for of循环的对象,
解释:里面都有一个Symbol.iterator方法,这个of循环首先调用这个方法,它返回一个对象(然后就是循环对象),这个对象内部有next方法,方法返回一个对象{value:'值',done:布尔值//是否结束},每次循环就是调用这个next方法,等done为true的时候循环结束。
这是不是很像Generator函数,所以说Generator调用的结果也是可以用of循环的,比如map和set结构都有这个Symbol.iterator方法,,所以它也是可以循环的,但是对象是没有这个方法的,所以需要我们手动添加一下
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
work: ['喝茶'],
[Symbol.iterator]: function * () {
const all = [...this.life, ...this.learn, ...this.work]
for (const item of all) {
yield item
}
}
}
for (const item of todos) {
console.log(item)
}
因为我们看到 它和Generator函数很像,所以我们用它辅助实现一下,
给[Symbol.iterator]定义一个生成器函数,返回一个可迭代对象,然后我们使用 yield 函数来辅助实现,因为它返回的和我们要的那个对象是一样的。
Symbol
symbol 是一种基本数据类型 (primitive data type)。Symbol()
函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()
"。
每个从Symbol()
返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。更进一步的解析见—— glossary entry for Symbol。
使用场景:扩展对象,属性名冲突问题
一般使用第三方库扩展时,我们预防覆盖方法名,可以使用Symbol对象做key,因为两个 Symbol 永远不会相等。
由于无法创建出一样的 Symbol 值, 所以我们可以用它创建「私有」成员
更多的方法可以查看mdn
Symbol
Modules
模块 es6中的import语句为变量,函数,和类创建的是只读绑定,标识符只有在导出的模块中可以修改,即使是导入绑定的模块也无法更改绑定的值。
一些简单的语法可以在mdn上查看
这里贴一个以前的异步模块
var me =(function hello(name){
function greeting(){
console.log('hello'+name)
}
return {//public API
greeting:greeting
}
})('kyle')
console.log(me.greeting())
Template literals(模板字⾯量)
// 反引号包裹
const str = hello es2015, this is a string
;
允许换行,可以通过${}插入表达式,返回得结果会输出到对应位置
xxx${...}xxx
**带标签的模板字符串,模板字符串的标签就是一个特殊的函数,
使用这个标签就是调用这个函数**
const name = 'tom'
const gender = false
function myTagFunc (strings, name, gender) {
// console.log(strings, name, gender)
// return '123'
const sex = gender ? 'man' : 'woman'
return strings[0] + name + strings[1] + sex + strings[2]
}
const result = myTagFunc`hey, ${name} is a ${gender}.`
console.log(result)
hey, tom is a woman.
就是把字符串根据变量分割成一个数组,然后后续就是字符串中的变量依次传递.
Default parameters(默认参数)
在es5中给函数的默认参数只能
function foo (enable) {
// 短路运算很多情况下是不适合判断默认参数的,例如 0 '' false null
// enable = enable || true
enable = enable === undefined ? true : enable
console.log('foo invoked - enable: ')
console.log(enable)
}
//这样去赋值现在es6中我们可以
function foo (enable = true) {
console.log('foo invoked - enable: ')
console.log(enable)
}
//包括解构的时候可以 const {sex='默认'}=props;
甚至函数可以
function foo({sex='1'}={}){
console.log(sex)
}
Enhanced object literals(对象字⾯量增强)
这一篇比较简单看代码把
// 对象字面量
const bar = '345'
const obj = {
foo: 123,
// bar: bar
// 属性名与变量名相同,可以省略 : bar
bar,
// method1: function () {
// console.log('method111')
// }
// 方法可以省略 : function
method1 () {
console.log('method111')
// 这种方法就是普通的函数,同样影响 this 指向。
console.log(this)
},
// Math.random(): 123 // 不允许
// 通过 [] 让表达式的结果作为属性名
[bar]: 123
}
// obj[Math.random()] = 123
console.log(obj)
obj.method1()
// Object.assign 方法
// const source1 = {
// a: 123,
// b: 123
// }
// const source2 = {
// b: 789,
// d: 789
// }
// const target = {
// a: 456,
// c: 456
// }
// const result = Object.assign(target, source1, source2)
// console.log(target)
// console.log(result === target)
// 应用场景
function func (obj) {
// obj.name = 'func obj'
// console.log(obj)
const funcObj = Object.assign({}, obj)
funcObj.name = 'func obj'
console.log(funcObj)
}
const obj = { name: 'global obj' }
func(obj)
console.log(obj)
// Object.is
console.log(
// 0 == false // => true
// 0 === false // => false
// +0 === -0 // => true
// NaN === NaN // => false
// Object.is(+0, -0) // => false
// Object.is(NaN, NaN) // => true
)
Destructuring assignments(解构分配)和Spread operator(展开操作符)
数组的解构
const arr = [100, 200, 300]
const [foo, bar, baz] = arr;
可以直接这样去解构,对应下表0,1,2
如果不要前面的可以
[,,baz]
这样就是只取2位置得了
经典的排序算法 交换位置可以:
const [arr[1],arr[0]]=[arr[0],arr[1]]
然后展开操作符
const [foo, ...rest] = arr;
取第一个 参数之后所有的返回一个数组。
展开操作符只能放在最后。
取出来的值也可以给定默认值
例如
const [foo, bar, baz = 123, more = 'default value'] = arr;
对象的解构:
const obj = { name: 'zce', age: 18 }
const { name } = obj;
相当于{name:name}
可以这样去解构,包括对象当参数传递时也可以这样解构
然后也可以给定默认值,前面有例子。
这个解构变量名字的声明和正常声明相反,是在右边,如下
const { name: objName = 'jack' } = obj;
name是从obj解构出来的属性名,objName是我们新的变量名来接收这个值,并且给了它一个默认值。
然后就是展开操作
const {name,...args}=obj;
不要在乎这些报错
这个解构展开就是说,除了解构指明的属性名之外,剩余的属性组成一个新对象,用剩余参数名称接受。
Proxy和Reflect
代理对象proxy和defineProperty的对比
优势:
- Proxy 可以监视读写以外的操作
- Proxy 可以很方便的监视数组操作
- Proxy 不需要侵入对象
一个简单的例子
const person2 = {
name: 'zce',
age: 20
}
const personProxy = new Proxy(person2, {
get (target, property) {
console.log('get', property)
return target[property]
},
set (target, property, value) {
console.log('set', property, value)
target[property] = value
}
})
personProxy.name = 'jack'
console.log(personProxy.name)
更多的api的使用方式可以在mdn中查看
Proxy的使用方式
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。Reflect
不是一个函数对象,因此它是不可构造的。
它提供了对对象的统一操作,之前对象的操作不统一,
const obj = {
name: 'zce',
age: 18
}
console.log('name' in obj)
console.log(delete obj['age'])
console.log(Object.keys(obj))
console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
具体提供了哪些方法,也可以直接去mdn中查看
Reflect
该内容借鉴于 拉钩大前端训练营
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。