ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现
初学者一开始学习JavaScript,其实就是在学3.0版的语法。
----------------------------------------------------
Node.js是JavaScript语言的服务器运行环境,对ES6的支持度比浏览器更高。经过Node,能够体验更多ES6的特性。建议使用版
本管理工具nvm,来安装Node,由于能够自由切换版本。不过, nvm 不支持Windows系统,若是你使用Windows系统,下面的
操做能够改用nvmw或nvm-windows代替。
----------------------------------------------------java
ES6新增了 let 命令,用来声明变量。它的用法相似于 var ,可是所声明的变量,只在 let 命令所在的代码块内有效es6
let 不像 var 那样会发生“变量提高”现象。因此,变量必定要在声明后使用,不然报错。算法
只要块级做用域内存在 let 命令,它所声明的变量就“绑定”(binding)这个区域,ES6明确规定,若是区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就造成了封闭做用域。凡是
在声明以前就使用这些变量,就会报错。编程
为何须要块级做用域?ES5只有全局做用域和函数做用域,没有块级做用域,这带来不少不合理的场景
第一种场景,内层变量可能会覆盖外层变量
第二种场景,用来计数的循环变量泄露为全局变量。
let 实际上为JavaScript新增了块级做用域json
ES5存在函数提高,无论会
不会进入 if 代码块,函数声明都会提高到当前做用域的顶部,获得执行
而ES6支持块级做用域,无论会不会进入if代码
块,其内部声明的函数皆不会影响到做用域的外部。
-------------------------------------------------
const 声明一个只读的常量。一旦声明,常量的值就不能改变。
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须当即初始化,不能留到之后赋值。
const的做用域与let命令相同:只在声明所在的块级做用域内有效,也是不提高,存在暂时性死区,不可重复声明
对于复合类型的变量(对象),变量名不指向数据,而是指向数据所在的地址,不可变的只是这个地址,但对象自己是可变的,因此依然能够为其添加新属性
------------------------------------------------------------
ES5只有两种声明变量的方法: var 命令和 function 命令。ES6除了添加 let 和 const 命令另外
两种声明变量的方法: import 命令和 class 命令。因此,ES6一共有6种声明变量的方法。
------------------------------------------------------------
全局对象是最顶层的对象,在浏览器环境指的是 window 对象,在Node.js指的是 global 对象
var 命令和 function 命令声明的全局变量,依旧是全局对象的属性;另外一方面规定,let 命令、 const 命令、 class 命令声明的全局变量,不属于全局对象的属性。
------------------------------------------------------------
数组解构的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
对象的解构赋值的内部机制,是先找到同名属性,而后再赋给对应的变量。真正被赋值的是后者,而不是前者。windows
解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象,因为 undefined 和 null 没法转为对象,因此对它们
进行解构赋值,都会报错。数组
avaScript内部,字符以UTF-16的格式储存,每一个字符固定为2个字节,对于那些须要4个字节储存的字符(Unicode码点大于
0xFFFF的字符),JavaScript会认为它们是两个字符。
--------------------------------------------------------------
JavaScript只有 indexOf 方法,能够用来肯定一个字符串是否包含在另外一个字符串中,ES6又提供了三种新方法
includes():返回布尔值,表示是否找到了参数字符串
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部
这三个方法都支持第二个参数,表示开始搜索的位置
--------------------------------------------------------------
repeat 方法返回一个新字符串,表示将原字符串重复 n 次
padStart 和 padEnd 字符串补全长度的功能,一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的
字符串。
--------------------------------------------------------------
模板字符串(template string)是加强版的字符串,用反引号(`)标识。它能够看成普通字符串使用,也能够用来定义多行字符
串,或者在字符串中嵌入变量。模板字符串中嵌入变量,须要将变量名写在 ${} 之中。
${}若是大括号中的值不是字符串,将按照通常的规则转为字符串。好比,大括号中是一个对象,将默认调用对象的 toString 方
法。大括号内部,就是执行JavaScript代码,所以若是大括号内部是一个字符串,将会原样输出promise
ES6还为原生的String对象,提供了一个 raw 方法。String.raw 方法,每每用来充当模板字符串的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对
应于替换变量后的模板字符串。
--------------------------------------------------------------
Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity。
Number.isNaN()用来检查一个值是否为NaN。
Number.isFinite()对于非数值一概返回false, Number.isNaN()只有对于NaN才返回true,非NaN一概返回false。
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面
Number.isInteger()用来判断一个数值是否为整数。
Math.trunc方法用于去除一个数的小数部分,返回整数部分。
Math.sign方法用来判断一个数究竟是正数、负数、仍是零。对于非数值,会先将其转换为数值。
它会返回五种值。浏览器
这是由于length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值之后,预期传入的参数个数就不包括这个参数了。若是设置了默认值的参数不是尾参数,那么length属性也再也不计入后面的参数了。缓存
一旦设置了参数的默认值,函数进行声明初始化时,参数会造成一个单独的做用域
ES6 引入 rest
参数(形式为...变量名),用于获取函数的多余参数,这样就不须要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
注意,rest 参数以后不能再有其余参数(即只能是最后一个参数),不然会报错。函数的length属性,不包括 rest 参数
--------------------------------------------------------------
扩展运算符(spread)是三个点(...)。它比如 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。容易和rest混淆,两个不是一个东西。
function f(v, w, x, y, z) { } const args = [0, 1]; f(-1, ...args, 2, ...[3]);
1.因为扩展运算符能够展开数组,因此再也不须要apply方法,将数组转为函数的参数了。
2.数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。扩展运算符提供了复制数组的简便写法。
const a1 = [1, 2]; // 写法一 const a2 = [...a1]; // 写法二 const [...a2] = a1; 3.合并数组 // ES5 [1, 2].concat(more) // ES6 [1, 2, ...more]
4.扩展运算符能够与解构赋值结合起来,用于生成数组。若是将扩展运算符用于数组赋值,只能放在参数的最后一位,不然会报错。
const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错
5.扩展运算符还能够将字符串转为真正的数组。
[...'hello']
// [ "h", "e", "l", "l", "o" ]
Array.from方法用于将两类对象转为真正的数组,扩展运算符背后调用的是遍历器接口(Symbol.iterator),若是一个对象没有部署这个接口,就没法转换。Array.from方法还支持相似数组的对象。所谓相似数组的对象,本质特征只有一点,即必须有length属性。所以,任何有length属性的对象,均可以经过Array.from方法转为数组,而此时扩展运算符就没法转换。
Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of基本上能够用来替代Array()或new Array(),而且不存在因为参数不一样而致使的重载。它的行为很是统一。
--------------------------------------------------------------
数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其余位置(会覆盖原有成员),而后返回当前数组。也就是说,使用这个方法,会修改当前数组。
[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
数组实例的 find() 和 findIndex()
[1, 4, -5, 10].find((n) => n < 0) // -5
数组实例的findIndex方法的用法与find方法很是相似,返回第一个符合条件的数组成员的位置,若是全部成员都不符合条件,则返回-1。
这两个方法均可以发现NaN,弥补了数组的indexOf方法的不足。
--------------------------------------------------------------
数组实例的 includes(),没有该方法以前,咱们一般使用数组的indexOf方法,检查是否包含某个值。indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,因此要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会致使对NaN的误判。
Object.assign方法用于对象的合并,将源对象(source)的全部可枚举属性,复制到目标对象(target)。Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)。
注意点(1)浅拷贝(2)同名属性的替换(3)数组的处理(4)取值函数的处理
常见用途(1)为对象添加属性(2)为对象添加方法(3)克隆对象(4)合并多个对象(5)为属性指定默认值
--------------------------------------------------------------
对象的每一个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法能够获取该属性的描述对象。描述对象的enumerable属性,称为”可枚举性“,若是该属性为false,就表示某些操做会忽略当前属性。
“可枚举”(enumerable)这个概念的最初目的,就是让某些属性能够规避掉for...in操做,否则全部内部属性和方法都会被遍历到。好比,对象原型的toString方法,以及数组的length属性,就经过“可枚举性”,从而避免被for...in遍历到。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable // false
从兼容性的角度,都不要使__proto__
用这个属性,而是使用下面的 Object.setPrototypeOf() (写操做)、 Object.getPrototypeOf() (读操
做)、 Object.create() (生成操做)代替。
-------------------------------
Object.entries 的基本用途是遍历对象的属性,
Object.entries 方法的一个用处是,将对象转为真正的 Map 结构。var map = new Map(Object.entries(obj));
扩展运算符( ... )用于取出参数对象的全部可遍历属性,拷贝到当前对象之中。等同于使用 Object.assign 方法。
let aClone = { ...a }; // 等同于 let aClone = Object.assign({}, a);
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、
Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
Symbol值经过 Symbol
函数生成。这就是说,对象的属性名如今能够有两种类型,一种是原来就有的字符串,另外一种就是新增
的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,能够保证不会与其余属性名产生冲突。
在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中。
let s = Symbol(); typeof s // "symbol"
Symbol值能够显式转为字符串。也能够转为布尔值,可是不能转为数值。
Symbol做为属性名,该属性不会出如今 for...in 、 for...of 循环中,也不会
被 Object.keys() 、 Object.getOwnPropertyNames() 返回,它也不是私有属性,有一
个 Object.getOwnPropertySymbols 方法,能够获取指定对象的全部Symbol属性名。这就形成了一种非私有的内部方法的效果。
Symbol.for() 与 Symbol() 这两种写法,都会生成新的Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者
不会。 Symbol.for() 不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key是否已经存在,若是不存在才
会新建一个值。
-------------------------------
Proxy能够译为“代理器”,ES6原生提供Proxy构造函数,用来生成Proxy实例。
var proxy = new Proxy(target, handler);
Proxy对象的全部用法,都是上面这种形式,不一样的只是 handler 参数的写法。其中, new Proxy() 表示生成一个Proxy实
例,target参数表示所要拦截的目标对象, handler 参数也是一个对象,用来定制拦截行为。
Proxy.revocable方法返回一个可取消的Proxy实例
-------------------------------
Reflect 对象与 Proxy 对象同样,也是ES6为了操做对象而提供的新API。
将 Object 对象的一些明显属于语言内部的方法(好比 Object.defineProperty ),放到 Reflect 对象上。
-------------------------------
ES6提供了新的数据结构Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。经过 add 方法向Set结构加入成员,结果代表Set结构不会添加剧复的值,一种去除数组重复成员的方法
// 去除数组的重复成员 [...new Set(array)] 或 Array.from(new Set(array));
操做:add(value),delete(value),has(value),clear()
遍历:keys(),values(),entries(),forEach()
WeakSet结构与Set相似,也是不重复的值的集合。可是,它与Set有两个区别。
首先,WeakSet的成员只能是对象,而不能是其余类型的值。
其次,WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,若是其余对象都再也不引用该
对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特色意味着,没法引用
WeakSet的成员,所以WeakSet是不可遍历的。
-------------------------------
ES6提供了Map数据结构,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更
完善的Hash结构实现,Map的键其实是跟内存地址绑定的,只要内存地址不同,就视为两个键
Map原生提供三个遍历器生成函数keys(),values(),entries(),forEach()
与其余数据结构的互相转换
与其余遍历语法的比较:
for循环:写法比较麻烦;
数组提供内置forEach:没法中途跳出
forEach 循环,break命令或return命令都不能奏效;
for...in循环有几个缺点:
数组的键名是数字,可是for...in循环是以字符串做为键名“0”、“1”、“2”等等。
for...in循环不只遍历数字键名,还会遍历手动添加的其余键,甚至包括原型链上的键。
某些状况下,for...in循环会以任意顺序遍历键名。
总之, for...in 循环主要是为遍历对象而设计的,不适用于遍历数组
----
for...of
循环相比上面几种作法:
有着同for...in同样的简洁语法,可是没有for...in那些缺点。
不一样用于forEach方法,它能够与break、continue和return配合使用。
提供了遍历全部数据结构的统一操做接口。
--------------------------------------------------------------
Promise 是异步编程的一种解决方案,原生提供了Promise对象,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),一旦状态改变,就不会再变,任什么时候候均可以获得这个结果,Promise也有一些缺点。首先,没法取消Promise,一旦新建它就会当即执行,没法中途取消。其次,若是不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,没法得知目前进展到哪个阶段(刚刚开始仍是即将完成)。
resolve函数的做用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved)
reject函数的做用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected)
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。所以能够采用链式写法
Promise 对象的错误具备“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误老是会被下一个catch语句捕获。通常来讲,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),老是使用catch方法。
const p = Promise.all([p1, p2, p3]);
Promise.all方法接受一个数组做为参数,p一、p二、p3都是 Promise 实例。
p的状态由p一、p二、p3决定,分红两种状况。
(1)只有p一、p二、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p一、p二、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p一、p二、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race方法一样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p一、p二、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
将现有对象转为 Promise 对象,Promise.resolve方法就起到这个做用
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
--------------------------------------------------------------
async 函数是什么?一句话,它就是 Generator 函数的语法糖。async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
async函数对 Generator 函数的改进,体如今如下四点:
(1)内置执行器。(2)更好的语义。(3)更广的适用性。(4)返回值是 Promise。
只有async函数内部的异步操做执行完,才会执行外面的then方法指定的回调函数
// 函数声明 async function foo() {} // 函数表达式 const foo = async function () {}; // 箭头函数 const foo = async () => {};
若是但愿多个请求并发执行,可使用Promise.all方法。
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。
异步 Generator 函数出现之后,JavaScript 就有了四种函数形式:普通函数、async 函数、Generator 函数和异步 Generator 函数
--------------------------------------------------------------
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数彻底不一样,形式上,Generator函数是一个普通函数,可是有两个特征。一是, function 关键字与函数名之间有一个星号;二是,函数体
内部使用 yield 语句,定义不一样的内部状态.
Generator函数的调用方法与普通函数同样,也是在函数名后面加上一对圆括号。不一样的是,调用Generator函数后并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用 next 方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield 语句(或 return 语句)为止。
Generator函数是分段执行的, yield 语句是暂停执行的标记,而 next 方法能够恢复执行。
next方法返回值的value属性,是Generator函数向外输出数据;next方法还能够接受参数,这是向Generator函数体内输入数据,传入
Generator 函数,做为上个阶段异步任务的返回结果
Generator函数能够返回一系列的值,由于能够有任意多个 yield,yield后面的表达式 123 + 456 ,不会当即求值,只会在 next 方法将指针移到这一句时,才会求值,Generator函数能够不用 yield 语句,这时就变成了一个单纯的暂缓执行函数。
yield 句自己没有返回值,或者说老是返回 undefined 。 next 方法能够带一个参数,该参数就会被看成上一
个 yield 语句的返回值。
for...of 循环能够自动遍历Generator函数,且此时再也不须要调用 next 方法。
Generator函数不能用NEW(会报错),又想在函数里面用THIS ?
首先,生成一个空对象,使用 bind 方法绑定Generator函数内部的 this 。这样,构造函数调用以
后,这个空对象就是Generator函数的实例对象了。
async函数并不属于ES6,而是被列入了ES7,可是traceur、Babel.js、regenerator等转码器已经支持这个功能,转码后马上就
能使用。异步编程对JavaScript语言过重要。Javascript语言的执行环境是“单线程”的,若是没有异步编程,根本无法用,非卡死不可。
ES6诞生之前,异步编程的方法,大概有下面四种。
回调函数
事件监听
发布/订阅
Promise 对象
-------------------------------
类的数据类型就是函数,类自己就指向构造函数。
class Point{ // ... }
typeof Point // "function"
constructor 方法是类的默认方法,经过 new 命令生成对象实例时,自动调用该方法。一个类必须有 constructor 方法,
若是没有显式定义,一个空的 constructor 方法会被默认添加。
实例的属性除非显式定义在其自己(即定义在 this 对象上),不然都是定义在原型上(即定义
在 class 上)。hasOwnProperty方法判断是自己属性仍是原型属性。
Class不存在变量提高(hoist),这一点与ES5彻底不一样。 Class之间能够经过extends关键字实现继承,这比ES5的经过修改原型链实现继承,要清晰和方便不少
子类必须在 constructor 方法中调用 super 方法,不然新建实例时会报错。这是由于子类没有本身的 this 对象,而是继
承父类的 this 对象,而后对其进行加工。若是不调用 super 方法,子类就得不到 this 对象。只有调用 super 以后,才可使用 this 关键字
类的继承是按照下面的模式实现的:Object.setPrototypeOf(B.prototype, A.prototype);
Object.getPrototypeOf 方法能够用来从子类上获取父类。所以,可使用这个方法判断,一个类是否继承了另外一个类。
若是在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接经过类来调用,这就称为“静态方法”。
父类的静态方法,能够被子类继承
在ES6以前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器,ES6模块的设计思想,是尽可能的静态化,使得编译时就能肯定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时肯定这些东西。
let { stat, exists, readFile } = require('fs') 这种加载称为“运行时加载”
import { stat, exists, readFile } from 'fs' 这种加载称为“编译时加载”
ES6的模块自动采用严格模式
模块功能主要由两个命令构成: export 和 import,export 命令用于规定模块的对外接口, import 命令用于输入其余
模块提供的功能,export 和 import 命令若是处于块级做用域内,就会报错。由于处于条件代码块之中,就无法作静态优化了,违背了ES6模块的设计初衷
模块的总体加载 import * as circle from './circle';
export default 命令,为模块指定默认输出,一个模块只能有一个默认输出,因此, import 命令后面才不用加大括号,由于只可能对应一个方法
编码规范:
(1)let取代var
(2)全局常量和线程安全:在 let 和 const 之间,建议优先使用 const ,尤为是在全局环境,不该该设置变量,只应设置常量。
..静态字符串一概使用单引号或反引号,不使用双引号。动态字符串使用反引号。
..使用数组成员对变量赋值时、函数返回多个值,优先使用解构赋值。
..单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
..对象尽可能静态化,一旦定义,就不得随意添加新的属性。若是添加属性不可避免,要使用 Object.assign 方法
..使用扩展运算符(...)拷贝数组,使用Array.from方法,将相似数组的对象转为数组。
..当即执行函数能够写成箭头函数的形式,那些须要使用函数表达式的场合,尽可能用箭头函数代替。由于这样更简洁,并且绑定了this。
..不要在函数体内使用arguments变量,使用rest运算符(...)代替
..老是用Class,取代须要prototype的操做 ..Module语法是JavaScript模块的标准写法,坚持使用这种写法。使用 import 取代 require 。 -------------------------------