1. ECMAScript 与Javascript的关系
2. ECMAScript的发展过程
3. ECMAScript 2015 (ES6) 的新特性
关于ECMAScript 以及 它 和Javasctipt的关系:
ECMAScript 是 脚本语言 、缩写为ES, 是Javascript 的标准化规范. Javascript 是 ECMAScript 的扩展语言. ECMAScript 提供最基本的语法,约定编写规则。 (语言层面)不能直接完成实际应用开发
Javascript 实现了ECMAScript 的语言标准 + 扩展 => 在浏览器中操作DOM 和 BOM
在浏览器环境中: Javascript = ECMAScript + Web APIs(即 DOM + BOM)
在node.js 环境中: Javascript = ECMAScript + Node APIs(即 fs, net 等)
Javascript 语言本身就是 ECMAScript.
ES2015 概述
超长连接: https://262.ecma-international.org/6.0/
重点变化(4类)
1. 解决原有语法问题: let const 提供的块级作用域
2. 对原有语法增强:解构 展开 参数默认值 模板字符串 等
3. 新方法,功能,对象: promise proxy object.assign
4. 新数据类型、数据结构: symbol set map ..
前言: node.js setup : 安装 nodemon : 监视文件变化
ES2015 let 与块级作用域
作用域scope: 代码中的一个(成员)它(能起作用的范围)
- 全局作用域 global scope
- 函数作用域 functional scope
- 块级作用域 ( { let i = 0 } )
// let 声明的成员只会在所声明的块中生效
if (true) {
var foo = 'var'
let joo = 'let'
console.log(foo) //'var'
console.log(joo) //"let"
}
console.log(foo) // 'var'
console.log(joo) // undefined
这是两个互不想干的 i
for(let i = 0; i < 3; i++) {
let i = "foo"
console.log(i)
}
// "foo"
// "foo"
// "foo"
for(let i = 0; i < 3; i++) let i = "foo"
console.log(i)
// 在变量声明之前使用var变量 : 不会报错,而是返回 undefined
console.log(i) // 此时 i 已经声明,只是没赋值 / 术语:变量声明的提升 bug
var i = 4;
// 在变量声明之前使用let变量 : 报错: cannot access i before initialization
console.log(j)
let j = 5;
// 提升
// 案例: let 应用场景:循环绑定事件,事件处理函数中获取正确索引
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = function () {
console.log(i)
}
}
elements[2].onclick()
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = (function (i) {
return function () {
console.log(i)
}
})(i)
}
elements[0].onclick()
var elements = [{}, {}, {}]
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = function () {
console.log(i)
}
}
elements[2].onclick()
ES2015 const
常量/恒量
const : 只读
声明 const 变量的两个注意点: 1. 不可被修改 2. 声明时需要赋值
// 1. 不可被修改
// 会报错: TypeError: Assignment to constant variable.
const name = 'joe'
name ="jack"
// 2.声明变量时需要赋值
// 会报错:SyntaxError: Missing initializer in const declaration
const name
name = 'zce'
注: 不可被修改 means: 不能在声明后重新指向一个新的内存地址。 可以修改const中的属性
const obj = {}
obj.name = "joe" // valid
obj= {
name : "joe" // TypeError: Assignment to constant variable.
// 改变内存指向
}
obj= {} // TypeError: Assignment to constant variable.
// 改变内存指向
最佳: 不用var / 主const / 辅let
数组的解构
Destructuring(动词:因此它是一种方式)
1. 解构数组制定位置成员: const [ , , bar] = arr, 获取index 2 成员
2. 定义: 将一个较大的数组or对象 改成 较小的数组or对象 的方式
3. const a = array【0】 // es2015 之前也有数组的解构,
4. ... speard operator 它和数组解构没有关系.
5. 解构的语法 const 【变量名1,,变量名2,...rest 】 = 数组 // ...rest 其实就是展开多个变量名
// 原始数组
const arr = [1,2,3]
// //以前提取数组中变量的方法
// const foo = arr[0]
// const joo = arr[1]
// const zoo = arr[2]
// console.log(foo,joo,zoo) // 1 2 3
// 解构方法1: 对应
const [foo,joo,zoo] = arr
console.log(foo,joo,zoo) // 1 2 3
const [ , ,boo,xxj] = arr // 若不需要留空// index 对号入座
const[zzz] = arr
console.log(zzz) // 1 // 不够长度没关系
console.log(boo,xxj) // 3 undefined // 对不上号的提取结果undefined
// 解构方法2: ...rest //...rest : 解构从当前位置index:1 开始向后的所有成员
const [ioo, ...rest] = arr
console.log(rest) // TYPE: array // [2,3]
console.log(...rest) // TYPE: array 中的成员 // 2 3
// 设置默认值: xxx =‘string’ yyy = 123 // 正常赋值
const [jjj,hhh, yyy =123, xxx='name'] = arr
console.log(jjj,hhh,yyy,xxx) // 1 2 123 'name'
对象的解构
前言: 与数组类似,以属性名匹配。而不是顺序
重点看: 设置默认值 + 变量名冲突(重命名语法)
const { 提取变量的名 : 重命名的名 = default vaule } = obj(从这个对象中提取)
// 原始object
const obj = {name: 'guohai', age:18}
// //以前提取object中属性的方法
// const name = obj.name
// const age = obj.age
// console.log(name,age) // guohai, 18
// 解构: 对应属性名
const { name } = obj
console.log(name) // guohai
// 设置默认值
const { random = 'default' , another} = obj
console.log(random) // default
console.log(another) // undefined : 匹配不到就返回undefined
// 与当前作用域中的变量重名
const age = 99
//const { age } = obj
const { age: objName } = obj //重新命名
console.log(objName) // 返回18 //使用objName 调用
`ES2015 `模板字符串` template literal`
- 换行
- 使用 `something, ${ 变量/js 语句 }` javascript expression , ${}返回值输出到字符串中
- 包裹使用 `something something\` something \` something `
const name = 'guohai'
const string = `As Dalian Science and \`Technology\` Welfare
Card is too much; the room falls in love;
the two places are separated and the speed
is fast to liberate ${name}drunkenness`
// 1.可用 \`,
// 2.可换行,
// 3.可用expression 差值表达式 ${name},任何标准js语句,返回值加入string
`带标签的模板字符串 tagged template literal`
作用: 对模板字符串加工, 检查等
const name = 'guohai'
const sex = 'true'
//作用: 对模板字符串加工
function tagFunc (string, name, sex) { //is array. the result of string.split()
console.log(string) //以表达式分割string ['hey, my name is ', 'i am a ', '.']
console.log(string[0], name, string[1], sex ? 'man' : 'woman', string[2])
}
const result = tagFunc `hey, my name is ${name}, i am a ${sex}.`
`字符串 の 扩展功能`
1. includes()
2. startsWith()
3. endsWith()
ES2015 参数
参数默认值:以前用逻辑判断做,'短路运算是什么?' function foo( enable= true) { }
没有传递参数 或 传递值为undefined 时 使用 enable = true
若有多个形参数, 带有默认参数的形参要出现在形参列表的最后。 (问: 如果有多个带有默认值的形参呢? 如何写)
// function foo () {
// console.log(arguments)
// }
function foo (first, ...args) {
console.log(args)
}
foo(1, 2, 3, 4)
ES2015 展开数组
温习 apply()方法
// 展开数组参数
const arr = ['foo', 'bar', 'baz']
// console.log(
// arr[0],
// arr[1],
// arr[2],
// )
// console.log.apply(console, arr)
console.log(...arr)
ES2015 箭头函数
简化,没有this机制 ()=>{}
ES2015 对象
对象字面量的增强 Enchanced Object Literals
语法: 计算属性名:【表达式】:值 ;表达式的计算结果作为属性的名
计算属性名规定的属性会放在属性列表开头,但是顺序对于object来说不重要
const bar = "345"
const obj = {
//1
foo : "123",
// bar : bar // before es2015
bar // es2015 //属性名 与 变量名一致 => 省略
//2
functionName : function(){ console.log('before es2015')} // before es2015
function2015 () { console.log('es2015')} // es2015
//3
//计算属性名:【表达式】:值 // 表达式的计算结果作为属性的名
//计算属性名规定的属性会放在开头,但是顺序对于object来说不重要
[1+1]: 123
}
ES2015 对象扩展方法 之 Object.assign
语法: Object.assign (目标对象,源对象1,源对象2 .... )
作用: 用后面对象的值 覆盖 目标对象的 值
返回值 === 目标对象
const target = {
a : 1,
b : 2
}
const source1 = {
a : 3,
b : 5
}
const source2 = {
a : 7,
b : 8,
c : 10
}
const result= Object.assign(target, source1, source2)
console.log(result) // 最后面的依次覆盖 结果为 { a: 7, b: 8, c: 10 }
console.log(target) // 结果为: { a: 7, b: 8, c: 10 }
console.log(result === target) // true
// 应用场景
function func (obj) {
obj.name = 'change obj'
console.log(obj) // obj 形参 实参 和 外部变量指向同一地址, 因此一样 //1 { name: 'change obj' }
const funcObj = Object.assign({}, obj) // 赋值到一个全新的空对象上{} => 不影响外部
funcObj.name = 'func obj'
console.log(funcObj)//2 { name: 'func obj' }
}
const obj = { name: 'global obj' }
func(obj)
console.log(obj) //3 { name: 'change obj' }
ES2015 对象扩展方法 之 Object.is
//== 会在比较前转换数据类型 === 严格对比
console.log(0 == false); // returns true, 隐式转换
//=== 特列
console.log( +0 === -0 ); // returns true, 但是希望得到 false
console.log(NaN === NaN); // returns false, 实际中希望得到 true
console.log(Object.is(0, false)) // false
console.log(Object.is(NaN,NaN)) // true
console.log(Object.is(+0,-0)) // false
ES2015 对象扩展方法 之 Proxy 代理
监视目标对象的属性读写 : ES2015 之前 : Object.defineProperty
对象访问代理器(名字不同是因为功能更强大) : proxy ,也可以监视 数组【array】
const person = {
name: 'joe',
age: 18
}
// proxy (代理对象, {代理处理对象}) 2个变量 、 代理处理对象就是函数集合体啦
const personProxy = new Proxy( person, {
//get (代理对象,外部访问代理对象属性的名字) {},
get (target, property) {
console.log(target, property) // {person} , name
return property in target ? target [property] : 'not found'
// RETURNED VALUE IS : 外部访问这个属性得到的结果
},
//set (代理对象,外部访问代理对象属性的名字,写入的值) {},
set (target, property, value) {
// 也可以在辅助前做检查,比如数据类型检查
// 为目标对象赋值
target[property] = value
console.log(target, property, value)
},
//deleteProperty (代理对象,需要删除的属性的名) {}
deleteProperty (target, property) {
delete target[name]
}
})
delete personProxy.age // 触发 //语法有点奇怪
personProxy.gender = 'male' // 触发
console.log(personProxy.name) // 触发
Proxy 的一些 方法 (待我详细调查)
- get
- set
- has
- deleteProperty
- getPropertyOf
- isExtensible
- preventExtensions
- getOwnPropertyDescriptor
- defineProperty
- ownKeys
- apply
- construct
- 问题:new Proxy(target,「方法」)会继承target的方法吗?在下例中listProxy使用了list 数组的push()方法, 同理, 是否会继承object的方法()
// proxy 监视 数组
const list = []
const listProxy = new Proxy (list, {
set(target, property, value){
// console.log(property, value)
target[property] = value
return true // 不可以忘记 : 代表设置成功
}
})
listProxy.push(2) //可
listProxy[1] = 200 //可
console.log(listProxy)
ES2015 Reflect 静态类
如果在Proxy中没有设置get set 等方法成员方法: 就相当于设置了一个get set 等方法并将参数交给Relfect的 get set 等 方法。
Reflect 内部封装了一系列对对象的底层操作
//Reflect 的意义: 统一处理OBJECT
console.log('name' in obj) // obj 有没有 ’name‘
console.log(delete obj['age']) // delete obj[property]
console.log(Object.keys(obj)) // obj的所有属性名
console.log(Reflect.has(obj,'name')) // obj 有没有 ’name‘
console.log(Reflect.deleteProperty(obj,'age')) // delete obj[property]
console.log(Reflect.ownKeys(obj)) // obj的所有属性名
ES2015 Reflect + Proxy の 正确打开方式
整个数据结构和方法都被objProxy复制走了吗. 这里可以用 objProxy.属性名来触发
const obj = {
foo : '123',
bar : '456'
}
const objProxy = new Proxy (obj, {
set(target, property, value) {
// 添加监视逻辑 // 具体操作交给REFLECT
return Reflect.set(target, property, value)
}
})
objProxy.foo // 触发
console.log(objProxy.name = 'gg') //123
console.log(objProxy) // { foo: '123', bar: '456', name: 'gg' }
console.log(obj) // { foo: '123', bar: '456', name: 'gg' }
ES2015 Promise
here will be a link
// 基本用法
// promise 对象接受一个函数作为参数, 这个函数接受两个函数作为参数,分别是成功和失败回调函数
Const promise = new Promise(resolve, reject) {
resolve (100)
// reject (200)
// 1/2 选择一个执行
})
promise.then((value)=>{console.log(value)}, ()=>{}) // then 返回一个promise
//结果是100
ES2015 Class
基本定义:
// 1. 定义一个函数作构造函数
function Person (name) {
this.name = name
}
//2. 定义函数的原型对象 // 共享
Person.prototype.say = function () {
console.log(`hi, my name is ${this.name}`)
}
//class
Class person {
constructor (name) {
this.name = name
}
say () {
console.log('say hello')
}
static create(name){ // STATIC
//因为该方法时放在类上,因此this指向的是类。而不是实例对象本身
return new person(name)
}
}
const jack = new person('jack')
jack.say()
静态方法 + 实例方法
静态方法:通过类型本身(class)调用 static ES2015中通过static 关键词添加 静态方法和变量
static 方法因为是挂载在类本身, this 指向不是某一个实例对象而是类本省
实例方法: 通过实例对象(instance)调用
类的继承 extends + super() 关键词
class Student extends Person { // extendS
constructor (number, name){
// this 放在最前面
super(name) // 继承父类的name 变量
this.number = number
}
hello() {
super.say() // 继承父类Person的say方法
}
}
ES2015 全新 数据结构 - - Set - - 是一个类
可以理解为集合, 与数组类似,内容不允许重复, 是一个类, 通过new 关键词创建(27课)。
实例方法: add(), 向实例中添加数据。 add()方法返回集合本身 => 可以链式调用
forEach( i => console.log(i)),遍历对象
for ( let item of arr ) { console.log ( item ) }
.size 获取set实例的长度。
.has('something') 实例中是否有 'something'
.clear() 清除所有
// set类の实例方法展示:
var s = new Set ()
s.add(1).add(22).add(3) // 向实例中添加数据。 add()方法返回集合本身 => 可以链式调用
s.forEach( i => console.log(i)) //遍历对象
//for ( let item of s ) { console.log ( item ) } //遍历对象
s.size //获取set实例的长度。
s.has('something')// 实例中是否有 'something'
s.clear() //清除所有
//去重
const arr = [1,2,3,3,1]
const noRepeat = new set (arr) // set 返回一个set 的实例对象 // 伪数组
const result = Array.from(noRepeat) // 将伪数组 转化为数组
ES2015 全新 数据结构 - - Map - - 是一个类
类似于对象Object,是key -value pair,
对象中的 key 只能是 string,如果不是string , 就会用toString()返回一个string 作为key。
下面是关于这个问题的展示, Map就是为了解决这个问题
const obj = {}
obj[true] = 'boolean'
obj[123] = 'number'
obj[{a:1}] = 'object'
console.log(Object.keys(obj)) // obj的所有属性名
console.log(Reflect.ownKeys(obj)) // obj的所有属性名
// result is : ['true', '123', '[object Object]']
Map 可以用任意数据类型作为key (与object的最大区别)
const m = new Map() // 利用 new Map ( )创建一个map 对象
const tom = {name : 'tom' } // 准备一个object 作为 key
m.set({tom, 90}) // m.set(key, value)
console.log(m) //result is: Map { {name : 'tom' } => 90 }
// m.has()
// m.delete()
// m.clear()
m.forEach (value, key) => {
console.log(value, key)
}
ES2015 全新 数据结构 - - Symbol - - 是一个类
作用: 表示一个独一无二的值, 目前最大作用, 为对象OBJECT 添加一个unique属性名
symbol 可以注册 object 的 key, 其不会被转化为 string
symbol.for('somthing')
const cache= {
[symbol()] = 'qqq'
}
// a.js
cache ['a_foo'] = Math.random()
// b.js
cache ['b_foo'] = '123'
// 解决key value pair 中 key 重复命名的问题。 约定 const [文件名_名] = 'value'
// 这可以规避问题 但不能解决
cache [key] = value // 个人, 我不常这样为object 添加值。 可以记忆一波
cache[symbol()] = '789'
cache[symbol()] = '456'
//获取symbol注册的 key value pair
let s = Symbol.for("something");
obj[s] = 1
console.log(obj[s])
ES2015 for ... of ... 循环
可以使用‘break’ 终止循环, 伪数组也可以遍历
// 遍历 array + array alike 数组 + 伪数组
for( item of arr) {
if ( item === 'stop') {
// some code
break
}
}
// 遍历 map
const map = new Map()
map.set('foo', 123) // (key, value)
// 拿到[key, value]
for( item of map) {
console.log(item) // ['foo', 123]
}
// 直接拿到key value
for( [key, value] of map) { // 数组解构语法[]
console.log(key, value) // foo 123
}
//! 不可以直接 遍历 object, 因为 object 中没有 Iterator 可迭代接口
Iterator 可迭代接口, 是一个对象
迭代器模式: 不需要担心被调用数据的结构是否发生了变化。对外提供统一遍历接口
arr [ symbol.iterator]( ).next()
在arr 中挂在了一个Symbol.iterator()方法, 其返回一个带有next()方法的对象,不断调用next()来遍历
//For of 内部原理
const set = new Set([123,456,789])
//浏览器查看
// Set 中有一个 iterator 方法,调用它()得到迭代器 Array Iterator
const iterator = set[Symbol.iterator]()
//迭代器 Array Iterator 中有一个next()方法
iterator.next()
iterator.next()
iterator.next()
手写 iterator + todo task 33/34
const obj = { // iterable: 有一个iterator() 方法
store: ['foo','bar','baz'],
[Symbol.iterator] : function (){
let index =0
const self = this
return { // iterator: iterator ()方法返回的迭代器
next: function (){
const result = {
value: self.store[index],
done: index >= self.store.length
}
index ++
return result // iteration result: next() 返回的对象 ()
}
}
}
}
for(const item of obj){
console.log(item)
}
生成器函数 generator ()
惰性执行 有Symbol.iterator 接口, 配合 yield 使用
function * foo (){
console.log('111')
yield 100
}
const generator = foo() // 返回一个生成器对象
console.log(generator.next())
console.log(generator.next())
// console.log(foo().next()) // 错误方法: 重新开始
// console.log(foo().next()) // 错误方法: 重新开始
//调用生成器函数会返回一个generator对象,生成器对象,
//这个对象有一个next()方法
// generator 也有 iterator 接口
//配合 yield() 使用 === 暂停
发号器 、iterator接口 手写 36课
test