ECMAScript 的概述
- ECMAScript 是一本脚本语言,缩写为ES,通常看做为JAVAScript 的标准化规范,实际是 JAVAScript 是ECMAScript的扩展语言,因为在ECMAScript 当中只是提供了最基本的语法(就是约定了我们的代码该如何编写,例如:如何定义变量和函数...)只是停留在语言层面,并不能完成页面当中的实际功能开发。我们经常使用的JAVAScript它实现了ECMAScript语言的标准,并且在这个基础之上做了扩展,使得我们可以在浏览器上操作DOM,BOM,在node环境可以去做读写文件之类的一些操作。
- 总的来说在浏览器环境当中的JAVAScript,它就等于 ECMAScript + web提供的API(BOM,DOM )
- 在node环境当中所使用的JAVAScript = ECMAScript + node提供的API
- JAVScript 中语言本身就是 ECMAScript。随着web这种模式深入的发展,从2015年开始 ES 保持着每年一个版本的迭代,很多新特性陆续出现。导致JAVAScript 这门语言的本身也就变得越来越高级,越来越便捷。
ES2015 概述
- 解决原有语法上的一些问题或者不足。例如:let和const提供的块级作用域。
- 对原有语法进行增强,使之变得更为便捷易用。例如:解构,展开,参数默认值,模板字符串...
- 全新的方法,全新的功能,全新的对象。例如:promise,proxy,Object.assign()。
- 全新的数据类型和数据解构。例如:Symbol,set,map...
ES2015与let块级作用域
- 在ES2015之前,ES中只有两种作用域 (全局作用域,函数作用域)
- 全局作用域
- 函数作用域
- 块级作用域 (ES2015新增)
- 以前块是没有单独的作用域的,在块中定义的成员,外部是可以访问到的。
例如:
if(true){
var foo = 123
}
console.log(foo)
if(true){
let foo = 123
}
console.log(foo)
const
- 用来声明一个只读的常量(恒量),特点是在let的基础上多了一个只读的特性。
数组的结构
- 数组的结构是根据位置,因为数据中的元素有下标,它是有规则的。
const arr = [100,200,300,4]
const [foo,bar,baz] = arr;
console.log(foo,bar,baz)
const [foo,...reset] = arr;
console.log(reset)
const arr = [100,200,300,4]
const [foo] = arr;
console.log(foo)
const [foo,bar,baz,boo,coo] = arr;
console.log(coo)
对象的结构
const obj = { name: "ykk", age:20 }
const { name, phone = "123000"} = obj
console.log(phone)
const obj = { name: "ykk", age:20 }
let name = "kee"
const { name: objName } = obj
console.log(objName)
const obj = { name: "ykk", age:20 }
let name = "kee"
const { name: objName = "default value"} = obj
console.log(objName)//ykk 还可以给重命名添加默认值
模板字符串
const str = `hello es2015
this is a string`
console.log(str)
const name = "tom"
const str = `HEY ${name}---${1+1} ----${true?0:1} --- ${Math.random()}`
console.log(str)
const str = console.log`hello word`
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 = myTagFunc`HEY, ${name} IS A ${gender}.`
console.log(result)
ES2015 字符串的扩展方法
const message = "Error: foo is not defined"
console.log(
)
参数默认值
//函数参数的默认值
// function foo (enable){
// // enable = enable || true
// enable = enable === undefined ? true : enable
// console.log(enable)
// }
// foo(false)
function foo (enable = true){
console.log(enable)//false
}
foo(false)
剩余数组
function foo(num,...args){
console.log(args)
}
foo(1,2,3,4)
展开数组
const arr = [1,2,3]
console.log(...arr)
箭头函数
对象的字面量
const bar = 1
const obj = {
bar,
method1(){
console.log("11")
},
[bar]:123,
[Math.random()]:456
}
console.log(obj)
obj.method1()
对象的扩展方法
Object.assign() 将多个源对象的属性赋值到一个目标对象
const source1 = {
a:123,
b:456
}
const target = {
a:999,
b:888
}
const source2 = {
a:1000,
c:666
}
const result = Object.assign(target,source1,source2)//{ a: 1000, b: 456, c: 666 }
console.log(result === target)//true
//如果源对象和目标对象有相同的属性,源对象会把目标对象覆盖,
function func(obj){
// obj.name = "ykk"
//使用Object.assign()给他赋值给一个新的目标对象,这样就不会影响外面定义的obj对象了
const result = Object.assign({},obj)
result.name = "ykk"
result.aa = "123"
}
const obj = { name: "uuu" }
func(obj)
console.log(obj)
Object.is() 用来判断两个值是否相等
console.log(
0==false,//true
0===false,//false
NaN===NaN,//false
+0 === -0,//true
Object.is(+0 , -0),//false
Object.is(NaN , NaN)//true
)
Proxy
- 如果我们想要监视某个对象的读写,可以使用es5提供的Object.defineProperty。
const person = {
name:"uu",
age:30
}
const personProxy = new Proxy(person,{
get(target,property){
return property in target ? target[property] : undefined
},
set(target,property,value){
target[property] = value
}
})
console.log(personProxy.xx)
console.log(personProxy.age)
Proxy VS Object.defineProperty()
- Object.defineProperty()只能够监视属性的读写
- Proxy优点
- Proxy 能够监视更多对象的操作,例如delete操作,对对象当中方法的调用
const person = {
name:"uu",
age:30
}
const personProxy = new Proxy(person,{
deleteProperxy(target,property){
console.log(target,property)
delete target[property]
}
})
delete person.name
console.log(person)
- Proxy 更好的支持数组对象的监视
const list = []
const result = new Proxy(list,{
set (target,property,value){
console.log(
target[property] = value
return true
}
})
result.push(100)
- proxy是以非侵入的方式监管了对象的读写。一个定义好的对象不需要对对象做任何操作,就可以监视到他内部的读写。Object.defineProperty()就需要用特定的方式,单独去定义对象当中那些需要被监视的属性,对于一个已经存在的对象,需要对它做很多额外的操作。
Reflect 统一的对象操作API
- reflect 是一个静态类,不能够通过 new Reflect().
- Reflect 封装了一系列对对象的底层操作
- Reflect 成员方法就是 Proxy 处理对象的默认实现
const person = {
name:"ykk",
age:20
}
const p = new Proxy(person,{
get(target,property){
console.log("wtach log")
return Reflect.get(target,property)
}
})
console.log(p.name)
- Reflect对象最大的意义是他提供了统一一套用于操作对象的API。因为之前操作对象的时候,有可能会使用Object的方法,也有 in 或者 delete 这样的操作符,对于一些新手来说太乱了,并没有什么规律。Reflect对象就很好的解决了这个问题,统一了对象的操作方式
const obj = {
name:"23",
age:12
}
console.log(Reflect.has(obj,'name'))
console.log(Reflect.deleteProperty (obj,'age'))
console.log(Reflect.ownKeys(obj))
Promise
- 一种全新的异步编程解决方案,通过链式调用方式,解决了传统异步编程中回调函数嵌套过深的问题。
class
class Person{
constructor(name){
this.name = name
}
say(){
console.log(`hi my name is ${this.name}`)
}
}
let p = new Person("jack")
p.say()
- 这种独立定义的语法,相比较之前函数的方式更容易理解,结构更加清晰
ES2015 静态方法
class Person {
constructor(name){
this.name = name
}
say(){
console.log(this.name)
}
static create(name){
return new Person(name)
}
}
let p =Person.create("tom")
p.say()
- 注意:因为静态方法是挂载到类型上面的,所以在静态方法内部他的this就不会指向某一个实例对象,而是当前的类型
ES2015 类的继承
- 在es2015之前使用继承大多都是用原型的方式去实现继承。在2015中实现了一个专门实现类型继承的关键词 extends。
- extends 相比于原型继承更方便,更清楚
class Person{
constructor(name){
this.name = name
}
say(){
console.log("person",this.name)
}
}
class Student extends Person{
constructor(name,number){
super(name)
this.number = number
}
hello(){
super.say()
console.log("Student",this.number)
}
}
let student = new Student("jack",2)
student.hello()
ES2015 Set
- set数据结构,可以理解为集合,与传统的数组比较相似,不过set内部的成员是不允许重复的,每一个值在同一个set中都是唯一的。
let s = new Set()
s.add(1).add(2).add(3).add(4).add(5)
console.log(s.has(1))
console.log(s.delete(1))
let arr = [1,2,3,4,2,4,5]
let result = [...new Set(arr)]
console.log(result)
ES2015 Map
let map = new Map ()
let tom = { name: 'jack'}
map.set(tom,20)
map.set(true,1).set("num","123")
map.forEach((value,key) =>{
console.log(value,key)
})
- map与对象最大的不同就是他可以用任意类型的数据作为键,而对象实际上只能使用字符串作为键
ES2015 Symbol
- 在ES2015之前对象的属性名都是字符串,而字符串有可能就会有重复的,重复的话就会产生冲突。
- 通过Symbol创建的每一个值都是唯一的,他永远不会重复
const name = Symbol()
const person = {
[name]:123,
say(){
console.log(this[name])
}
}
person.say()
ES2015 Symbol 补充
const obj = {
[Symbol()]:"symbol value",
foo:123
}
for(let val in obj){
console.log(val)
}
console.log(Object.keys(obj))
console.log(JSON.stringify(obj))
console.log(Object.getOwnPropertySymbols(obj))
for of 循环
- 在ECMAScript当中遍历数据有很多种方法,最基本的for循环,比较适用于遍历普通的数组,然后还有for in 循环 比较适合遍历键值对,还有一些函数式的遍历方法... 例如数组对象的forEach方法。各种各样的遍历数据方式都有一定的局限性。所以es2015借鉴了很多其他的语言,引入了一种全新的遍历方式 for of 循环。
- for of 循环作为遍历所有数据结构的统一方式。
const arr = [ 100, 200, 300, 400 ]
for(let item of arr){
console.log(item)
if(item>100){
break
}
}
const obj = { name:'kk', age: 19 }
for(const item of obj){
console.log(item)
}
可迭代接口
- for...of 循环是一种数据统一遍历方式,但是经过试验,发现它只能够遍历数组之类的数据结构,对于普通的对象如果直接遍历就会报出错误。
- ES能够表示有结构的数据类型越来越多,从最早的数组,对象到现在新增了set和map。开发者还可以组合使用这些类型去定义符合自己业务需求的数据结构。为了提供一种统一的遍历方式,ES2015就提出了一种Iterable接口,可迭代的。
- 可迭代接口就是一种可以被for...of循环统一遍历访问的规格标准。只要这个数据他实现了可迭代接口,他就能够被for..of循环去遍历。之前尝试的set map,数组可以被for...of遍历的数据,是因为他们内部都已经实现了这个接口
const set = new Set(["foo","bar","coo"])
let interator = set[Symbol.iterator]()
console.log(interator.next())
console.log(interator.next())
console.log(interator.next())
console.log(interator.next())
console.log(interator.next())
//打印如下:
// { value: 'foo', done: false }
// { value: 'bar', done: false }
// { value: 'coo', done: false }
// { value: undefined, done: true }
// { value: undefined, done: true }
实现可迭代接口
const obj = {
store:["foo","bar","boo"],
[Symbol.iterator]:function(){
let self = this
let index = 0
return {
next:function(){
const result = {
value: self.store[index],
done: index>=self.store.length
}
index ++
return result
}
}
}
}
for(const item of obj){
console.log(item)
}
ES2015 迭代器模式
const todos = {
life:["逛淘宝","花钱","买衣服","吃小龙虾"],
learn:["语文","数学","英语"],
work:["喝茶"],
each:function(callback){
let arr = [].concat(this.life,this.learn,this.work)
for(const item of arr){
callback(item)
}
},
[Symbol.iterator]:function(){
const arr = [...this.life,...this.learn,...this.work]
let index = 0
return {
next:function(){
const result = {
value:arr[index],
done:index++>=arr.length
}
return result
}
}
}
}
todos.each(function(item){
console.log(item)
})
for(const item of todos){
console.log("迭代器",item)
}
- 迭代器他的核心意义就是对外提供统一遍历接口,外部不用去关系数据的内部结构是什么样的,ES2015迭代器实现的语言层面的迭代器模式,可以适用于任何数据结构,只需要你的代码实现iterator方法,以及他的迭代逻辑。
ES2015 生成器
- 引入Generator生成器的目的就是为了能够在复杂的异步代码中减少回调函数嵌套产生的问题,提供更好的异步编程解决方案
// function *foo(){
// console.log("log")
// return 100
// }
// const result = foo()
// console.log(result.next())//{ value: 100, done: true }
// next方法的返回值与迭代器方法的返回值有相同的结构,说明 generator 也实现了 iterator接口
function * foo(){
console.log(11)
yield 100
console.log(22)
yield 200
console.log(33)
yield 300
}
const result = foo()
console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())
// 生成器函数会自动返回生成器对象,调用这个对象的方法,函数体开始执行,执行过程中遇到 yield关键词暂停下来,yield后面的值会作为后面的结果返回,周而复始。
// 生成器函数最大的特点就是惰性执行。
const todos = {
life:["逛淘宝","花钱","买衣服","吃小龙虾"],
learn:["语文","数学","英语"],
work:["喝茶"],
[Symbol.iterator]:function * (){
const arr = [...this.life,...this.learn,...this.work]
for (const item of arr){
yield item
}
}
}
for (const item of todos){
console.log(item)
}
ES2016 概述
- 与ES2015相比 ES2016只是一个小版本,仅包含两个小功能。
const arr = ["foo", NaN, false]
console.log(arr.indexOf("foo"))
console.log(arr.indexOf(NaN))
console.log(arr.includes(NaN))
console.log(Math.pow(2,10))
console.log(2 ** 10)
ES2017 概述
const obj = {
foo:"value1",
bar:"value2"
}
// Object.values()返回对象当中所有值组成的数组
// console.log(Object.values(obj))
// Object.entries()是以数组的方式返回对象的键值对
// console.log(Object.entries(obj))// [ 'foo', 'value1' ], [ 'bar', 'value2' ] ]
// for (const [key,value] of Object.entries(obj)){
// console.log(key,value)
// }
console.log(new Map(Object.entries(obj)))//Map(2) { 'foo' => 'value1', 'bar' => 'value2' }
// Object.getOwnPropertyDescriptors
// String.prototype.padEnd
// String.prototype.padStart
//Async/Await