一 ECMAScript介绍
ECMAScript只提供了最基本的语法,JavaScript实现了ECMAScript的标准,是ECMAScript的扩展语言。在浏览器环境下,JavaScript就是ECMAScript加上web api(dom、bom);在node环境JavaScript就是ECMAScript加上node apis(fs、net等);JavaScript语言本身就是指ECMAScript;从ECMAScript2015开始,一年一迭代;
任何人都可以向标准委员会(又称 TC39 委员会)提案,要求修改语言标准。
一种新的语法从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由 TC39 委员会批准。
- Stage 0 - Strawman(展示阶段)
- Stage 1 - Proposal(征求意见阶段)
- Stage 2 - Draft(草案阶段)
- Stage 3 - Candidate(候选人阶段)
- Stage 4 - Finished(定案阶段)
ES2015 及之后的版本也称ES6是相对于以前ES5的统称
ES6的入门看参考阮老师的博客https://es6.ruanyifeng.com/
二 常用的ES2015语法
1 块级作用域
let;先声明后引用,不会变量提示;for循环中的循环变量和循环体中变量可以同名,且互不影响;
2 const关键词
const声明时必须赋值;不改变内存地址;
3 数组解构
const arr = [1,2,3,4,5];const a2 = [a,b,c,d,e=100,f = 'default'] = arr;const a3 = [a,...rest] = arr;console.log(a,b,c)console.log(e)console.log(f)console.log(rest)
4 对象解构
const obj = {name: 'jason', age: 20};const {age} = obj;// 解构属性console.log(age);const name = 'tom';// 通过制定属性获取时,重复了,就用别名const {name: objName} = obj;// 默认值const {highSchool = 'NYPD'} = obj;console.log(highSchool);// 场景const {log} = console;log('11')
5 模板字符串-插值表达式
const str = `Hello there, what,s up,there is a \`string\``;// 支持直接换行console.log(str)// 插值表达式const name = 'Lee';const msg = `hello, ${name}, are you ${20 * 1.5} yarns old? you got ${ Math.floor(Math.random() * 5)} children?`;console.log(msg)
6 模板字符串-模板函数
// const str = console.log `Hello World`;const name = 'jason';const gender = true;function myTagFunc (string, name ,gender) {// console.log(string, name, gender)// return '123'const sex = gender?'man':'women';return string[0] + name + string[1] + sex + string[2]}const result = myTagFunc `Hey, ${name} is a ${gender}.`;console.log(result)
7 字符串的扩展方法
includes()、startsWith()、endsWith()、
const msg = 'Error: foo is not defined';console.log(msg.startsWith('Error'),msg.endsWith('defined'),msg.includes('is not'))
8 参数默认值
//判断默认值function foo(flag) {flag = flag === undefined ? true: false;console.log(flag)}// 给定默认值function foo(flag = true, ss) {console.log(flag, ss)}foo(undefined,1)
9 剩余参数
function foo() {console.log(arguments)}// 只能放在形参的最后,且只能用一次function foo1(first,...rest) {console.log(rest)}foo(1,2,3,4,5)foo1(1,2,3,4,5)
10 数组展开
const arr = ['jason', 'rich', 'jim'];// 参数确定console.log(arr[0],arr[1],arr[2],);// 参数不确定console.log.apply(console, arr);console.log(...arr)
11 箭头函数
// function foo(n) {// return n * 1// }const foo = n => n * 1
12 箭头函数this指针 指向当前作用域
const person = {name: 'Jason',say: function () {console.log(`hi, this is ${this.name}`)},sayArrow: () => {console.log(`hi, this is ${this.name}`)},sayAsync: function() {setTimeout(function() {console.log(this.name)}, 1000);},sayAsyncArrow: function() {setTimeout(() =>{console.log(this.name)}, 1000);}}person.say(); // hi, this is Jasonperson.sayArrow() // hi, this is undefinedperson.sayAsync() // undefinedperson.sayAsyncArrow() // Jason
13 对象字面量增强
const bar = '123';const foo = {// bar: barbar, // 省略同名属性// may: function() {// console.log('11')// }may() { // 省略冒号和functionconsole.log('11');console.log(this)},[Math.random()]:'222' // 计算属性}foo.may()
14 对象扩展方法
// Object.assign方法const source = {a:123,b:123}const target = {a: 456,c: 789}Object.assign(target, source);console.log(target) // { a: 123, c: 789, b: 123 }console.log(source) // { a: 123, b: 123 }// Object.is方法console.log(0 == false,0 === false,-0 === +0,Object.is(+0, -0),Object.is(NaN, NaN))
15 代理proxy
const person = {name:'Jason',age: 20}const personProxy = new Proxy(person, {get(target, prop) {console.log(target, prop);// return 100return prop in target ? target[prop]: 'has no prop'},set(target, prop, value) {console.log(target, prop, value);if(prop === 'age') {if(!Number.isInteger(value)) {throw new TypeError(`${value} is not int`)}}target[prop] = value;}});console.log(personProxy.name) // Jasonconsole.log(personProxy.hight) // Jason// personProxy.age = '222'; // TypeError: 222 is not intpersonProxy.age = 22; // { name: 'Jason', age: 20 } age 22personProxy.height = '199cm'; // { name: 'Jason', age: 22 } height 199cm
16 proxy和defineProperty区别
// object.definePropertyconst man = {};Object.defineProperty(man, 'name', {get() {console.log('name visited')return man._name},set(value) {console.log('name setted')man._name = value;}})Object.defineProperty(man, 'age', {get() {console.log('age visited')return man._age},set(value) {console.log('age setted')man._age = value;}});man.name = 'jack';console.log(man.name);// name setted// name visited// jack// ----------------------------------proxy--------------------------// 对象的更多操作可以监视到// 更好的对数组对象的监视 之前想实现必须重写数组的方法// 非侵入的方式监视对象// delete也可以监视到const person = {name:'Jason',age: 20}const personProxy = new Proxy(person, {deleteProperty(target, prop) {console.log('delete ', prop)delete target[prop]}})delete personProxy.age;console.log(person) // { name: 'Jason' }// 对数组更好的监视const list = [];const listProxy = new Proxy(list, {set(target, prop, value) {console.log('set :', prop, value);target[prop] = value;return true; // 表示设置成功}})listProxy.push('sss') // set : 0 sss set : length 1
17 reflect对象,类似math对象,静态类不能通过new新建,内部封装了对对象底层的操作方法; reflect成员方法就是prox处理对象的默认实现
const person = {name: 'Jason',age: 20}const personProxy = new Proxy(person, {get(target, prop) {console.log('watch prop get logic')return Reflect.get(target, prop)}});console.log(personProxy.age);// 提供了统一一套对对象的操作api 13个方法console.log('name' in person);console.log(delete person.name);console.log(Object.keys(person));console.log(Reflect.has(person, 'name'))console.log(Reflect.deleteProperty(person, 'name'))console.log(Reflect.ownKeys(person))
18 class相关
// 以前// es6之前继承 参考博文地址 https://www.cnblogs.com/humin/p/4556820.htmlfunction Person(name, age) {this.name = name; // 通过this访问当前的实例对象this.age = age; // 通过this访问当前的实例对象// 实例方法this.hello = function() {console.log('hello there, my name is ' + this.name)}}// 原型方法// 如果要给当前所有的实例之间共享成员,需要借助成员的原型prototypePerson.prototype.say = function() {console.log(`I am ${this.age} yarns old`)}// 继承// 1 将父类的实例作为子类的原型function Man (){};Man.prototype = new Person();Man.prototype.name = 'man';Man.prototype.age = 20;var man = new Man();console.log(man.name)console.log(man.say())console.log(man.hello())console.log('---------------');// 2 使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)function Women(name, age) {Person.call(this);this.name = name||'women';this.age = age || 18;}var woman = new Women();console.log(woman.name)console.log(woman.hello())console.log('---------------');// console.log(woman.say()) // 报错// 3 实例继承 为父类实例添加新特性,作为子类实例返回function NormalMen (name, age) {var instance = new Person();instance.name = name || 'normalMen';instance.age = age || 18;return instance;}var normalMen = new NormalMen();console.log(normalMen.name)console.log(normalMen.hello())console.log(normalMen.say())console.log('---------------');// 4 拷贝继承function innormalMen(name) {var person = new Person();for(var key in person) {innormalMen[key] = person[key]}this.name = name|| 'Tom';}var innormalMen = new innormalMen();console.log(innormalMen.name)// console.log(innormalMen.say()) // 不可枚举方法不存在// console.log(innormalMen.hello()) // 不可枚举方法不存在console.log('---------------');// 5 组合继承 通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用function WhiteMan(name) {Person.call(this);this.name = name || 'jim';}WhiteMan.prototype = new Person();WhiteMan.prototype.constructor = WhiteMan; // 修复构造函数指向var whiteMan = new WhiteMan('white');console.log(whiteMan.name)console.log(whiteMan.hello())console.log(whiteMan.say())// 6 寄生组合继承 通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点function YellowMan(name){Person.call(this);this.name = name || 'Tom';}(function(){// 创建一个没有实例方法的类var Super = function(){};Super.prototype = Person.prototype;//将实例作为子类的原型YellowMan.prototype = new Super();})();console.log('---------------------------------------------------------------');// classclass Student {constructor(name) {this.name = name;}// 实例方法say() {console.log(`my name is ${this.name}`)}// 静态方法static create(name) {return new Person(name)}}// 继承class GirlStudent extends Student {constructor(name, num) {super(name); // 调用父类的构造函数this.num = num;}hello() {super.say();console.log(`my student number is ${this.num}`)}}const s = new GirlStudent('lili', 2222);s.hello()
19 set对象
const s = new Set();// 添加s.add(1).add(2).add(3).add(4).add(2);console.log(s) // Set { 1, 2, 3, 4 }// 遍历s.forEach(i => {console.log(i)});// 1// 2// 3// 4// 长度console.log(s.size)// 4// 删除console.log(s.delete(3))// true// 清除s.clear();console.log(s)// Set {}const arr = [1,2,3,4,2,1]// 去重const aa = new Set(arr);// 生成新数组const rr = Array.from(new Set(arr))// 解构新数组const dd = [...new Set(arr)]console.log(aa)// Set { 1, 2, 3, 4 }console.log(rr)// [ 1, 2, 3, 4 ]console.log(dd)// [ 1, 2, 3, 4 ]
20 map对象
// map 可以用任意类型的数据作为keyconst m = new Map();const tom = {a :'1'};// 添加m.set(tom,10);console.log(m) //Map { { a: '1' } => 10 }// 获取console.log(m.get(tom)) // 10// 删除m.delete(tom);console.log(m) // Map {}m.set(tom,20);// 查找console.log(m.has(tom)) // true// 遍历m.forEach((value, key) => {console.log(value, key)})// 20 { a: '1' }// 清空m.clear();console.log(m) // Map {}
20 symbol数据类型
js中基本的数据类型 string, number, boolean, null, undefined, object, symbol, bigInt(es2019)
// symbol 数据类型 表示一个独一无二的值 主要用在给对象添加一个唯一属性名// 基本的数据类型 string, number, boolean, null, undefined, object, symbol, bigInt(es2019)const cache = {};// 在a.js中 定义一个对象值cache['foo'] = Math.random();// 在b.js中 定义同名的值导致会被覆盖cache['foo'] = '123';console.log(cache) // { foo: '123' }const ss = Symbol();console.log(ss);console.log(typeof ss)// Symbol()// symbolconsole.log(Symbol('a'))console.log(Symbol('b'))console.log(Symbol('c'))// Symbol(a)// Symbol(b)// Symbol(c)const a = {};// 以symbol为keya[Symbol()] = '1'a[Symbol()] = '2'console.log(a)// { [Symbol()]: '1', [Symbol()]: '2' }// 计算属性const obj = {[Symbol()]: 123};console.log(obj)// { [Symbol()]: 123 }// 私有成员const name = Symbol();const person = {[name] : 'Jason',say() {console.log(this.name)}}person.say(); // undefinedconsole.log(Symbol() === Symbol(), // falseSymbol('foo') === Symbol('foo'), // false)// 相同的symbol值 for方法接受字符串,如果true和'true',则是同一个const s1 = Symbol.for('foo')const s2 = Symbol.for('foo')console.log(s1 === s2) // true// 内置的symbol常量console.log(Symbol.hasInstance) // Symbol(Symbol.hasInstance)console.log(Symbol.iterator); // Symbol(Symbol.iterator)// 自定义对象标签const xobj = {[Symbol.toStringTag] : 'XObject'}console.log(xobj.toString()) // [object XObject]// symbol 定义的key是无法用for...in循环得到的 无法用Object.keys获取,也会被JSON.stringify忽略const yobj = {[Symbol()]: 'Symbol',foo: 11}for(let key in yobj) {console.log(key)}console.log(Object.keys(yobj))console.log(JSON.stringify(yobj))// 想要获取 getOwnPropertySymbol() 只能获取到symbol属性console.log(Object.getOwnPropertySymbols(yobj))
21 for…of循环
// for...of循环是一种数据遍历的统一方式const arr = [100, 200, 300, 400];for (let item of arr) {console.log(item);if (item > 100) {break;}}// 100// 200// forEach无法breakconsole.log('---------------')const s = new Set(arr);for(const item of s) {console.log(item)}// 100// 200// 300// 400console.log('---------------')const m = new Map();m.set('top', 1)m.set('bottom', 2)for(const item of m) {console.log(item)}// [ 'top', 1 ]// [ 'bottom', 2 ]for(const [key, value] of m) {console.log(key, value)}// top 1// bottom 2console.log('---------------')const o = {a: 1, b: 2, c: 3};for(const item of o) {console.log(item)}// TypeError: o is not iterable
22 可迭代接口 实现了iterable接口就可以被for…of遍历;在之前的可以被for…of循环例子中,这些对象原型方法上都有Symbol.iterator方法
const set = new Set(['Jason', 'Jhon', 'Jim', 'Tom']);const iterator = set[Symbol.iterator]();console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());// { value: 'Jason', done: false }// { value: 'Jhon', done: false }// { value: 'Jim', done: false }// { value: 'Tom', done: false }// { value: undefined, done: true }// { value: undefined, done: true }// 如何实现可迭代接口const obj = { // iterablestore: ['Jason', 'Jhon', 'Jim', 'Tom'],[Symbol.iterator]: function() {let index = 0;const self = this;return { // iteratornext: function() {const result = { // iterationResultvalue: self.store[index],done: index>=self.store.length}index ++;return result;}}}}for(const item of obj) {console.log(item)}
23 迭代器的意义
// 迭代器接口的意义const todoList = {today: ['1','2','3','4'],tomorrow: ['6','7','8','9'],afterTomorrow: ['11','12','13','14'], // 后续增加了forEach: function(callback) { // 提供统一forEach接口const arr = [].concat(this.today, this.tomorrow,this.afterTomorrow)for(const item of arr) {callback(item)}},[Symbol.iterator]: function() {const arr = [...this.today, ...this.tomorrow, ...this.afterTomorrow];let index = 0;return {next: function() {return {value: arr[index],done: index++>=arr.length}}}}}// 要遍历for(const item of todoList.today) {console.log(item)}// 要遍历for(const item of todoList.tomorrow) {console.log(item)}// 此时如果todoList中增加了afterTomorrow就必须重新写for(const item of todoList.tomorrow) {console.log(item)}// 如果添加了统一的forEach方法就可以减少耦合todoList.forEach(function(item){console.log(item)});console.log('------------------------------')// 用迭代器实现for(const item of todoList) {console.log(item)}
24 生成器函数 generator
// 生成器函数function* foo() {console.log('foo start')return 100}// 第一次执行初始化一个生成器对象const result = foo();console.log(result) // Object [Generator] {}console.log(result.next())console.log('------------------------')// foo start// { value: 100, done: true }// 调用生成器对象的next方法,才会走下一步,生成器函数也实现了iteratable接口// 配合yield使用function* fn() {console.log('1')yield 100;console.log('2')yield 200;console.log('3')yield 300;}const fnResult = fn();console.log(fnResult.next())console.log(fnResult.next())console.log(fnResult.next())console.log(fnResult.next())console.log('------------------------')// 1// { value: 100, done: false }// 2// { value: 200, done: false }// 3// { value: 300, done: false }// { value: undefined, done: true }// 惰性执行// 案例1function* createIdMaker() {let id = 0;while(true) {yield id++;}}const idMaker = createIdMaker();console.log(idMaker.next())console.log(idMaker.next())console.log(idMaker.next())console.log(idMaker.next())console.log('------------------------')// 案例2const todoList = {today: ['1', '2', '3', '4'],tomorrow: ['6', '7', '8', '9'],afterTomorrow: ['11', '12', '13', '14'], // 后续增加了forEach: function (callback) {// 提供统一forEach接口const arr = [].concat(this.today, this.tomorrow, this.afterTomorrow);for (const item of arr) {callback(item);}},// [Symbol.iterator]: function() {// const arr = [...this.today, ...this.tomorrow, ...this.afterTomorrow];// let index = 0;// return {// next: function() {// return {// value: arr[index],// done: index++>=arr.length// }// }// }// }// 用生成器函数代替[Symbol.iterator]: function* () {const arr = [...this.today, ...this.tomorrow, ...this.afterTomorrow];for(const item of arr) {yield item}},};for(const item of todoList) {console.log(item)}
25 ES modules 模块化
26 es2016
const arr = ['Jason', 'Tom', 'Jim', 'Rich'];// 判断是否存在foo 是否等于-1console.log(arr.indexOf('foo')); // -1console.log(arr.indexOf('Tom')); // 1// 在es2016中年引入了includesconsole.log(arr.includes('foo')); // falseconsole.log(arr.includes('Tom')); // true// 指数运算let a = 2;console.log(Math.pow(a, 10)); // 1024// es2016中引入了** 为指数运算符console.log(2 ** 10); // 1024
27 es2017
const foo = {name: 'Jason',age: 20}// 1 Object.valuesconsole.log(Object.values(foo)) // [ 'Jason', 20 ]// 2 Object.entriesconsole.log(Object.entries(foo)) // [ [ 'name', 'Jason' ], [ 'age', 20 ] ]// 延伸 普通对象的for...of循环for(const [key, value] of Object.entries(foo)) {console.log(key, value)}// name Jason// age 2// 普通对象转换为map对象console.log(new Map(Object.entries(foo)))//Map { 'name' => 'Jason', 'age' => 20 }// 3 Object.getOwnPropertyDescriptionconst p1 = {firstName: 'Lee',lastName: 'Jason',get fullName() {return this.firstName + '.' + this.lastName}}console.log(p1.fullName) // Lee.Jason// 用Object.assign复制属性后重置const p2 = Object.assign({}, p1)p2.lastName = 'Tom'// Object.assign 不能复制get/set属性console.log(p2) // { firstName: 'Lee', lastName: 'Tom', fullName: 'Lee.Jason' }// 如何解决?const des = Object.getOwnPropertyDescriptors(p1);// console.log(des);const p3 = Object.defineProperties({}, des);p3.lastName = 'Jim';console.log(p3.fullName) // Lee.Jim// 4 字符串方法 padStart/padEndlet a = '>';let b = '<';const logTitle = 'request log'console.log(`${a.padStart(16,'-')} ${logTitle} ${b.padEnd(16,'-')}`)// ---------------> request log <---------------// 5 数组末尾允许添加尾逗号const arr = [1,2,3,4,5,]// 6 async await
