ECMAScript
和JavaScript
ECMAScript
是JavaScript
的标准化规范,实际上JavaScript
是ECMAScript
的扩展语言,ECMAScript
提供最基本语法,停留在语言层面;而JavaScript
在此基础上做扩展,浏览器端的JavaScript
等于ECMAScript
加Web
所提供的API
(DOM
和BOM
);Node
环境中的JavaScript
等于ECMAScript
加Node
所提供的API
ECMAScript
的发展过程
ECMAScript 2015
可以叫做ES6
,主要有4个大类的提升
- 解决原有语法上的一些问题或不足(
let
、const
提供块级作用域) - 对原有语法进行增强、更易用(解构、展开运算符、模板字符串、函数默认值等等)
- 全新的对象、全新的方法、全新的功能 (
Promise
、Proxy
、Object.assign
) - 全新的数据类型和数据结构(
Set
、Map
)
新特性
-
let
和const
解决块没有单独作用域问题,
let
和const
定义的变量只能在当前作用域使用if (true) { var foo = 'foo' } console.log(foo) if (true) { let foo = 'foo' } console.log(foo) // foo is not undefined // 循环绑定事件,事件处理函数中获取正确索引 var ele = [{}, {}, {}] for (var i = 0;i < ele.length; i++) { // ele[i].onclick = function() { // console.log(i) //} // 使用闭包创建函数作用域 摆脱全局作用域影响 elements[i].onclick = (function (i) { return function () { console.log(i) } })(i) } ele[2].onclick() // 3 ele[2].onclick() // 2 // 使用let块级作用域 for (let i = 0;i < ele.length; i++) { ele[i].onclick = function() { console.log(i) } }
let
没有变量提升console.log(foo) var foo = 'sdf' console.log(foo) let foo = 'sdf' // foo is not undefined
const
常量,只读的let,声明之后不能再修改(指不能修改指向的内存地址,可以修改其中的属性成员)// 恒量只是要求内层指向不允许被修改 const obj = {} // 对于数据成员的修改是没有问题的 obj.name = 'zce' obj = {} // Assignment to constant variable.
-
数组的解构
Destructruing
const arr = [100, 200, 300] const [foo, bar, baz] = arr // 100 200 300 const [foo, ...rest] = arr console.log(rest) // [ 200, 300 ]
-
对象的解构
Destructuring
// 简单对象 const obj = { name: 'wang', age: 24 } const { name, age } = obj // wang 24 // 嵌套对象 const obj2 = { worker: { name: 'wang', sex: '男' }, address: 'zhognguo' } const { worker: { name }, address } // wang zhognguo // 默认值 const obj3 = { name: 'wang', age: 24 } const { name, age, sec = '女' } = obj // wang 24 女
-
模板字符串字面量
Template literals
// 反引号包裹 const a = `hello world` // hello world // 允许换行 const b = `hello es2015, this is a \`string\`` // hello es2015, this is a `string` // 输出模板 $('#contioner').append(` There are ${count} items in your basket, ${onSale} are on sale! `); // 可以使用 ${} 插入表达式 const fruits = 'apple' const c = `this is ${fruits}` // this is apple
-
模板字符串标签函数
Tagged Templates
// 模板字符串标签是一个特殊的函数 使用这个标签就是调用这个函数 const str = console.log`hello world` // ["hello world", raw: Array(1)] // strings:字符串数组,以${}分隔符分割得到的数组 // 其余的参数与表达式相关 ${}的值 function Tag(strings, str,str2,...) const name = 'tom' const gender = false 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}.` // hey, tom is a woman.
-
字符串的扩展用法
// includes 返回布尔值,表示是否找到参数字符串 // startsWith 返回布尔值,表示参数字符串是否在源字符串开头 // endsWith 返回布尔值,表示参数字符串是否在源字符串尾部 const message = 'Error: foo is not defined.' console.log( message.startsWith('Error'), // true message.endsWith('.'), // true message.includes('foo') // true ) // 支持两个参数 表示开始搜索位置 let s = 'Hello world!'; s.startsWith('world', 6) // true s.endsWith('Hello', 5) // true s.includes('Hello', 6) // false
-
参数默认值Default parameters
// ES6之前 函数默认值使用短路运算符 function log (x, y) { // 如果y传入false 则下面赋值无效 // 需要先判断y是否被赋值 if (typeof y === 'undefined') y = 'world' y = y || 'world' console.log(y) } // 通常情况 函数默认值应该在函数的尾参数 function foo(a, enable = true) // 非尾部设置 这个参数需要穿undefined function foo(a = 'a', enable = true) { console.log('foo invoked - enable: ') console.log(enable) console.log(a) } // 只有参数值为undefied时才会使用默认值 foo(undefined, false) // 函数length属性 返回没有指定默认值的参数个数 foo.length // 0
-
剩余参数
Rest parameters
// ...rest剩余参数 用于获取函数的多余参数 代替arguments // rest参数之后不能再有其他参数 function foo (first, ...args) { console.log(args) } foo(1,2 ,3, 4) // [2, 3, 4] // 使用rest替代arguments例子 function sortNumbers () { return Array.prototype.slice.call(arguments).sort() } const sortNumbers = (...numbers) => numbers.sort() sortNumbers(1, 2, 4, 6, 2, 3) // [1, 2, 2, 3, 4, 6]
-
展开数组
Spread
// 扩展运算符... 好比reset参数的逆运算 console.log(...[1,2,3]) // 1 2 3 // 1.复制数组 const a1 = [1, 2] const a3 = [3] // const a2 = a1 // 复制指向底层数据结构的指针 并不是克隆全新的数组 const a2 = [...a1] // const [...a2] = a1 console.log(a2) // 2.合并数组 // ES5的合并 // let a4 = a1.concat(a3) // [1, 2, 3] // ES6的合并 let a4 = [...a1, ...a3] console.log(a4) console.log(a2 === a1, a4[0] === a1[0]) // 上述两种都是浅拷贝 // 3.与解构赋值结合 const list = [1, 2, 3, 4, 5] let [a, ...rest] = list console.log(a, rest) // 1 [ 2, 3, 4, 5 ] const [first, ...last] = [] console.log(first, last) // undefined [] // 4.转换字符串为真正数组 console.log([...'hello']) // ...运算符能识别四个字节的Unicode字符 js会将Unicode字符转换为2个字符 console.log('x\uD83D\uDE80y'.length) // 4 console.log([...'x\uD83D\uDE80y'].length) // 3 // 5.// 任何实现了Iterator接口的对象 都可以使用扩展运算符 Number.prototype[Symbol.iterator] = function* () { let i = 0 const num = this.valueOf() while (i < num) { yield i++ } } console.log([...5]) // [ 0, 1, 2, 3, 4 ] // Map、Set结构和Generator函数 let map = new Map([ [1, 'one'], [2, 'two'], [3, 'three'], ]) let keys = [...map.keys()] console.log(keys) // [ 1, 2, 3 ] let set = new Set([1, 2, 3, 4, 5, 2, 21, 1]) console.log([...set]) // [ 1, 2, 3, 4, 5, 21 ] const go = function* () { yield 1 yield 2 yield 3 } console.log([...go()]) // [ 1, 2, 3 ]
-
箭头函数
Arrow Functions
const arr = [1, 2, 3, 4, 5, 6, 7] // arr.filter(function (item) { // return item % 2 // }) // 常用场景,回调函数 console.log(arr.filter((i) => i % 2)) // [ 1, 3, 5, 7 ] // 1.函数体内this对象,定义时的对象,而不是使用时的对象 // 2.arguments对象在函数体内不存在,可以使用rest参数替代 // 3.不能使用yield命令,箭头函数不能用作Generator对象 // 箭头函数与 this // 箭头函数不会改变 this 指向 const person = { name: 'tom', // sayHi: function () { // console.log(`hi, my name is ${this.name}`) // } // 定义对象的方法,且该方法内部包括this 不应该使用箭头函数 sayHi: () => { console.log(`hi, my name is ${this.name}`) }, sayHiAsync: function () { // const _this = this // setTimeout(function () { // console.log(_this.name) // }, 1000) console.log(this) setTimeout(() => { // console.log(this.name) console.log(this) }, 1000) } } person.sayHi() // hi, my name is undefined person.sayHiAsync()
-
对象字面量增强
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) }, // 属性名表达式 [bar]: 123, ['a' + 'bc']: 123 }
-
对象扩展方法
Object.assign
、Object.is
// Object.assign() 用于对象的合并 将源对象可枚举属性 复制到目标对象 const target = { a: 1, b: 1 } const source1 = { b: 2, c: 2 } const source2 = { c: 3 } Object.assign(target, source1, source2) target // {a:1, b:2, c:3} // 也可用于对象深拷贝 const obj = { name: 'global obj' } const funcObj = Object.assign({}, obj) funcObj.name = 'func obj' console.log(funcObj) //{ name: 'func obj' } console.log(obj) // { name: 'global obj' } // Object.is用于比较两个值严格相等 与===一致 Object.is('foo', 'foo') // true Object.is({}, {}) // false // 不同之处 +0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
-
Proxy
代理对象// 为对象设置访问代理器 可以理解为在目标对象之前架设一层“拦截” 外界访问该对象 都必须通过这层拦截 // 轻松监视对象的属性读写 const person = { name: 'zce', age: 20, } const personProxy = new Proxy(person, { // 监视属性读取 get(target, property) { // 先判断属性是否存在 return property in target ? target[property] : 'default' // console.log(target, property) // return 100 }, // 监视属性设置 set(target, property, value) { if (property === 'age') { if (!Number.isInteger(value)) { throw new TypeError(`${value} is not an int`) } } target[property] = value // console.log(target, property, value) }, }) personProxy.age = 100 personProxy.gender = true console.log(personProxy.name) // zce console.log(personProxy.xxx) // default
-
Proxy
vsObject.defineProperty()
// Object.defineProperty()只能监视对象中的属性读写 Proxy能监视更多对象操作 const personProxy = new Proxy(person, { deleteProperty (target, property) { console.log('delete', property) delete target[property] } }) delete personProxy.age console.log(person) // { 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) // set 0 100 set length 1 // Proxy 非侵入方式监管对象读写 已定义好的对象 不需要对对象本身做操作 就可监视对象的操作 Object.defineProperty(person,'name',{ get(){ console.log("name 被访问..."); return person._name; }, set(newValue){ console.log("name 被设置 ..."); person._name = newValue; } }) person.name = 'wang'// name 被设置 ... console.log(person.name) // name 被访问 ... wang const personProxy = new Proxy(person, { get(target, property) { return target[property]; }, set(target, property, value) { return (target[property] = value); } });
-
Reflect
// Reflect封装一些列对对象的底层操作 // 现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上 // 价值 提供一套用于操作对象的API const obj = { name: 'zce', age: 18 } console.log('name' in obj) console.log(delete obj['age']) console.log(Object.keys(obj)) // 让Objkect操作都变成函数行为 console.log(Reflect.has(obj, 'name')) console.log(Reflect.deleteProperty(obj, 'age')) console.log(Reflect.ownKeys(obj))、
-
Class
类// class 关键词 // 生成实例对象的传统方法是通过构造函数 function Person(name) { this.name = name } Person.prototype.say = function () { console.log(`hi, my name is ${this.name}`) } class Person { // constructor()方法是类的默认方法 通过new命令生成对象实例时,自动调用该方法 constructor(name) { this.name = name } // 静态方法 不会被实例继承 直接通过类调用 static create(name) { return new Person(name) } say() { console.log(`hi, my name is ${this.name}`) } } const p = new Person('tom') p.say() const xiaoming = Person.create('xiaoming') xiaoming.say() // 类的继承 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() // hi, my name is jack my school number is 100
-
Set数据结构
// 类似于数组,但是成员的值都是唯一的 const s = new Set() [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)); for (let i of s) { console.log(i) } // 作用去除数组的重复成员 const array = [1,2,3,4,2,1,23,4] [...new Set(array)] // [1,2,3,4,23]
-
Map数据结构
// Object对象 键值对集合 只能通过字符串作为键 // Map数据结构 “键”的范围不限于字符串 const m = new Map() const tom = { name: 'tom' } m.set(tom, 90) console.log(m) // Map(1) { { name: 'tom' } => 90 } console.log(m.get(tom)) // 90 // map操作 // 1.转为数组 const arr = [...m] console.log(arr) // [ [ { name: 'tom' }, 90 ], [ { name: 'tom' }, 80 ] ] // 2.数组转为map console.log(new Map([arr])) // Map(1) { [ { name: 'tom' }, 90 ] => [ { name: 'tom' }, 80 ] } // 3.Map转为对象 Map的键被转为字符串 function strMapToObj(strMap) { let obj = Object.create(null) for (let [k, v] of strMap) { obj[k] = v } return obj } console.log(strMapToObj(m)) // [Object: null prototype] { '[object Object]': 80 } 同名属性被覆盖 // 4.对象转Map let obj = { a: 1, b: 2 } let map = new Map(Object.entries(obj)) console.log(map) // Map(2) { 'a' => 1, 'b' => 2 }
-
Symbol
// ES6 引入 原始数据类型Symbol,表示独一无二的值 // 数据类型:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)、Symbol // 对象属性名:一种原有字符串 另一种新增的 Symbol 类型(保证不会与其他属性名产生冲突) const s = Symbol() console.log(s) // Symbol() console.log(typeof s) // symbol // 两个 Symbol 永远不会相等 console.log(Symbol() === Symbol()) // false // 使用 Symbol 为对象添加用不重复的键 const obj = {} obj[Symbol()] = '123' obj[Symbol()] = '456' console.log(obj) // { Symbol(): "123", Symbol(): "456" } // 内置 Symbol 常量 console.log(Symbol.iterator) console.log(Symbol.hasInstance) const obj = { [Symbol.toStringTag]: 'XObject' } console.log(obj.toString()) // Symbol 属性名获取 const obj = { [Symbol()]: 'symbol value', [Symbol()]: 'symbol vaasdlue', foo: 'normal value', } console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(), Symbol() ]
-
Iterator
(遍历器)和for...of
循环// Iterator是一种接口 为所有不同的数据结构提供统一访问机制 只要部署Iterator接口 就可以完成遍历操作 // 只要实现Iterator接口 就可以被for...of消费 // 对象实现可迭代接口 const obj = { store: ['foo','bar','baz'], [Symbol.iterator]: function() { let index = 0 const self = this return { next: function () { const result = { value: self.store[index], down: index >= self.store.length } index++ return result } } } } for (let item of obj) { console.log(item) } // foo bar baz // for...of循环内部调用的是数据结构的Symbol.iterator方法 // for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串 // 与其他遍历语法比较 const arr = [1, 2, 3, 4] for (var i = 0; o < array.length; i++) { console.log(array[i]) } // 写法较麻烦 使用forEach array.forEach((value, index) => { console.log(value, index) }) // 此时 无法跳出forEach循环 break和return命令都不能奏效 for (let value of array) { console.log(value) } // 优点:它可以与break、continue和return配合使用 // 提供了遍历所有数据结构的统一操作接口
-
ES Modules
待补充。。。