ECMAScript规范
ECMAScript是一种脚本语言规范,从ES6开始广泛使用
新的规范不一定所有JS引擎都能支持,所以出现了Babel用于将ECMAScript2015+版本的代码转换为向后兼容的JavaScript语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
Babel通过语法转换器来支持新版本的JavaScript语法,这些插件让你现在就能使用新的语法,无需等待浏览器的支持
Web标准与ECMAScript规范
Web标准是关于Web技术领域的一系列标准,包括HMTL、CSS、DOM、媒体、性能、WebAPI、安全、无障碍、图形等。ECMAScript则是一种语规范,JavaScript是ECMAScript的一种实现和扩展。
ECMAScript和JavaScript
1996年初网景推出了JavaScript,1996年11月网景正式向ECMA(欧洲计算机制造商协会)提交语言标准。1997年6月,ECMA以JavaScript语言为基础制定了ECMAScript标准规范ECMA-262。JavaScript成为了ECMAScript最著名的实现之一。
除此之外,ActionScript和JScript也都是ECMAScript规范的实现语言。
所以是先有JavaScript,后有ECMAScript规范。ECMAScript规范是基于JavaScript制定的,JavaScript是ECMAScript规范的实现。但ECMAScript只是描述语言的语法和基本对象,JavaScript除了核心语言部分,还包括BOM和DOM的WebAPI部分。
JavaScript由三部分组成
1.ECMAScript(核心):规定了语言的组成部分=>语法、类型、语句、关键字、保留字、操作符、对象
2.BOM(浏览器对象模型):支持访问和操作浏览器窗口,可以控制浏览器显示页面以外的部分。
3.DOM(文档对象模型):把整个页面映射为一个多层节点结果,可借助DOM提供的API,可删除、添加和修改任何节点
什么是ES5?
ES5全程ECMAScript5,即ES5,是ECMAScript的第五次修订(第四版因为过于复杂废弃了),又称ECMAScript2009,于2009年完成标准化。
什么是ES6?
ES6,全称ECMAScript6.0,即ES6,是ECMAScript的第六次修订,又称ES2015,于2015年06月发版,是JavaScript的下一个版本标准。
ES6主要是为了解决ES5的先天不足,目前浏览器的JavaScript是ES5版本。大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能。ES6是继ES5之后的一次改进,相对于ES5更加简洁。提高了开发效率
ES5特性
1.stric模式:严格模式(developer.mozilla.org/zh-CN/docs/…)限制了一些用法
2.Array增加方法:有every、some、forEach、filter、indexOf、lastIndexOf,isArray、map、reduce、reduceRight方法。
3.Object方法:Object.getPrototypeOf、Object.create等方法。
- Object.getPrototypeOf - 方法返回指定对象的原型
- Object.create - 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型
- Object.getOwnPropertyNames - 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。
- Object.defineProperty - 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
- Object.getOwnPropertyDescriptor - 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
- Object.defineProperties - 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
- Object,keys - 法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。
- Object.preventExtensions / Object.isExtensible - 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
- Object.seal / Object.isSealed - 方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
- Object.freeze / Object.isFrozen - 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。
freeze()返回和传入的参数相同的对象。
ES6特性
1.块级作用域 => 关键字 let,常量const
在ES6中通常用let和const来声明,let表示变量、const表示常量
特点:let和const都是块级作用域。以{}代码块作为作用域范围只能在代码块里面使用,不存在变量提升,只能先声明再使用,否则会报错。在代码块内,在声明变量之前,该变量都是不可用的,这在语法上,称为“暂时性死区”,在同一个代码块内,不允许重复声明。
const声明的是一个只读常量,在声明时就需要赋值。(如果const的是一个对象,对象所包含的值是可以被修改的。抽象点说,就是对象所指向的地址不能改变,而变量成员是可以修改的)
2.对象的扩展
属性和方法的简写
对象字面量属性的简写
ES6允许在对象之中,直接写变量,这时,属性名为变量名,属性值为变量的值
let foo = "bar";
let baz = { foo }; // {foo: "bar"}
对象字面量方法的简写。省略冒号与function关键字
let o = {
method() {
return "Hello!";
}
};
==>
let o = {
method:function() {
return "Hello!";
}
}
Object.keys()
获取对象的所有属性名或方法名(不包括原型的内容),返回一个数组。
let obj = {
name: 'john',
age: 21,
getName: function() {
alert(this.name)
}
};
Object.keys(obj) ==> ["name","age","getName"];
Object.keys(obj).length ==> 3;
Object.keys(['aa','bb','cc']) ==> ["0","1","2"];
Object.keys('abcdef') ==> ['0',"1","2","3","4","5"];
Object.assign()
assign方法将多个原对象的属性和方法都合并到了目标对象上面。可以接受多个参数,第一个参数是目标对象,后面的都是源对象
let target = {}; //目标对象
let source1 = {name: 'ming', age: "19"};//源对象1
let source2 = {sex: '女'};//源对象2
let source3 = {sex: '男'};//源对象3,和source2中的对象有同名属性sex
Object.assign(target,source1,source2,source3);
console.log(target) // {name: 'ming', age: '19', sex: '男'};
3.解构赋值
数组的解构赋值
解构赋值是对赋值运算符的扩展。是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。在代码书写上简洁且易读,语义更加清晰明了,也方便了复杂对象中数据字段获取。数组中的值会被解析到对应接收该值的变量中,数组的解构赋值要一一对应如果有对应不上的都是undefined
let [a, b, c] = [1, 2, 3]; // a = 1 b = 2 c = 3对象的解构赋值
对象的解构赋值和数组的解构赋值其实类似,但是数组的数组成员是有序的,而对象的属性则是无序的,所以对象的解构赋值简单理解是等号的左边和右边的结构相同。
let {foo, bar } = {foo: 'aaa', bar: 'bbb'}; // foo = 'aaa' bar = 'bbb' let {baz: foo} = {baz: 'ddd'} // foo = 'ddd';
4.展开运算符
展开运算符,允许一个表达式在某处展开,展开运算符在多个参数(用于函数调用)或多个元素(用于数组字面量)或者多个变量(用于解构赋值)的地方可以使用。
let obj1 = { value1: 1, value2: 2 }; let obj2 = {...obj1}; cosnole.log(obj2) // {value1: 1, value2: 2}展开运算符的写法与obj2 = obj1直接赋值的写法的区别在于如果直接赋值的话,对于引用类型来说,相当于只是赋值了obj1的内存空间地址,当obj2发生改变的时候,obj1也会随着发生改变,而使用展开运算符写法的话,由于obj1对象中的属性类型都是基本类型,相当于重新创建了一个对象,此时obj2发生改变的时候,并不会影响obj1这个对象,但是仅限于其属性都为基本类型的情况(或者说只进行了一层的深拷贝),如果该对象中的属性还有引用类型的话,修改属性中引用类型的值,则两个对象的属性值都会被修改。
let obj1 = { attri1: [3, 6, 0], attri2: 4, attri3: 5 }; let obj2 = {...obj1}; obj2.attri2 = 888; obj2.attri1[0] = 7; console.log(obj1) // {attri1: [7, 6, 0], attri2: 4, attri3: 5} console.log(obj2) // {attri1: [7, 6, 0], attri2: 888, attri3: 5}展开运算符的应用
1.在函数中使用展开运算符
function test(a, b, c){}; let arr = [1, 2, 3]; test(...arr);2.在数组字面量中使用展开运算符
let arr1 = [1, 2]; let arr2 = [...arr1, 3, 4]; // [1,2,3,4] // 使用push方法 let arr1 = [1, 2]; let arr2 = [3, 4]; arr1.push(..arr2) // [1,2,3,4]3.用于解构赋值,解构赋值中展开运算符只能用在最后,否则会报错
let [a, b, ...c] = [1, 2, 3, 4]; console.log(a ,b, c) // 1, 2, [3,4];4.类数组变成数组
let oLis = docuemnt.getElementByTagName("li"); let liArr = [...oLis];5.对象中使用展开运算符
ES7中的对象展开运算符可以让我们更快捷地操作对象:
let {x, y, ...z} = {x:1, y:2, a:3, b:4};\ x; // 1 y; // 2 z; // {a:3,b:4}将一个对象插入另外一个对象当中
let z = {a:3, b:4}; let n = {x:1, y:2, ...z}; cosnole.log(n); // {x:1, y:2, a:3, b:4}合并两个对象:
let a = {x:1, y:2}; let b = {z:3}; let ab = {...a, ...b}; console.log(ab); // {x:1, y:2, z:3};
5.函数的扩展
函数的默认参数
ES6为参数提供了默认值。在定义函数时便初始化了这个参数,以便在参数没有被传递进去时使用。
箭头函数
在ES6中,提供了一种简洁的函数写法,我们称作“箭头函数”
写法:函数名=(形参)=>{.....} 当函数体只有一个表达式时,{}和return 可以省略
当函数体中只有一个时,()可以省略。
特点:箭头函数中的this始终指向箭头函数定义时的离this最近的一个函数,如果没有最近的函数就指向window
6.模板字符串
用一对反引号`标识,它可以当作普通字符串使用,也可以用来定义多行字符串,也可以在字符串中嵌入变量,js表达式或函数、变量、js表达式或函数需要写在${}中。
let name = "Bab", time = 'today'; `Hello ${name}, how are you ${time}?` // return "Hello Bob, how are you today?"
7.for...of循环
let arr = ['小林',"小吴",'小佳'];
for(let key of arr){
console.log(key) // 小林 小吴 小佳
}
8.Class类,有constructor、extends、super 本质上是语法糖,(对语言的功能并没有影响,但是更方便程序员使用)。
class Artist {
constructor(name){
this.name = name;
}
perform(){
return this.name + " performs";
}
}
class Singet extends Artist {
constructor(name, song){
super.constructor(name);
this.song = song;
}
perform(){
return super.perform() + "[" + this.song + "]";
}
}
let james = new Singer("Etta James","At last");
james instanceof Artist; // true
james instanceof Singer; // true
james.perform(); // "Etta James performs [At last]"
class类的继承ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念。
ES6所写的类相比于ES5的优点:
区别于函数,更加专业化(类似于JAVA中的类);
写法更加简便,更加容易实现类的继承
9.Map + Set + WeakMap + WeakSet
四种集合类型,WeakMap、WeakSet作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉。
// Sets
let s = new Set();
s.add('hello').add('goodbye').add('hello');
s,size === 2;
s.has('hello') === true;
// Maps
let m = new Map();
m.set('hello', 42);
m.set(s, 34);
m.get(s) == 34;
// WeakMap
let wm = new WeakMap();
wm.set(s, {extra: 42});
wm.size === undefined;
// WeakSets
let ws = new WeakSet();
ws.add({data: 42});
10.Math + Number + String + Array + Object APls
一些新的API
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN('NaN') // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3,4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes('cd') // true
"abc".repact(3) // 'abcabcabc';
Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1,2,3) // Similar to new Array(...), but without special one-arg behacior
[0, 0, 0].fill(7, 1) // [0,7,7];
[1,2,3].find(x => x == 3) // 3;
[1,2,3].findIndex(x => x == 2) // 1
[1,2,3,4,5].copyWithin(3,0) // [1,2,3,1,2];
['a','b','c'].entries() // iterator [0, "a"], [1,"b"],[2,"c"]
['a','b','c'].keys() // iterator 0, 1, 2
['a','b','c'].values() // iterator "a","b","c"
Object.assign(Point, {origin: new Point(0,0) });
11.proxies:使用代理(Proxy)监听对象的操作,然后可以做一些相应事情。
let target = {};
let handler = {
get: function(receiver,name) {
return `Hello,${name}`;
}
}
let p = new Proxy(target,handler);
p.world === "Hello,world";
可监听的操作:get、set、has、deleteProperty,apply,construct、getOwnPropertyDescriptor、defineProperty、getPrototypeOf、setPrototypeOf、enumerate、ownKeys、preventExtensions、isExtensible
12.Symbol:唯一命名
Symbol是一种基本类型,Symbol通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的。
let key = Symbol('key');
ley key2 = Symbol('key');
key == key2 // false
13.Promise:处理异步操作的对象,使用了Promise对象之后可以用一种链式调用的方式来组织代码,让代码更加直观(类似jQuery的deferred对象)
function fakeAjax(url){
return new Promise(function(resolve,reject){
if(!url){
return setTimeout(reject,1000);
}
return setTimeout(resolve,1000);
});
}
// no Url,Promise rejected
fakeAjax().then(function(){
console.log('success');
}.fnction(){
console.log('fail');
})
Promise对象是异步编程的一种解决方案,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,要是为了解决异步处理回调地狱(也就是循环嵌套的问题)而产生的。Promise构造函数包含一个参数和一个带有resolve(解析)和reject(拒绝)两个参数的回调,在回调中执行一些操作(例如异步),如果一切都正常,则调用resolve,否则调用reject
对于已经实例化过的Promise对象可以调用Promise.then()方法,传递resolve和reject方法作为回调
then()方法接收两个参数:onResolve和onReject,分别代表当前Promsie对象在成功或失败时Promise的3种状态:Fulfilled为成功的状态,Rejected为失败的状态,Pending既不是Fulfilld也不是Rejected的状态,可以理解为Promise对象实例创建时候的初始状态
14.import和export
ES6标准中,JavaScript原生支持模块(Module),这种将JS代码分割不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块导入的方式可以在其他地方使用。
export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口
import用于在一个模块中加载另一个含有export接口的模块。
import和export命令只能在模块的顶部,不能再代码块之中。
15.Set数据结构
Set是ES6提供的一种新的数据结构,类似于数组,所有的数据都是唯一的,没有重复的值,它本身是一个构造函数,由于成员是唯一的。不重复的特点,因此可以通过Set轻松实现对数组的去重、交、并、补等操作
Set属性和方法
- Size()数据的长度
- Add()添加某个值,返回Set结构本身
- Delete()删除某个值,返回一个布尔值,表示删除是否成功
- Has()查找某条数据,返回一个布尔值
- Clear()清楚所有成员,没有返回值
interface Set{
add(value):this;
claer():void;
delete(value):boolean;
forEach(callbackfn: (value, value2, set: Set) => void, thisArg?: any):void;
has(value): boolean; 7 | readonly size: number;
}
Set主要应用场景:数组去重、交集、并集、补集。
根据Set的特点,有很多场景可以通过Set快速实现。JavaScript Set 实用场景(数据:去重、交、并、补)
let arr1 = [1, 2, 3, 4, 5, 6, 7, 4, 3, 2, 1];
// 去重
let newArr = [...new Set(arr1)];
cosnole.log(newArr) // [1,2,3,4,5,6,7]
let arr1 = [1, 2 ,3, 4, 5];
let arr2 = [3, 4, 5, 6, 7];
let arr1_set = new Set(arr1);
let arr2_set = new Set(arr2);
// 交集
let intersectionArr = [...arr_set].filter(val => arr2_set.has(val));
cosnole.log(intersectionArr) // [3,4,5]
// 并集
let unionArr = [...new Set([...arrr1,...arr2])];
cosnole.log(unionArr) // [1,2,3,4,5,6,7]
// 补集
let complementaryArr = [...arr1_set].filter(val => !arr2_set.has(val));
cosnole.log(complementaryArr) // [1,2]
15.Modules
ES6的内置模块功能借鉴了CommonJS和AMD各自的优点:
(1)具有CommonJs的精简语法、唯一导出出口(single exports)和循环依赖(cyclicdependencies)的特点。
(2)类似AMD,支持异步加载和可配置的模块加载
// lib/math.js
export function sum(x,y){
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi,math.pi));
// otherApp.js
import {sum,pi} from "lib/math";
alert("2π = " + sum(pi,pi));
Module Loaders:
// Dynamic Loading - "System" is default loader
System.import('lib/math').then(function(m){
alert('2π = ' + m.sum(m.pi,m.pi));
})
// Directly manipulate module cache
System.get('jquery');
System.set('jquery',Module({$:$})); // WARNING:not yet finalized
ES5和ES6的区别
- 系统库的引入
- ES5:需要先使用requrie导入React包,成为对象,再去进行真正引用
- ES6:可以使用import方法来直接实现系统库引用,不需要额外制作一个类库对象
- 导出及引用单个类
- ES5:要导出一个类给别的模块用,一般通过module.exports来实现,引用时,则依然通过require方法来获取
- ES6:可以使用export default来实现相同的功能,使用import方法来实现导入
- 定义组件
- ES5:组件类的定义可以通过React.createClass实现
- ES6:让组件类去继承React.Component类就可以了
- 组件内部定义方法
- ES5:采用的是 xxx.function()的形式,方法大括号末尾需要添加逗号;
- ES6:省略了【:fucntion】这一段,并且结尾不需要加逗号来实现分割
- 定义组件的属性类型和默认属性
- ES5:属性类型和默认属性分别通过propTypes成员和getDefaultProps方法来实现(这两个方法应该是固定名称的)
- ES6:统一使用static成员来实现
- 初始化STATE
- ES5:初始化state的方法是固定的getlnitialState;
- ES6:第一种,直接构造state函数,第二种,相当于OC中的方法重写,重写cosntructor方法