转码器:babel,允许用ES6的语法来写程序,而不用考虑现有环境是否支持。
EcmaScrip是规范,JavaScript是实现。
Es6包含ecma2015 ecma2016 ecma2017,一般是指ecma2015。
let 和 const
顶层对象的属性:
ES5 顶层对象的属性与全局变量是等价的。
ES6 开始全局变量不都是顶层对象的属性了,只有var、function定义的才算,let、const、class定义的不算顶层对象的属性。
const与let一样:
- 块级作用域内有效
- 声明不会提升,需先声明再使用(暂时性死区)
- 不允许重复声明
- const声明只读常量,声明对象时指对象地址不可变,但对象内容可改变(需立即初始化)
- 另:对象冻结可使用Object.freeze( obj )。
ES5声明变量有2种方式:var + function
ES6声明变量有6种方式:var + function + let + const + import + class
ES5只有全局+函数作用域,无块级作用域(导致:内部变量覆盖外部变量、for计数变量外泄为外部变量等)
ES6 let只在块级作用域内有效,可以替换立即执行函数了IIFE。{let a;…..}
ES5不允许在块级作用域中声明函数,但是浏览器真正实现是:允许声明函数,而且会提升至作用域前面。
ES6明确允许在块级作用域中声明函数,而且浏览器环境和非浏览器环境表现不同:
- 浏览器环境:相当于var声明,会提升(提升至所在全局||函数作用域头部),赋值延后(函数声明会提升至所在块级作用域头部)。
- 非浏览器环境:相当于let声明,不会提升,只在当前块级作用域有效。
所以:为了表现一致,尽量不在块级作用域内声明函数,若必须声明,则使用 函数表达式方式 ,而非函数声明方式。
let为js新增了块级作用域,声明变量,只在当前代码块内有效。
立即执行函数表达式IIFE(Immediately-Invoked Function Expression):(function(){})();
变量的解构赋值
解构赋值的规则:只要等号右边不是对象,就现将其转换成对象。
解构赋值:数组、对象、字符串、数值、布尔值、函数参数等的解构赋值。
** 对象的解构赋值 **
模式不会被赋值,被赋值的是模式后面的变量:
let {loc: first, glo:last} = {loc:123, glo: 456};//loc和glo是模式,first和last才是真正赋值的变量
对于已经声明的变量进行解构赋值时,需要加上小括号,因为不加的话,js会将行首的花括号解释为代码块,引起语法错误。
let x;
{x} = {x:1}; //SyntaxError: syntax error
({x} = {x:1}); //正确
解构赋值的几个好处:
- 提取JSON数据的值;
- 函数参数定义默认值;
- 提取某对象或模块的方法;
字符串的扩展
对字符串进行操作的时候,要注意常规字符(两字节)和辅助字符(四字节)的区别。
unicode是世界上所有的字符集,是一种二进制的表示方法,但是并没有规定存储方式。UTF-8和UTF-16、UTF-32就是其具体的存储实现,具体区别可以阅读阮一峰老师的两篇文章,参考:
- http://www.ruanyifeng.com/blog/2014/12/unicode.html
- http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
字符串的扩展重点:
- 码点大括号表示法: '\u{20BB7}'
- 对于传统for循环无法识别的大于0xFFFF的码点,for...of可以正确识别
- normalize() repeat() includes() startsWith() endsWith()等函数
- 模板字符串:`xxxxxx ${variable} yyy ${func()}` //引入变量时使用${}
正则的扩展
正则表达式新增flags属性,返回修饰符
- ES5:/abc/ig.source 返回正文abc
- ES6:/abc/ig.flags 返回修饰符ig
- 新增修饰符u和y,u代表可以处理unicode大于0xFFFF的字符,y类似于g,只是y是从剩余字符中的第一个位置开始匹配,g是只要剩余字符中匹配即可。
- /abc/ig.sticky是否设置了粘连属性y
- 提案:RegExp.escape()函数对字符串进行转义
- 提案:/abc/s.dotAll属性,修饰符/s:点代表一切字符
数值的扩展
- 二进制和八进制表示法(0B和0O)
- 新增一些方法:Number.isNaN() .isFinite() .parseInt() .parseFloat() .isSafeInteger()
- 未解决js浮点运算的bug,引入一个极小常量Number.EPSILON
- Math对象扩展了一些函数:.trunc() .sign()等
数组的扩展
- 增加一些方法:Array.from() 将类数组转换成数组;Array.of()将一组值转换成数组,解决Array构造函数参数不等的歧义问题
- [].find() .findIndex() .entries() .keys() .values() .includes()等方法
- 尽量避免出现空位,防止表现不一致引起问题
JS中数组的迭代方法:
方法 | 举例 |
---|---|
forEach:对数组中的每一项执行操作 | arr.forEach(function(v, i, arr){return v+1;}) |
every:每一项都满足某条件则返回true | if(arr.every(function(v,i,arr){return v > 2;})){} |
some:只要有一项满足某条件则返回true | if(arr.some(function(v,i,arr){return v > 2;})){} |
map:对数组每一项进行操作,返回数据组成新数组返回 | var newArr = arr.map(function(v, index, arr){return v*2;}); |
filter: 对数组进行过滤,满足条件的项组成新数组返回 | var newArr = arr.filter(function(v, index, arr){return v > 10;}); |
reduce:对数组的相邻两项进行操作 | var sum = arr.reduce(function(sum, item){return sum+item;}, 0) |
includes:数组是否包含某值 | if(arr.includes(2)){} |
函数的扩展
参数默认值:双重默认值 function({x=0,y=0} = {}){...},尽量将定义了默认值的参数放于参数的尾部,方便省略
(function(a,b,c=1){}).length //2 返回函数的预期参数个数(不含指定了默认值的参数和rest参数)
rest参数:只能是尾参数
扩展运算符:... 将数组转换成逗号分隔的数据序列
箭头函数=>:this指向定义时的作用域(this始终指向定义时的this,而非运行时的this)
函数绑定运算符(双冒号** ::**)obj::method 绑定this
尾调用、尾递归调用(先做了解):尾调用-函数的最后一步是调用另一个函数,内部函数可以不用再维护外部函数的调用栈信息。
对象的扩展
- 属性名表达式 obj['a'+'bc'] = 111; 简洁属性表示法 let obj = {x, y};
- Object.keys() .values() .entries()
- Null传导运算符** ?:** 判断对象是否存在,防止报错 person?.user?.p1?.age
- 对象的扩展运算符** ...** 单纯的解构赋值可以读取对象的继承属性,扩展运算符的解构赋值只能读取对象自身的属性
var o = Object.create({ x: 1, y: 2 });//x y算继承属性
o.z = 3;
let { x, ...m } = o;
x // 1---单纯的结构赋值
m // {z: 3} --- 扩展运算中的解构赋值
- 对象属性的遍历5种方法
for...in //返回对象自身和继承的可枚举属性
Object.keys() //返回对象自身可枚举属性
Object.getOwnPropertyNames() //返回对象自身所有属性(包括不可枚举)
Object.getOwnPropertySymbols() //返回对象自身Symbols属性
Reflect.ownKeys() //返回对象自身symbol类型和字符串类型的属性(不管是否可枚举)
方法 | 适用范围 | 描述 |
---|---|---|
for...in | 数组、对象 | 获取可枚举的实例和原型属性名(返回键名) |
Object.keys() | 数组、对象 | 返回可枚举的实例属性名组成的数组 |
Object.getOwnPropertyNames() | 数组、对象 | 返回除原型属性以外的所有属性(包括不可枚举的属性)名组成的数组 |
for…of | 可迭代对象(Array Map Set等) | 返回属性值(返回键值) |
Reflect.ownKeys() | 对象自身的所有属性,包含symbols属性和不可枚举属性 |
- object.is(p1, p2):同值判断 基本等同于全等===,不同:
object.is(+0, -0);//false
object.is(NaN, NaN);//true - Object.assign(target, source1, source2):对象合并,浅拷贝,只拷贝对象自身的属性,继承(prototype上的属性)+不可枚举的属性都不会拷贝到。
首参不能是null、undefined,否则报错;
处理数组时:
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3] 按照下标进行覆盖合并
Object.create(原型 | null,实例属性设置):只有显示的指定属性设置的enumrabe等属性才可以。属于ES5
Symbol
ES6新增原始数据类型Symbol,表示独一无二的值(对象属性名可以保证不会冲突)。Symbol给我感觉用处不是很顺畅,很别扭的感觉。
- Symbol(str):参数表示对Symbol实例的描述,不能用new(不是对象);Symbol值不能进行隐士字符串转换,只能显示转换String(Symbol('aa')) or Symbol('aa').toString(),也不能转换成数值,但是可以进行布尔换算
- Symbol值作为属性名时,只能用[]引用,不能用点.获取(用点默认代表字符串属性)
- Symbol.for('aa') 是全局登记的值:每次会先搜索给定参数的值是否存在,如果已经存在,则返回,否则新建一个值返回。Symbol.keyFor(Symbol.for('aa'))返回Symbol.for()注册的值的描述。
Symbol('aa') === Symbol('aa') //false
Symbol.for('aa') === Symbol.for('aa') //true
Symbol.keyFor(Symbol.for('aa')) //'aa'
Symbol.keyFor(Symbol('aa'))//undefined
- 定义了一些内置的Symbol值,用于指向语言内部的方法
Map和Set数据结构
数据集合:ES5 -> Array+Object ,ES6 -> Map+Set
--- Set
类似于没有重复值的数组。键名和键值完全一样。
//操作方法 add delete has clear
var set = new Set([1,2,3,2,3]);//[1,2,3]
set.add(4);//[1,2,3,4]
set.size;// 4
set.delete(1);
set.has(2);
set.clear();//清空
//遍历方法 .keys() .values() .entries() .forEach()
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
set.forEach((value,key,set) =>console.log(value+':'+key) );
--- WeakSet
与set类似,但是只保存对象类型的数据,是对象类型数据的弱引用,目的是防止内存泄漏。常用方法有:add has delete,无法遍历
--- Map
// 操作方法 set get has delete clear
var map = new Map([ ['name', 'fss'], ['age', 27] ]);
map.set('job', 'computer').set('xx', 'aa');
map.get('xx');//'aa'
map.size;//3 条目个数
map.has('job');//true
//遍历方法 map.keys() .values() .entries() forEach()
--- WeakMap
与Map结构类似,键名只能是对象类型,弱引用,防止内存泄漏。常用方法:get set has delete,无法遍历。
Proxy
对对象的一层代理拦截,修改对象操作的默认行为。
//handler为{}时,代表无拦截效果;handler内部定义了一些内置的拦截方法:get set apply construct等
var proxy = new Proxy(target, handler);
var handler = {
get: function(target, name) {
if (name === 'prototype') {
return Object.prototype;
}
return 'Hello, ' + name;
},
apply: function(target, thisBinding, args) { //一般函数调用时拦截函数
return args[0];
},
construct: function(target, args) {//构造函数调用时拦截函数
return {value: args[1]};
}
};
var fproxy = new Proxy(function(x, y) {
return x + y;
}, handler);
fproxy(1, 2) // 1
new fproxy(1,2) // {value: 2}
fproxy.prototype === Object.prototype // true
fproxy.foo // "Hello, foo"
- Proxy.revocable 目标对象不允许直接访问,只能通过代理访问,访问结束,收回代理权,不允许再访问
- Proxy代理时,目标对象内部的this指向proxy代理对象
Reflect
- 与Proxy类似,Proxy handler中有的方法Reflect也都有,只不过Proxy是自定义拦截行为,Reflect实现的是对象的原生行为。
- 观察者模式:函数自动观察数据对象,一旦数据对象发生变化,函数自动执行。(懵懵懂懂???)
Promise
- 异步编程的一种解决方案(传统方法:回调函数和事件),可以这样理解Promise:一个容器,保存着异步编程操作的结果(未来完成)。
- 状态有2种,要么成功resolved || fulfilled,要么失败rejected
- 提供一些API:
var promise = new Promise(resolve, rejected);//新建后立即执行,无法中断
promise.then(resolveFunc, rejectedFunc)
.then(resolveFunc)//链式写法
.catch(function(){})//状态为rejected时执行,resolveFunc执行出错时也执行
- Promise.all([p1,p2,p3])所有参数状态都为resolved时,整体才为resolved;有一个失败,则整体rejected
- Promise.race([p1,p2,p3])只要有一个实例状态改变,则整体状态改变,执行相应处理函数
- Promise.resolve() .rejected() 将参数先转换为Promise对象,再执行相关函数
- .done() .finally() 区别是:finally不管怎样都会执行,done总是在回调链的尾端,保证抛出任何错误(捕捉错误的)。
- Promise.try() 模拟try模块,尝试运行同步函数 Promise.catch()
遍历器Iterator接口
为各种不同的数据结构(Array、Object、Map、Set)提供一种统一的访问机制(即for...of)。只要部署了Symbol.iterator(默认指向该对象的遍历器接口,必须要有next方法)属性的数据结构,就被称为部署了遍历器接口。
Generator函数
ES6提供的异步编程解决方案,可以理解成一个状态机,封装了多个内部状态。
- 语法
function* generatorFunc(){//带有星号*
//some code
yeild 'status1';//函数内部可以用yeild定义状态
//some code
yeild 'status2';
//some code
yeild 'status3';
//some code
return 'status4'; //可以没有
}
var geneFunc = generatorFunc();
geneFunc.next();//{value:'status1', done: false}
geneFunc.next();//{value:'status2', done: false}
geneFunc.next();//{value:'status3', done: false}
geneFunc.next();//{value:'status4', done: true}
geneFunc.next();//{value:undefined, done: true}
- Generator函数像正常函数一样调用,但是函数并不是立即执行,调用生成函数后,返回一个遍历器对象,第一次调用next方法,函数从头开始执行到yeild语句,返回一个状态;后续再调用next,从上次停止的地方开始执行至下一个yeild或者函数最后。(提供函数暂停执行)
- yeild和return的异同:都可以返回紧跟其后的表达式的值;但是yeild不能用在普通函数中,只能在生成器函数中使用;return只执行一次,yeild可以执行多次;
- yeild用在表达式中,必须使用圆括号:不知道有啥用???
console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK
foo(yield 'a', yield 'b'); // OK 函数参数和赋值时,可以不用圆括号
let input = yield; // OK
- yeild语句本身没有返回值,每次返回undefined(var x = yeild 123;//x是undefined);next方法可以传入一个参数,当做 (上一次) yeild语句的返回值,好处:可以在生成函数运行之后,再次向函数内注入值。
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
二十三:SIMD
(目前JS引擎基本不支持,可以仅作了解)
SIMD:单指令 多数据(single instruction / mutiply data)
SISD:单指令 单数据(single instruction / single data)--- JS默认运算模式
SIMD是js操作CPU对应指令的接口,完成多个数据的运算,广泛用于3D图形运算、物理模拟等运算量超大的项目之中。
SIMD提供12种数据类型,总长度都是128个二进制位。
Float32x4:四个32位浮点数
Float64x2:两个64位浮点数
Int32x4:四个32位整数
Int16x8:八个16位整数
Int8x16:十六个8位整数
Uint32x4:四个无符号的32位整数
Uint16x8:八个无符号的16位整数
Uint8x16:十六个无符号的8位整数
Bool32x4:四个32位布尔值
Bool16x8:八个16位布尔值
Bool8x16:十六个8位布尔值
Bool64x2:两个64位布尔值
每种数据类型被x符号分隔成两部分,后面的部分表示通道数,前面的部分表示每个通道的宽度和类型。比如,Float32x4就表示这个值有4个通道,每个通道是一个32位浮点数。
利用SIMD的一些静态方法,可以完成一些运算,具体就不展开介绍了,可以参考链接:http://es6.ruanyifeng.com/#docs/simd