《ES6标准入门》笔记

         嗯,之前之做项目大概了解一些,之后看Vue实战里讲一些,简历里写了这个,所以决定系统学习,第一次接触大佬阮一峰是讲Flex布局的一篇博文,感觉很好,居然把书开源,嗯,嘻嘻,真好,生活加油.   2020.2.01

        嗯,记得都是书里的笔记,和一些常见的Demo,适合温习,面试的时候看看,后端可以看看,前端不适合,还是系统的学习好。

电子档网址:http://es6.ruanyifeng.com/  

pdf百度云资源:链接:https://pan.baidu.com/s/100G-QDBtNFrQPlp8vdthBQ      提取码:cffe 
 

嗯,最近看的书摘一句:

                一切一切,凡已属于和能属于这个世界的一切,都无可避免得带有以主体为条件[的性质],并且也仅仅只是为主体而存在的.世界即是表象 。                                                         

                                                                                                  -----------《作为意志和表象的世界》

第一章,ECMScript6简介:

ECMAScript 6 (以下简称 ES6 )是 JavaScript 语言的下 代标准.
 
ECMAScript JavaScript 的关系是,前者是后者的规格,后者是前者的 种实现(另 外的 ECMAScript 还有 JScript Action Script )。在日常场合,这两个词是可以互换。ECMA是国际标准化组织。
 

ES6

即指5.1版本之后的javaScript版本,意指下一代的javaScript语言.
 

Node

是JavaScript 语言的服务器运行环境( ru ntime ),类比于java的JVM,它对 ES6 的支持 度更高。除了那些 默认打开的功能,还有 些语法功能也已经实现了,但是默认没有打开 ,使用 ' node --vs-options I grep harmony '命令,可以 查看 Node 中己 经实现的 E S6 特性

简单的说 Node.js 就是运行在服务端的 JavaScript。

Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。

Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

webpack 

一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle

Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。即

WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。

webpack的核心优势在于它从入口文件出发,递归构建依赖关系图。通过这样的依赖梳理,webpack打包出的bundle不会包含重复或未使用的模块,实现了按需打包,极大的减少了冗余。

《ES6标准入门》笔记_第1张图片

《ES6标准入门》笔记_第2张图片

 

npm

随同NodeJS一起安装的包管理工具,为你和你的团队打开了连接整个 JavaScript 天才世界的一扇大门。它是世界上最大的软件注册表,每星期大约有 30 亿次的下载量,包含超过 600000 个 包(package) (即,代码模块)。来自各大洲的开源软件开发者使用 npm 互相分享和借鉴。包的结构使您能够轻松跟踪依赖项和版本。

类似于maven的一个依赖工具,用 JavaScript (运行在 Node.js 上)写的 npm,全称是 Node Package Manager,

npm install --save app 与 npm install --save-dev app有什么区别?

  • npm install --save app: 保存需要加载的依赖的信息到package.json里面,该模块使用时,该依赖被调用

  • npm install --save-dev app: 开发测试时使用的依赖,当需要测试时,可以调用该依赖

Node.js 是由一个在德国工作的美国程序员 Ryan Dahl 写的。他写了 Node.js,但是 Node.js 缺少一个包管理器,于是他和 npm 的作者一拍即合、抱团取暖,最终 Node.js 内置了 npm。

ES6 转码器,

 
Babel可以将 ES6 代码转为 ES5 代码.
//转码前
input.map(item =>{item + 1});
//转码后
input.map(function(item){return item + 1;});

第二章,let和const命令

let命令

基本使用:

let用于声明变量,用法类似于var,用于声明局部变量,即指在当前代码块里可见,

适用场景:for循环:

for(var i = 0; i < 10; i++){ a[i] = function(){console.log(i);}}  
a[6](); //输出10;变量i是全局的,在全局范围内有效,赋值给数组内部的console.log(i)中的i是全局的。即所有的i在经过最后一次循环之后都是10.
for(let i = 0; i < 10; i++){ a[i] = function(){console.log(i);}}
a[6]();//输出6,对于let声明的,在循环块里才有效,即每一次都是新的值

既然是新的值,为啥会记住呢?因为javaScript引擎会记住上一轮的值,对于for循环来讲,就是设置循环变量的那部分是父作用域,循环的那部分是子作用域。
 

for(let i = 0; i < 3; i++){let i = 'liruilong'; console.log(i)};
// 会输出3次liruilong

不存在变量提升:

var 命令会发生“变量提升”现象, 即变量可以在声明之前使用,值为 undefined 。这 种现象多少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。 为了纠正这种现象, let 命令改变了语法行为,它所声明的变量一定要在声明后使用,否 则便会报错.
 

《ES6标准入门》笔记_第3张图片

暂时性死区(TDZ):

只要块级作用域内存在 let 命令,它所声明的变量就“绑定”( bi nding )这个区域,不再受外部的影响。在let之前对变量赋值会报错.
 
码块 内, 使用 let 命令声 变量之前,该变量都是不可用的。这在语法上称为“暂 时性死区”( temporal dead one ,简称 TDZ
 
S6 明确规定 如果区块中存在 let const 命令,则这个区块对这些命令声明的变量就形成封闭作用域。只要在声明之前就使用这些变量 就会报错

 《ES6标准入门》笔记_第4张图片

 不允许重复声明.

let 不允许在相同作用域内重复声明同 一个变量。

块级作用域

ES5 只有全局作用域和函数作用域,没有块级作用域,这导致很多场景不合理。 let 实际上为 JavaScript 新增了块级作用域.

块级作用域与函数声明
ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明
ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。 ES6 规定,在块级作用域 之中,函数声明语句的行为类似于 let,在块级作用域之外不可引用。

do表达式:

do表达式
let x = do{ 
	let t = f(); 
	t * t + 1;
	}; 
	// 书上说变量x会得到整个作用域的值,那他是得到的值经过t * t + 1;这个运算没。

const命令

基本用法:

const声明一个只读的常量,一旦声明,常量的值就不能改变。所以必须初始化。

const 的作用域与 let 命令相同:只在声明所在的块级作用域内有效。也不会发生常量提升,同样存在暂时性死区,只能在声明后使用.
con st 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。 对于简单类型的数据(数值、字符串、布尔值〉而言,值就保存在变量指向的内存地址中,因此等同于常量。但对于复合类型的数据(主要是对象和数组)而言,变量指向的内存地址保存 的只是一个指针, const 只能保证这个指针是固定的,至于它指向的数据结构是不是可变的, 这完全不能控制

《ES6标准入门》笔记_第5张图片

ES6 声明变量的 6 种方法:对于ES5来讲,使用 var 命令和 function 命令.ES6中新增了let 和const,import 和 class 命令.
 

顶层对象的属性:

 
顶层对象在浏览器环境中指的是 window 对象,在 Node 环境中指的是 global 对象。在 ES5 中,顶层对象的属性与全局变量是等价的。ES6开始,全局变量将逐步于顶层对象的属性隔离.
 

var a = 1;
//如果在Node的REPL环境,可以写成global.a
//或者采用通用的方法,写成this.a
window.a // 1
let b=  1;
window.a  //undefined

使用global的写法.

//CommonJs的写法
require('system.global/shim')();
//ES6模块的写法
import shim form 'system.global/shim';shim();

第三章,变量的解析赋值

数组的解构赋值

ES6 允许按照一定模式从数组和对象中提取值,然后对变量进行赋值,这被称为解构 ( Destructuring)。

基本使用

ES6允许写出类似于python的 直接赋值的语法,模式匹配
let [a,b,c] = [1,3,4];

 

《ES6标准入门》笔记_第6张图片

 
function* fibs(){ 
	let a = 0;
	let b = 0;
	while(ture){
	yield a;
	[a,b] = [b,a + b];
	}
}
let [first, second, third, fourth, fifth, sixthl] =fibs(); 
sixth // 5

//yield也必须在Generator函数中才有意义,脱离了Generator就没意义了,fibs被定义为一个Generator函数,具有原生的Iterator接口。结构赋值会依次从这个接口获取值。运行.next(),遇到一个yield命令,就暂停.

《ES6标准入门》笔记_第7张图片
 
 

对象的解构赋值

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值是由它的 位置决定的:而对象的属性没有次序,变量必须与属性同名才能取到正确的值。
《ES6标准入门》笔记_第8张图片
 
 
例子:之前的一个项目做的菜单路由的懒加载使用了递归+vuex和vue-router
 
import { getRequest } from "./api";
/*
 * 对菜单路由进行处理
 * */


export const initMenu = (router, store) => {
    if (store.state.routes.length > 0) {
        return;
    }
    getRequest("/system/config/menu").then(data => {
        if (data) {
            let fmtRoutes = formatRoutes(data);
            //添加路由
            router.addRoutes(fmtRoutes);
            //初始化数据
            store.commit('initRoutes', fmtRoutes);

        }
    })
}
// *
// 菜单数据的格式话 *
export const formatRoutes = (routes) => {
    let fmRoutes = [];
    routes.forEach(router => {
        // 数组的解构赋值
        let {
            path,
            component,
            name,
            meta,
            iconcls,
            children
        } = router;
        if (children && children instanceof Array) {
            children = formatRoutes(children);
        }
        //对象的解构赋值
        let fmRouter = {
            path: path,
            name: name,
            iconcls: iconcls,
            meta: meta,
            hidden: false,
            children: children,
            component (resolve) {
                if (component.startsWith("Home")) {
                    require(['../views/' + component + '.vue'], resolve);
                } else if (component.startsWith("Emp")) {
                    require(['../views/emp/' + component + '.vue'], resolve);
                } else if (component.startsWith("Per")) {
                    require(['../views/per/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sal")) {
                    require(['../views/sal/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sta")) {
                    require(['../views/sta/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sys")) {
                    require(['../views/sys/' + component + '.vue'], resolve);
                }
            }
        }
        fmRoutes.push(fmRouter);
    })
    return fmRoutes;
}

字符串的解构赋值

字符串的解构赋值,字符串也可以解构赋值,因为此时字符串被转换为类似的数组的对象。
 

const {a,b,c,d,e} = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"

let{lenth:len} = 'hello';
len //5

数值和布尔值的解构赋值

《ES6标准入门》笔记_第9张图片

解析赋值的规则是,只要等号右边的值不是对象或者数组,就先将其转换为对象.
 
let {prop:x} = undefined;
let {prop:y} = null;

函数参数的解构赋值


//函数参数的解构赋值
function add([x,y]){return x + y;}
add([1,2]); // 3

[[1,2],[3,4]].map(([a,b] => a + b ));
//[3,7]
function move({x = 0, y = 0} = {}){ return[x,y];}

圆括号问题

《ES6标准入门》笔记_第10张图片

变量的解构赋值用途:

交换变量的值:从函数返回多个值.(数组+对象);

let x = 1;
let y = 2;
[x,y] = [y,x]
// 交换变量的值

//从函数返回多个值
// 返回数组
function example(){return [1,2,3];}
let [a,b,c] = example();
// 返回对象
function example(){return foo:1,bar:2};}
let {foo, bar} = example();

函数参数的定义提取Json数据

//函数参数的定义
function f([x,y,z]){...}
f([1,2,3]);

//参数是一组没有顺序的值
function f({x,y,z}){...}
f({z:3,y:2,x:1});

解构赋值对于提取JSON对象种的数据尤为有用:


//提去JSON数组的值
let jsonData = {id:42, status:"OK"data:[876,5309]};
let { id, status, data: number } = jsonData;

遍历Map结构


//遍历Map解构
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for(let [key,value] of map){
	console.log(key + "is" + value);
}
可以只写或者键或者值的写法。

输入模块的指定方法
加载模块时,往往需要指定输入的方法。解构赋值使得输入语句非常清晰。
 

const ( SourceMapConsumer, SourceNode } = require (”source-map"); 

 

第七章,函数的扩展

函数参数的默认值

基本用法:

函数参数的默认值:
ES6之前
function log(x, y){
y = y || 'world';
console.log(x,y);
}
ES6之后可以使用参数设置默认值,即直接写在参数定义的后面。也可以于解构赋值结合使用。
function log(x,y = 'World'){console.log(x,y)}

函数的length属性,

指定了默认值之后,函数的length属性将返回没有指定的默认值的参数个数,

ES6之后可以使用参数设置默认值,即直接写在参数定义的后面。也可以于解构赋值结合使用。
function log(x,y = 'World'){console.log(x,y)}

(function (a) {}).length // 1 
(functi on (a= 5){}).length // 0 
(function (a, b , c = 5){}).length // 2

作用域:

一旦设置了参数的作用域,函数进行声明初始化时,参数会形成一个单独的作用域,等到初始化结束,这个作用域就会消失,这种语法在不设置参数作用域不会出现.


let x = 1;
function f(x,y = x){
console.log(y);
} 
f(2) // 2

let x = 1;
function f(x,y = x){
let x = 2;
console.log(y);
} 
f()// 1
let foo = 'outer';
function bar(func = x => foo){
let foo = 'inner';
console.log(func());
}
bar() // outer

function bar(func = () => foo){
let foo = 'inner';
console.log(func());
}
bar() //ReferenceError:foo is not defined;

rest参数

ES6引入了rest,即类似Java的可变参数,用于获取多个参数.


//rest参数的使用
function add(...values){
let sum = 0;
for(var val of values){
sum += val;
}
return sum;
add(1,23,4) //28

//使用argument对象
function  sortNumbers(){ return Array.proptotype.slice.call(arguments).sort();}
//使用rest参数
const sortNumbers = (...numbers) => numbers.sort();

严格模式

《ES6标准入门》笔记_第11张图片

name属性

函数的name属性返回函数的函数名

function foo (){};
foo.name // foo

箭头函数

基本用法:

ES6 允许使用“箭头”(=>)定义函数。类似于Java8的lambda表达式.
 

var f = v => v;
var f = function(v){return v}
// 正常函数写法 [l , 2 , 3].map (function (x) { return x * x; ., ) } 
//箭头函数写法  [1, 2 , 3 ].map(x => x * x ); 
下面是另一个例子。
//正常函数写法 var result= values . sort (function (a, b) { return a - b; . , ) } 
//箭头函数写法 var result = values . sort ((a, b) => a - b); 

箭头函数有以下几个使用注意事项

1. 函数体内的 this 对象就是定义时所在的对象,而不是使用时所在的对象
 

function foo(){
	setTimeout( () =>{
	console.log('id:',this.id);
	},100);
}
var id = 21;
foo.call({id:42});
// id:42

2. 不可以当作构造函数。 也就是说, 不可以使用 new 命令, 否则会抛出一个错误。

3. 不可以使用 arguments 对象, 该对象在函数体内不存在。如果要用,可以用 rest 参数 代替。

4. 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。 

嵌套的箭头函数

//ES6之前
function insert(value) { 
return {into: function (array) { 
return {after: function (afterValue) { 
array.splice(array.indexOf(afterValue) + 1,0,value); 
return array; }
} ; 
}} ; 
insert(2).into([l, 3]).after(l) ; //[1, 2, 3) 
//ES6
let insert= (value) => ({into: (array) => ({after: (afterValue) => { array.splice(array. indexOf(afterValue) + 1 , 0, value); 
return array;  } } ) } }; 
insert(2).into([l, 3]).after(l) ; //[l, 2 , 3) 

绑定this

箭头函数可以绑定 this 对象,大大减少了显式绑定 this 对象的写法( call、 apply、 bind)。但是,箭头函数并非适用于所有场合,所以 ES7 提出 了“函数绑定”( function bind) 运算符,用来取代 call、 apply、 bind 调用。虽然该语法还是 ES7 的一个提案( github.com/ zenparsing/es- function-bind),但是 Babel 转码器已经支持。 

  • call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表
  • apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。
  • bind和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。

《ES6标准入门》笔记_第12张图片

尾调用优化

尾调用(Tail Call)(钩子函数)

是函数式编程的一个重要概念, 就是指某个函数的最后一步是调用另一个函数。也称钩子函数,在Javal里JVM中有钩子线程.在jvm进程结束的调用,尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、 内部变量等信息都不会再用到了,直接用内层函数的调用帧取代外层函数的即可。

尾调用的优化

“尾调用优化” (Tail Call Optimization),即只保留内层函数的调用帧。 如果所有函数都是尾调用,那么完全可以做到每次执行时调用帧只有一项,不需要在维护调用它的栈,这将大大节省内存。这就是 “尾调用优化”的意义。
 

function f(){
	let m = 1;
	let n = 2;
	return g(m + n);
}
f();
// 优化之后
function f(){
return g(3);
}

尾递归:

函数调用自身称为递归。如果尾调用自身就称为尾递归。 递归非常耗费内存,因为需要同时保存成百上千个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
严格模式:ES6 的尾调用优化只在严格模式下开启,正常模式下是无效的。 

函数参数的尾逗号:,允许函数的最后一 个参数有尾逗号(trailing comma)。


第八章,数组的扩展

扩展运算待

第十一章,Set和Map数据结构

Set

ES6 提供了新的数据结构一-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)}; 
// 2 3 5 4 
Set 函数可以接受一个数组。或者具有迭代器接口的其他的数据结构作为参数,用来初始化
const set = new Set([1,2,3,4,4]);
[...set] // [1,2,3,4]
set.size  //4

Set 实例的属性和方法

Set 结构的实例有以下属性。 

  • • Set. prototype. constructor: 构造函数,默认就是 Set 函数。
  • • Set . prototype . size:返回 Set 实例的成员总数。 

Set 实例的方法分为两大类:

  • 操作方法(用于操作数据〉
    • add (value ):添加某个值,返回 Set 结构本身。
    • delete(value ):删除某个值,返回一个布尔值,表示删除是否成功。
    • has (value ):返回一个布尔值,表示参数是否为 Set 的成员。
    • clear (): 清除所有成员,没有返回值。 
  • 遍历方法(用于遍历成员〉 
    • keys () : 返回键名的遍历器。
    • values ():返回键值的遍历器 。
    • entries ():返回键值对的遍历器 。
    • forEach ():使用回调函数遍历每个成员。
    • 数组的 map 和 filter 方法也可以用于 Set。

WeakSet 

WeakSet 结构与 Set 类似,也是不重复的值的集合。 但是,它与 Set 有两个区别。ES6规定WeakSet是不可遍历的.

  • 第一, WeakSet 的成员只能是对象,而不能是其他类型的值。
  • 第二, WeakSet 中的对象都是弱引用,即垃坡回收机制不考虑 、 WeakSet 对该对象的引用, 也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内 存,不考虑该对象是否还存在于 WeakSet 之中。 
  • javaScript的垃圾回收机制.依赖于引用计数器,
const ws =new WeakSet(); 
const a = [ (1 , 2], (3, 4)); 
const ws =new WeakSet(a) ; // WeakSet {[l, 2), [3, 4)) 

WeakSet 结构有以下 3 个方法。

  • WeakSet.prototype . add(value ): 向 WeakSet 实例添加一个新成员。
  • WeakSet.prototype . delete (value ): 清除 WeakSet 实例的指定成员。
  • WeakSet . prototype . has (value ): 返回一个布尔值, 表示某个值是否在 WeakSet 实例中。

Map

含义和基本用法  

ES6 提供了 Map 数据结构。它类似于对象,

const m =new Map() ;
const o = {p:’Hello World’} ; 
m.set(o,’content ’); 
m.get(o) // ”content” 
m.has(o) //true 
m.delete(o) //true 
m.has(o) // false

实例的属性和操作方法

  • size 属性 :size 属性返回 Map 结构的成员总数。
  • set(key, value) :set 方法设置 key 所对应的键值,然后返回整个 Map 结构。如果 key 己经有值,则键值 会被更新,否则就新生成该键。可以链式操作.
  • get( key) :get 方法读取 key 对应的键值,如果找不到 key,则返回 undefined。
  • has(key) :has 方法返回一个布尔值,表示某个键是否在 Map 数据结构中 。
  • delete( key) :delete 方法删除某个键,返回 true。如果删除失败,则返回 false。
  • clear() : clear 方法清除所有成员,没有返回值。

遍历方法

Map 原生提供了 3 个遍历器生成函数和 l 个遍历方法。

  • keys ():返回键名的遍历器。
  • values ():返回键值的遍历器。
  • entries (): 返回所有成员的遍历器。
  • forEach ():遍历 Map 的所有成员 。 需要特别注意的是, Map 的遍历顺序就是插入顺序。
const map= new Map([ [’ F ’,’no ’] , [’ T’,’yes ’ ] ,]); 
for (let key of map.keys() ) { 
console .log (key); }
// ” F” 
// ” T” 
for (let value of map.values()) { 
console.log(value) ;} 
// ”no”
// ” yes” 
for (let [key, value] of map.entries()) { 
console.log(key, value);} 
// ”F””no” 
// ”T” ” yes”
 //等同于使用 map.entries () 
for (let [key, value] of map) { console . log(key,value)} ; 
Map 结构转为数组结构的比较快速的方法是结合扩展运算符( ..)。
const map = new Map ( [ [ 1, ’ one ’], (2,’two ’] , (3,’three ’ ],]); 
 
[...map.keys () ] //(1 , 2 , 3] 
[...map.values(}] //[ ’ 。ne ’,’ two ’,’ three ’ ] 
[...map.entries ()] I I [ [ 1,’one ’], (2,’two ’], (3,’three ’] ] 
[...map] I I [ [ 1,’one ’], (2,’two ’], (3,’three ’ ]] 

与其他数据结构的互相转换

数组转Map:将数组传入Map构造函数就可以转为Map。
new Map([[true,7],[{foo:3},['abc']]]);
// map {true => 7, Object {foo:3} =>['abc']}


Map转为对象:如果 Map 的所有键都是字符串,则可以转为对象。
function strMapToObj(srtMap){
let obj = Object.create(null);
for( let [k,v] of strMap){
	obj[k] = v;
	}
	return obj;
	}
const myMap = new Map().set('yes',true).set('no',false);
strMapToObj(myMap) //{yes:true, no:false }

Map 转为 JSON

一种情况是, Map的键名都是字符串,这时可以选择转 为对象 JSON。
function strMapToJson(strMap){return JSON.stringify(strMapToObj(strMap));}
let myMap =new Map().set(’yes’,true).set(’no ’,false); 
strMapToJson(myMap) 
// ’{”yes”:true ,”n。”: false }’ 

另一种情况是,Map 的键名有非字符串,这时可以选择转为数组 JSON。
function mapToArrayJson(map) { 
return JSON.stringify([ ... map]);} 
let myMap =new Map().set(true,7).set({foo : 3),[’ abc ’]); 
mapToArrayJson(myMap)
// ’[( true,7 ], [{ ” foo”: 3}, [”abc”]]]’ 

JSON 转为 Map,正常情况下所有键名都是字符串 。
function jsonToStrMap(jsonStr) { 
return objToStrMap (JSON.parse(jsonStr) ); 
jsonToStrMap (’{” yes” : true ,”no”: false } ’ ) 
// Map {’ yes ’=> true,’no’ => false} 

WeakMap 

WeakMap 结构与 Map 结构类似,也用于生成键值对的集合。 

  • 第一, WeakMap 只接受对象作为键名(null 除外),不接受其他类型的值作为键名。

  • 第二, WeakMap 的键名所指向的对象不计入垃圾回收机制。 

第十二章,Proxy

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编 程”(meta programming),即对编程语言进行编程。

Proxy 可以理解成在目标对象前架设一个“拦截”层

外界对该对象的访问都必须先通过 这层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写。 

ES6 原生提供 Proxy 构造函数,用于生成 Proxy 实例。 
var proxy= new Proxy(target, handler) ; 

Proxy 对象的所有用法都是上面这种形式,不同的只是 handler 参数的写法。 其中,

var obj = new Proxy({}, {
get:function (target, key){
console.log();
return Reflec.get(tafget, key);
},
set:function(target, key){
console.log();
return Reflec.get(tafget, key);
}
});

new Proxy (),表示生成一个 Proxy 实例, target 参数表示所要拦截的目标对象, handler 参数也 是一个对象,用来定制拦截行为。 

下面是 Proxy 支持的所有拦截操作。 对于可以设置但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。

  • get(ta「get, propKey,receiver) 拦截对象属性的读取,比如 proxy.foe 和 proxy [ ’ foo’]。最后一个参数 receiver 是一个可选对象,参见下面 Reflect.get 的部分。
  • set(target, propKey, value, receiver) 拦截对象属性的设置,比如 proxy. foo = v 或 proxy [’ foo ’] = v,返回一个布尔值。
  • has(target, propKey) 拦截 propKey in proxy 的操作,返回一个布尔值。
  • deleteProperty(target, propKey) 拦截 delete proxy[propKey)的操作,返回一个布尔值。
  • ownKeys(target) 拦截 Object . getOwnPropertyNarnes(proxy)、Object.getOwnPropertySyrnbols (proxy)、 Object.keys(proxy),返回一个数组。该方法返回目标对象所有自身属性的属 性名,而 Object .keys ()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey) 拦截 Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc) 拦截 Object . defineProperty(proxy, propKey, propDesc〕、 Object . define Properties(proxy, propDescs ),返回一个布尔值。
  • preventExtensions(target) 拦截 Object.preventExtensions (proxy) ,返回一个布尔值。
  • getPrototypeOf(target) 
  • 拦截 Object . getPrototypeOf (proxy)返回一个对象。
  • isExtensible(target) 
  • 拦截 Object . isExtensible (proxy,返回一个布尔值。
  • setPrototypeOf(target, proto) 拦截 Object . setPrototypeOf (proxy, proto ),返回一个布尔值。 如果目标对象是 函数, 那么还有两种额外操作可以拦截。
  • apply(target, object, args) 拦截 Proxy 实例,并将其作为函数调用的操作,比如 proxy ( ... args ) 、 proxy . call (object, ... args ) 、 proxy . apply ( ... ) 。
  • construct( ta咱et, args) 拦截 Proxy 实例作为构造函数调用的操作,比如 new proxy ( . . . args ) 。
     

实例,Web服务的客户端

const service = createWebService("url");
service.employees().then(json =>{const employees = JSON.parse(json);});
//Proxy 可以拦截这个对 象的任意属性,所以不用为每一种数据写一个适配方法,只要写一个 Proxy 拦截即可。
function createwebService(baseUrl){
return new Proxy({},{
get(target, propKey, receiver) { return () => httpGet (baseUrl+’ / ’ + propKey) ; })}

第十三章,Reflect

Reflect 对象与 Proxy 对象一样,也是 ES6 为了操作对象而提供的新的 API. Reflect 对象的设计目的有以下几个。 

  • I. 将 Object 对象的一些明显属于语言内部的方法(比如 Object . defineProperty) 放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Reflect 对象上部署, 未来的新方法将只在 Reflect 对象上部署。也就是说,从 Reflect 对象上可以获得语言内部的方法。
  • 2 . 修改某些 Object 方法的返回结果,让其变得更合理。比如, Object . defineProperty (obj , name, desc)在无法定义属性时会抛出 一 个错误,而 Reflect . defineProperty(obj , name, desc)则会返回 false
    try{
    	Object.defineProperty(target,property,atttribute);
    	//success
    }catch(e){
    	//failure
    }
    if(Reflect.defineProperty(target,property,atttribute)){
    //success}else{
    //failure
    }

     

  • 3. 让 Object 操作都变成函数行为。某些 Object 操作是命令式,比如 name in obj 和 delete obj [name],而 Reflect.has(obj, name )和 Reflect.deleteProperty (obj, name )让它们变成了函数行为
  • 4. Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就 能在 Reflect 对象上找到对应的方法。这就使 Proxy 对象可以方便地调用对应的 Reflect 方法来完成默认行为,作为修改行为的基础。也就是说,无论 Proxy 怎么修改默认行为,我们 总可以在 Reflect 上获取默认行为。

静态方法

  • Reflect.apply(target,thisArg,args)
  • Reflect.construct( target,args) 
  • Reflect.get(target,name,receiver) 
  • Reflect.set(target,name, value,receiver) 
  • Reflect.defineProperty( target,name,desc) 
  • Reflect.deleteProperty( target, name) 
  • Reflect.has( target,name) 
  • Reflect.ownKeys(target) 
  • Reflect.isExtensible(target) 
  • Reflect. preventExtensions( target) 
  • Reflect.getOwnPropertyDescriptor(target, name) 
  • Reflect.getPrototypeOf(target) 
  • Reflect.setPrototypeOf(target, prototype) 

第十四章,Promise对象

Promise 的含义

Promise 是异步编程的一种解决方案,比传统的解决方案一一回调函数和事件→一更合理且 更强大。 ES6 将其写进了语言标准,统一了用法,并原生提供了 Promise 对象。 

所谓 Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个 异步操作)的结果。从语法上来说, Promise 是一个对象,从它可以获取异步操作的消息。 Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。 

Promise 对象有以下两个特点:

  • 1. 对象的状态不受外界影响。 Promise 对象代表一个异步操作,有 3 种状态: Pending (进行中)、 Fulfilled (己成功)和 Rejected (己失败)。只有异步操作的结果可以决定当前是哪一种 状态,任何其他操作都无法改变这个状态。这也是“Promise”这个名字的由来,它在英语中意 思就是“承诺”,表示其他手段无法改变。
  •  2. 一旦状态改变就不会再变,任何时候都可以得到这个结果。 Promise 对象的状态改变只 有两种可能:从 Pending 变为 Fulfilled 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变,而是一直保持这个结果,这时就称为 Resolved (己定型)。 就算改变己经 发生,再对 Promise 对象添加回调函数,也会立即得到这个结果。。这与事件 CEvent)完全不同。 事件的特点是,如果错过了它,再去监昕是得不到结果的。
  • Promise 也有一些缺点。首先,无法取消 Promise, 一旦新建它就会立即执行,无法中途取 消。其次,如果不设置回调函数, Promise 内部抛出的错误不会反应到外部。 再者,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。 

基本用法

var promise= new Promise(function(resolve, reject) { 
// ... some code 
if (/*异步操作成功*/){ resolve(value) ; } 
else { reject(error); } }) ; 


Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。 它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

  • resolve 函数的作用是,将 Promise 对象的状态从“未完成”变为“成功”(即从 Pending 变为 Resolved),在异步操作成功时调用,并将异步操作的结果作为参数传递出去:
  • reject 函数的作用是,将 Promise 对象的状态从“未完成”变为“失败”(即从 Pending 变为 Rejected), 在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去。 Promise 实例生成以后,可以用 then 方法分别指定 Resolved 状态和 Rejected 状态的回调 函数。
 promise.then(function (value){//success},function (error){//failure})

then 方法可以接受两个回调函数作为参数。

  • 第一个回调函数是 Promise 对象的状态变为 Resolved 时调用,
  • 第二个回调函数是 Promise 对象的状态变为 Rejected 时调用。其中,第二个 函数是可选的,不一定要提供。这两个函数都接受 Promise 对象传出的值作为参数。 

Promise 对象的简单例子。
 

function timeout(ms){return new Promise((reslve,reject) =>{setTimeout(recelve,ms,'done')})}
 timeout(100).then((value) =>{ console .log (value); }); 

Promise 新建后就会立即执行。


let promise= new Promise(function(resolve, reject){console.log(’Promise’); resolve();  }) 
promise.then(function(){console.log (’Resolved.’);}); 
console.log(’Hi!’); 
// Promise 
// Hi! 
// Res。lved

 用 Promise 对象实现的 AJAX 操作的例子。

var getJSON = function(url) { 
var promise= new Promise(function(resolve, reject) { 
var client= new XMLHttpRequest() ; 
client.open(” GET”, url); 
client.onreadystatechange = handler; 
client.responseType = ” json”; 
client.setRequestHeader (”Accept”,”application/json” ) ; client.send() ; 
function handler(){
if(this.readyState !== 4){
return; 
}
if (this.status === 200) { 
resolve(this.response) ; } 
else { reject(new Error(this.statusText)} ; 
 };});
 return promise ;
 
 getJSON (”/posts.Json”).then(
 function(json){ 
 console.log ( ’Contents:’+ json); 
 }, 
 function(error} { console. error (’出错了’, error);});

Axios

一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

// 为给定 ID 的 user 创建请求
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// 上面的请求也可以这样做
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
export const postRequest = (url, params) => {
        return axios({
            method: 'POST',
            url: `${base}${url}`,
            data: params,
        })
    }
    // put请求封装
export const putRequest = (url, params) => {
        return axios({
            method: 'put',
            url: `${base}${url}`,
            data: params,
        })
    }
    // get请求封装
export const getRequest = (url, params) => {
        return axios({
            method: 'get',
            url: `${base}${url}`,
            data: params,
        })
    }
    // delete请求封装
export const deleteRequest = (url, params) => {
    return axios({
        method: 'delete',
        url: `${base}${url}`,
        data: params,
    })

 使用 then 时,你将接收下面这样的响应 :

axios.get('/user/12345')
  .then(function(response) {
    console.log(response.data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
    console.log(response.config);
  });

学习网站:http://www.axios-js.com/zh-cn/docs/

Promise.prototype.then() :

Promise 实例具有 then 方法,即 then 方法是定义在原型对象 Promise .prototype 上 的。它的作用是为 Promise 实例添加状态改变时的回调函数。 前面说过, then 方法的第一个参数是 Resolved 状态的回调函数,第二个参数(可选)是 Rejected 状态的回调函数。 then 方法返回的是一个新的 Promise 实例(注意,不是原来那个 Promise 实例)。因此可 以采用链式写法,即 then 方法后面再调用另一个 then 方法。 

Promise.prototype.catch() :

方法是 . then(null, rejection)的别名,用于指定发 生错误时的回调函数。: 如果异步操作抛出错误,状态就会变为Rejected ,然后调 用 catch 方法指定的回调函数处理这个错误。另外, then 方法指定的回调函数如果在运行中抛出错误,也会被 catch 方法捕获。

Promise.all() :Promise.all 方法用于将多个 Promise 实例包装成一个新的 Promise 实例。异步并发情况.

var p = Promise.all([pl,p2,p3]);
 //p 的状态由 pL p2 、 p3 决定, 分成两种情况。 
 1. 只有 pl 、 p2 、 抖的状态都变成 Fulfilled, p 的状态才会变成 Fulfilled,此时 pl、 p2、 p3 的返回值组成一个数组,传递给 p 的回调函数。 
 2. 只要 pl、 p2、 p3 中有一个被 R可ected, p 的状态就变成 Rejected, 此时第一个被 R句ected 的实例的返回值会传递给 p 的回调函数。
 

Promise.race() :Promise.race 方法同样是将多个 Promise 实例包装成一个新的 Promise 实例。Promise . race 方法的参数与 Promise . all 方法一样,如果不是 Promise 实例,就会先 调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。 

Promise.resolve() :有时需要将现有对象转为 Promise 对象, Prom工se.resolve 方法就起到这个作用。

Promise.reject() :Promise.reject(reason )方法也会返回一个新的 Promise 实例,状态为 Rejected。

第22章,Module的语法

ES6 模块

ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令 输入。这种加载称为“编译时 加载”或者静态加载,即 ES6 可以在编译时就完成模块加载

import { stat, exists, readFile } from ’ fs ’ ;

ES6 的模块自动采用严格模式,不管有没有在模块头部加上“use strict ”。

模块功能主要由两个命令构成: export 和 import。

  • export 命令用于规定模块的对外接 口,
  • import 命令用于输入其他模块提供的功能。 

export命令

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果希望外部能 够读取模块内部的某个变量,就必须使用 export 关键字输出该变量。下面是一个 JS文件,里 面使用 export 命令输出了变量
 

 //profile.js 
export var f irstName =’Michae l ’ ; 
export var lastName =’Jackson ’; 
export var year = 1958;

// profile.js
var firstName =’ Michael’;
var lastName =’Jackson ’ ; 
var year = 1958; 
export {firstName, lastName, year} ; 
export 命令除了输出变量,还可以输出函数或类 (class)。 export 输出的变量就是本来的名字,但是可以使用 as 关键字重命名 

import命令

使用 export 命令定义了模块的对外接口以后,其他 JS 文件就可以通过 import 命令加 载这个模块了。

// main.js
 import {firstName, lastName, year} from '. lprofile ’; 
 function setName(element} { element.textContent = firstName +’’+ lastName; }

模块的整体加载

除了指定加载某个输出值,还可以使用整体加载(即使用*来指定一个对象,所有输出值都加载在这个对象上)。模块整体加载所在的对象(上例是 circle )应该是可以静态分析的,所以不允许运行时改变。

// circle.js 
export function area(radius) { 
return Math.PI*radius * radius; 
export function circumference(radius) { 
return 2 * Math.PI * radius ; 

// main.js
import * as circle from ’ .lcircle ’; 
console .log ( ’ 因面积:’+ circle.area(4)); 
console . log ( ' 圆周长:’+ circle.circumference(l4)) ; 

export default 命令

使用 export default 命令为模 块指定默认输出。export default 命令用于指定模块的默认输出。显然, 一个模块只能有一个默认输出, 因此 export deaul t 命令只能使用一次。所以 import 命令后面才不用加大括号,因为只可 能对应一个方法。

//modules.js 
function add(x, y) { 
return x * y; 
export {add as default); 
//等同于 
// export default add; 
// app.js
import { default as xxx ) from ’modules ’ ;
// 等同于 
// import xxx from ’ modules ’;

 export与 import的复合写法

如果在一个模块之中先输入后输出同一个模块, import 语句可以与 export 语句写在 一起。
 

export { foo , bar } from ’my_module ’; 
//等同于
import { foo, bar J from ’my_module ’; 
export { foo, bar };

模块的继承

模块之间也可以继承。

假设有一个 circleplus 模块继承了 circle 模块。
// circleplus . js 
export * from ’ circle ’ ; 
export var e = 2.71828182846; 
export default function(x) { 
return Math . exp (x) ; 
上面的 export *表示输出 circle 模块的所有属性和方法。

import()

import 命令会被 JavaScript 引擎静态分析, 先于模块内 的其他模块执行(称 为“连接”更合适)。

// 报错
if (x === 2 ) { 
import MyModual from ’./lmyM odual ’ ; }

引擎处理 import 语句是在编译时,这时不会分析或执行if 语句,所以 import 语句放在 if 代码块之中毫无意义,因此会报句法错误, 而不是执行时错误。也就是说, import 和 export 命令只能在模块的顶层,不能在代码块之中 (比如,在 if 代码块之中, 或在函数之中)。 这样的设计固然有利于编译器提高效率,但也导致无法在运行时加载模块。在语法上,条 件加载不可能实现。如果 import 命令要取代 Node 的 require 方法,这就形成了一个障碍。 因为 require 是运行时加载模块, import 命令无法取代 require 的动态加载功能。

import ()函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用 。它是运行 时执行,也就是说,运行到这一句时便会加载指定的模块。另外, import ()函数与所加载的 模块没有静态连接关系,这点也与 import 语句不相同。import ()类似于 Node 的 require 方法,区别主要是,前者是异步加载,后者是同步 加载。

  • 按需加载:import ()可以在需要的时候再加载某个模块
  • 条件加载:import ()可以放在 if 代码块中,根据不同的情况加载不同的模块。
  • 动态的模块路径:import ()允许模块路径动态生成。
import(specifier);

if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}

import(f())
.then(...);

import()加载模块成功以后,这个模块会作为一个对象,当作then方法的参数。因此,可以使用对象解构赋值的语法,获取输出接口。

import('./myModule.js')

.then(({export1, export2}) => {

// ...·

});

上面代码中,export1export2都是myModule.js的输出接口,可以解构获得。

如果模块有default输出接口,可以用参数直接获得。

import('./myModule.js')

.then(myModule => {

console.log(myModule.default);

});

上面的代码也可以使用具名输入的形式。

import('./myModule.js')
.then(({default: theDefault}) => {
console.log(theDefault);
});

第 23 章 Module 的加载实现

嘻嘻,剩下的每天看一点

你可能感兴趣的:(前端)