javascript 总结笔记

179 阅读9分钟

正文:

  • JavaScript简介JavaScript 和java 没个卵的关系。 ECMAScript就是对实现该标准规定的各个方面内容的语言的描述。JavaScript实现了ECMAScript。 所以es5 es6 es7 是ECMAScript5 6 7 的简写。
  • JavaScript在当前五个主要浏览器( IE、 Firefox、 Chrome、 Safari和 Opera)中都得到了不同程度的支持。 所以才有了兼容性的问题
  • 但人类的脚步是向前的。 比如babel 编译器解决了这种差异。统一编译成es5!
  • 推荐在javaScript 中使用 "use strict" 。

"use strict"; //在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。 严格模式禁止这种用法,全局变量必须显式声明。

"use strict";
v = 1; // 报错,v未声明

for (i = 0; i < 2; i++) { // 报错,i未声明
}
//因此, 严格模式下, 变量都必须先用var命令声明, 然后再使用。


function f() { 
return this; //非严格模式下 因为"this"指向全局对象,返回全局对象
}
function f() { 
"use strict"; //这行代码看起来像是字符串,而且也没有赋值给任何变量,但其实它是一个编译指示(pragma),
// 用于告诉支持的 JavaScript引擎切换到严格模式
return this; //严格模式下 this的值为undefined,
}

严格模式下方法入参不能有重复定义的变量、 对象不能有重复的属性。而且有时候  "use strict"可以节省函数执行的时间。 推荐语句以一个分号结尾。

3、 js 基本数据类型字符串 string

var const let数值 
Number Number()真假 
Boolean new Boolean()
数组 Array new Array() | []
对象 Object new Object | {}

NaN isNaN(a) //转成Number类型进行判断 
so alert(isNaN("10")); //false(可以被转换成数值 10)

var num2 = Number(""); //0 
var num4 = Number(true); //1 
var num3 = Number("000011"); //11
//what you find that ?so not use Number use parseInt()

var num2 = parseInt(""); // NaN 
var num1 = parseInt("1234blue");// 1234
var num4 = parseInt(22.5); // 22 
var num5 = parseInt("070"); // 56(八进制数)
var num6 = parseInt("70"); // 70(十进制数)

var num7 = parseInt("0xf"); // 15(十六进制数)

false 的8个值:

'0、 -0、 NaN、 false 、null、 undefined、 new Boolean()问个问题:[] === '' 
[]== ''

结果是什么? why?

typeof 返回的是字符串,有六种可能:

"number""string""boolean""function""undefined""object": {[],{}}

其中 null类型

 typeof null === "object" 

so 判断变量是数组还是对象的时候 要过滤null。

if (isWaitting && isWaitting instanceof Array) {}

举个例子:

var shallowCopy = function(obj) { //一个浅拷贝方法

应该知道为什么有这个方法吧 (数组、对象中地址引用的锅)

if (typeof obj != 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;

}

再来个深拷贝: //gitHub 上有一个deep-assign npm可以install下载 有需要的大家可以搜下

var deepCopy = function(obj) {
if (typeof obj !== 'object') return; //null NaN Math
var newObj = obj instanceof Array ? [] : (!obj ? null : {});
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) :
obj[key];
}
}
return newObj;
}

4、 在html引入js要注意的事项肯定要写在 < script > < /script>里面!。如果浏览器不支持js的话 那用

<noscript ><p> 本页面需要浏览器支持( 启用) JavaScript。 </noscript>

我是至今也没见过浏览器显示这个的。 除非你把浏览器设置了不支持js语法。js一般引入的位置是在body后面而不是header里面。 因为浏览器渲染的过程中是单线程的。 当js加载完成并执行后才能继续往下加载别的。 一句话概括: 就是js加载多的话,入口页面渲染完成所用的时间过多, 体验不好。 这个也引发了后面的前端优化什么的。 尽量不再js中改变dom节点的更改、 减少http的请求、使用img snipe、 根据路由懒加载依赖的文件、 外部引用js文件的时候加defer( 这个属性的用途是表明脚本在执行时不会影响页面的构造。 也就是说, 脚本会被延迟到整个页面都解析完毕后再运行。 因此, 在 < script > 元素中设置 defer 属性, 相当于告诉浏览器立即下载,但延迟执行。

<script type = "text/javascript" defer = "defer"  src = "example1.js" > 
</script> )

拓展:同样与 defer 类似,async 只适用于外部脚本文件, 并告诉浏览器立即下载文件。 但与 defer 不同的是, 标记为 async 的脚本并不保证按照指定它们的先后顺序执行。 例如:

<script type = "text/javascript"  async src = "example1.js" >
< /script> 
<script type = "text/javascript"  async src = "example2.js" >
< /script> 

在以上代码中, 第二个脚本文件可能会在第一个脚本文件之前执行。 因此, 确保两者之间互不依赖 非常重要。指定 async 属性的目的是不让页面等待两个脚本下载和执行, 从而异步加载页面其他内容。为此, 建议异步脚本不要在加载期间修改 DOM。 异步脚本一定会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 事件触发之前或之 后执行。 支持异步脚本的浏览器有Firefox 3.6、 Safari5 和 Chrome5、 来个运算符吧

var a = 0;
a++;
++a;
老生常谈了var a = 0;
a + +1 输出什么?
//1 why?

'' + 1
1 + ''
'1' + '2'
1 + true

输出什么?

var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2; // 输出什么?

0.1 + 0.2 // 输出什么? 应该怎么做才能不丢失精度呢?考虑

6、 Boolean以前常常看到人们判断一个变量是否为空 是否是没定义

if( a != null && typeof a != 'undefined') {}

忘了上面的‘ false 的8个值’ 了嘛? 今天以后就可以这样写

if(a) {} // a 不能是 0 -0 NaN '' false null undefined

特殊情况特殊对待!!!a //强制转化a的类型为Boolean类型 追加:a && b //短路运算 a如果为false 那么b不用判断了 懒!

var c = a || 'fd' //默认赋值 a如果为false 那么c赋值为‘fd’ 

Infinity //了解下就行 我都没遇到过 无穷大
-
-Infinity //无穷大的负数 你说是什么呢?

for (;;) { // 无限循环 doSomething(); } 
for (true) { // 无限循环 doSomething(); } 

continue; break; //只能使用在循环遍历中 // foreach() map()里能用不 ?思考下

7、 函数function函数中的结束: return;函数中没有java的重载。 只有替换函数中没有类但能模拟类//es5

var superClass = function() {
this.name = null;
}

superClass.prototype.setName = function(name) {
this.name = name;
}
superClass.prototype.getName = function() {
return this.name;
}
var superC = new superClass();
superC.setName('fd');
superC.getName();

var subClass = superClass.prototype; //继承
subClass.prototype.mackSound = function() {
this.name = '电风扇';
}
//es6 
class superClass2 {
constructor() {
this.name = name;
}
getName() {
return this.name;
}

setName(name) {
this.name = name;
}
show() {
console.log(this.name);
}
}
var as = new superClass2();

as.setName('fdsf');

as.show();
class superClass3 extends superClass2 { //继承
}
var sdf = new superClass3();

8、闭包

(function sd() { console.log(12) })()//自动执行函数 
function adds() {
var something = "ssh";
var another = [1, 2, 3];

function doSomething() {
alert(something);
}

function doAnother() {
alert(another.join(","));
}
return {
 doSomething: doSomething,
 doAnother: doAnother
};
}
var fn = adds();
fn.doSomething(); // cool
fn.doAnother(); // 1 ! 2 ! 3

//私有数据变量something和another,以及doSomething() 和doAnother() 两个内部函数, //它们的语法作用域(而这就是闭包)也就是foo() 的内部作用域。


var a = (function() {
var i = 0;
return function() {
return i++;
}
})()
var b = (function(name) {
var i = 0;

function identify() {
alert(name.toUpperCase());
}

function add() {
return i++;
}
return{
identify:identify,
add:add
}
})('ssh')

9、上下文执行环境 作用域

var a = 10;
function sum(b) {
return a + b;
}

function add() {
var a = 20;
var c = sum(30);
return c;
}

add(); //40

为什么不是50呢? 我的理解是: 当函数没有执行前,函数中的变量就已经确定指向。比如sum()中 当没有调用add()方法的时候,sum()方法里的a已经的指针已经指向了全局作用域下定义的var a = 10;而非调用时候的var a = 20;

总结: 函数创建的时候 就已经确定了作用域 . 要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记.上面描述的只是跨一步作用域去寻找。如果跨了一步,还没找到呢?——接着跨!——一直跨到全局作用域为止。要是在全局作用域中都没有找到,那就是真的没有了。这个一步一步“跨”的路线,我们称之为——作用域链。

我们拿文字总结一下取自由变量时的这个“作用域链”过程:(假设a是自由量) 第一步,现在当前作用域查找a,如果有则获取并结束。如果没有则继续; 第二步,如果当前作用域是全局作用域,则证明a未定义,结束; 否则继续; 第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域; 第四步,跳转到第一步。

var x = {
name:'ssh',
age:18
}

var fn = function(){
console.log(this);
console.log(this.name);
}
fn.call(x);//fn.apply(x)

//当一个函数被call和apply调用时,this的值就取传入的对象的值。既然说到了call apply 那就再继续说点吧用call()apply() 可以强制指定this的指向。那么this又是什么玩意呢?它代表当前作用域本身。 在全局作用域下、在函数块级作用域下。有一天看到了一个这种写法

var arr = [1,2,4,5,7,34]
Math.max.call(null,1,2,4,5,7,34) //34
Math.max.apply(null,arr) //34

从上面可以看出两个东西: 1、call apply 接收参数的格式不同 怎么不同? 自己看! 2、重新定义了Math.max函数的this指向。
Es6:Math.max(...arr) //了解下就行。

上下文执行环境:就是当前函数执行时候所处的环境。 环境应该都知道 java开发前都要先配置jre jdk开发环境 咱们的javascript中 函数运行时候也要有自己的环境。 当一个函数执行完成后当前上下文执行环境就会被销毁。 至于涉及到堆栈方面的知识,技术有限,说不出来。。。。。想知道这样面的知识可以自行google。

10.地址引用遇到的坑地址引用的数据类型有:

array object   
Var arr = [1,2];

自己理解: 变量arr 中其实放的不是数据 1,2 。寄存的只是一个hash地址。该地址指向存放[1,2]的数据池。如果重新给arr赋值 arr = [1,3] 则arr指针重新指向了另一个数据池[1,3] 。但是:当 var arr= [1,2] ;arr.push(4); 这时候指针是不变化的。变化的是指针定向的数据池里的数据。这点要注意。

记得有个面试题如下:

var arr = [1, 2, 3];
var c = arr;
arr = [1, 2];
console.log(c); //[1, 2, 3]
arr.push(4);
console.log(arr); //[1, 2,4]
console.log(c); //[1, 2, 3]

var arr2= [1, 2, 3];
var c2 = arr2;
arr2.push(4);
console.log(arr2); //[1, 2, 3,4]
console.log(c); //[1, 2, 3]

var obj = [{ name: 'ssh' }, { name: 'ssh2'
}]
var obj2 = [];
obj.forEach((item, index) => {
item.name == 'ssh' ? obj2.push(item) : false;
})
obj2[0].name = 'ssh2222';
console.log(obj) // 有变化嘛如果有 why?

11、Math常用的几种:

Math.abs() //返回数的绝对值。
Math.floor() //对数进行下舍入。 地板 懂?
Math.ceil() //对数进行上舍入。 天花板 懂?
Math. max(x,y) //返回 x 和 y 中的最高值。
Math.min(x,y)// 返回x 和 y 中的最低值。
Math. pow(x,y) //返回 x 的 y 次幂。
Math. random()// 返回 0 ~ 1 之间的随机数。
Math. round(x) //把数四舍五入为最接近的整数。
Math.trunc(2.643) //2 截取小数点前面的数字

12、DateVar a = new Date();

本地全部时间格式:
toLocaleString(); // "2017/11/16 下午4:27:09"
a.toLocaleDateString() //"2017/11/16"
toLocaleTimeString() // "下午4:27:09"
getFullYear() //2017
getTime()
getTime() 返回从 197011 日至今的毫秒数。//+new Date()
getDay()
//如何使用 getDay() 和数组来显示星期几,而不仅仅是数字  //周日 =>0 ?

13、 Array 方法主要几点就行了:push pop 都是从数组后面开始处理数据的 (栈)

shift unshift 从头开始 (队列)
slice() 相当于重新定义了个数组 即不同的指针
splice() //从数组中移除一个或多个数组,并用新的item代替他们 返回被替换的数组

var a = ['a','b','c','d'];
var b = a.splice(1,3,'f');
a// ["a", "f"]
b// ["b", "c","d"]

14、数组去重的正确编写姿势(面试很爱问!)//使用数组的indexOf()方法可以很简单的达到目的。

Array.prototype.unique = function() {
// 创建一个新的临时数组,用于保存输出结果
var n = []; 
// 遍历当前数组
for (var i = 0; i < this.length; i++) {
// 如果当前数组的第i个元素已经保存进了临时数组,那么跳过,否则把当前项push到临时数组里面
if (n.indexOf(this[i]) == -1) n.push(this[i]);
// if (!n.includes(this[i]))
n.push(this[i]);
}
return n;
}

最快姿势//把已经出现过的元素通过下标的形式存入一个Object内。下标的引用的实现原理利用的是哈希算法,要比用indexOf()搜索数组快的多。

Array.prototype.unique = function() {
// n为hash表,r为临时数组
var n = {}, r = [];
for (var i = 0; i < this.length; i++) {
// 如果hash表中没有当前项
if (!n[this[i]]) {
// 存入hash表
n[this[i]] = true;
// 把当前数组的当前项push到临时数组里面
r.push(this[i]); 
}
}
return r;
}

但从耗时的角度来讲,这是最优的一种解决方式。但是从内存占用角度来说,这并不是最优的,因为多了一个hash表。这就是所谓的空间换时间(世间安得双全法?)。中庸姿势 //推荐

Array.prototype.unique = function() {
this.sort();
var re = [this[0]];
for (var i = 1; i < this.length; i++) {
if (this[i] !== re[re.length - 1]) {
re.push(this[i]);
}
}
return re;
}

// [...new Set([1,1,3,4,3,56,6])] 最简单的方法 既然提到了new Set() 那么就延伸点别的吧删除数组中指定的项:

Array.prototype.$remove = function (v) {
const index = this.indexOf(v) //this代表数组本身
if (index !== -1) this.splice(index, 1)
return this;
}

const arr = [1, 2, 3]
arr.$remove(2) //下标从1开始

Array.prototype.$delete = function (v){ var set = new Set(this);// this 代表该数组本身 set .delete(v); return [...set] } var arr = [1, 2, 3] arr.remove(2)// [1, 3]

es6 新添加了很多方法如

foreach、map 、filter 、reduce 、objectAssign 、copy、 includes 、indexof 、Array.isArray() 、Array.from() new Set() new Map() 等 。

在此不做详细的说明,想了解的可以自行google。

15、单线程中的异步我们还经常遇到setTimeout(fn,0)这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢?答案是不会的,setTimeout(fn,0)的含义是, 指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。 举例说明:

console.log('先执行这里');
setTimeout(() => {
console.log('执行啦1')
},10);
setTimeout(() => {
console.log('执行啦2')
},0);

console.log('最后执行这里');

16、异步(重点 面试还是爱问!)Promise:异步下面是一个Promise对象的简单例子。

function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}

timeout(100).then((value) => {
console.log(value); //'done'
});

上面代码中,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolve,就会触发then方法绑定的回调函数。 Promise 实例

const wait = function(val) {
    // 定义一个 promise 对象
    const promise = new Promise((resolve, reject) => {
            // 将之前的异步操作,包括到这个 new Promise 函数之内
            const task = function() {
                if (val) {
                    console.log('执行完成');
                    resolve(true)
                } else { console.log('执行失败');
                    reject(false) }
                // callback 中去执行 resolve 或者 reject
            }
            setTimeout(task, 2000)
        })
        // 返回 promise 对象
    return promise
}

const w = wait (1);
var a = 0;
w.then((val) => {
    console.log('ok 1' + val);
    a = 231;
    console.log(a);
    return w;
}, (val) => {
    console.log('err 1' + val)
}).then((val) => {
    console.log('ok 2' + val);
    var b = a + 2;
    console.log(b)
}, (val) => {
    console.log('err 2' + val)
})

async :异步依次读取两个文件:

var asyncReadFile = async function () {
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};//注意 封装的readFile ()方法要返回promise对象

async function getStockPriceByName(name) {
var symbol = await getStockSymbol(name);
var stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result) {
console.log(result);
});

使用注意点: 第一点,前面已经说过,await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。

async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}

// 另一种写法 (推荐)
async function myFunction() {
await somethingThatReturnsAPromise().catch(function
(err) {
console.log(err);
});}

因为以下所有的代码都会用到Promise,因此干脆在所有介绍之前,先封装一个Promise,封装一次,为下面多次应用。

const fs = require('fs')
const path = require('path')  // 后面获取文件路径时候会用到
const readFilePromise = function (fileName) {
    return new Promise((resolve, reject) => {
        fs.readFile(fileName, (err, data) => {
            if (err) {
                reject(err)  // 注意,这里执行 reject 是传递了参数,后面会有地方接收到这个参数
            } else {
                resolve(data.toString())  // 注意,这里执行 resolve 时传递了参数,后面会有地方接收到这个参数
            }
        })
    })
}

那么前面步骤return的值会被当做参数传递给后面步骤的函数,如下面代码中的a就接收到了return JSON.parse(data).a的值

const fullFileName = path.resolve(__dirname, '../data/data2.json')
const result = readFilePromise(fullFileName)
result.then(data => {
    // 第一步操作
    console.log(data)
    return JSON.parse(data).a  // 这里将 a 属性的值 return
}).then(a => {
    // 第二步操作
    console.log(a)  // 这里可以获取上一步 return 过来的值

})

我们知道then会接收两个参数(函数), 第一个参数会在执行resolve之后触发(还能传递参数), 第二个参数会在执行reject之后触发(其实也可以传递参数,和resolve传递参数一样), 但是上面的例子中,我们没有用到then的第二个参数。 这是为何呢 ———— 因为不建议这么用。对于Promise中的异常处理,我们建议用catch方法,而不是then的第二个参数。请看下面的代码,以及注释。

const fullFileName = path.resolve(__dirname, '../data/data2.json')
const result = readFilePromise(fullFileName)
result.then(data => {
    console.log(data)
    return JSON.parse(data).a;
}).then(a => {
    console.log(a)
}).catch(err => {
    console.log(err.stack)  // 这里的 catch 就能捕获 readFilePromise 中触发的 reject ,而且能接收 reject 传递的参数
})

读取两个文件data1.json和data2.json,现在我需要一起读取这两个文件,等待它们全部都被读取完,再做下一步的操作。此时需要用到Promise.all。(以前记得做个打印报表的功能。当时报表数据差不多几万条。所以想到了分页打印。比如分5页,每页200条。那么打印的时候肯定要按顺序打印。即1-200,201-300以此打印。那么就写了个方法,循环遍历整个数据,每200条放到一个方法中,结果把5个方法再push到一个数组中。记得当时用的是$q 里面的all()方法来执行的。 // Promise.all 接收一个包含多个 promise 对象的数组

Promise.all([result1, result2]).then(datas => {
    // 接收到的 datas 是一个数组,result1, result2 依次执行,返回包含了多个 promise的内容
    console.log(datas[0])
    console.log(datas[1])
})

  var funcA = function(){
                console.log("funcA");
                return "hello,funA";
            }
            var funcB = function(){
                console.log("funcB");
                return "hello,funB";
            }
          var a = [funcA ,funcB];
          Promise.all([funcA , funcB]).then(datas => {   //  Promise.all(a).then()
    // 接收到的 datas 是一个数组,依次包含了多个 promise 返回的内容
    console.log(datas[0])
    console.log(datas[1])
})

复读取两个文件data1.json和data2.json,现在我需要一起读取这两个文件,但是只要有一个已经读取了,就可以进行下一步的操作。此时需要用到Promise.race// Promise.race 接收一个包含多个 promise 对象的数组

Promise.race([result1, result2]).then(data => {
// data 即最先执行完成的 promise 的返回值
console.log(data)
})

栗子:

var uploadQuestion = function(questions) {
var promises = [];
angular.forEach(questions, function(question) {
var promise = $http({ //
url: 'upload/question',
method: 'POST',
data: question
});
promises.push(promise);

});
return $q.all(promises);
}

$q:异步

var iWantResolve = true;

function es6promise() {
    return $q(function(resolve, reject) {
        $timeout(function() {
            if (iWantResolve) {
                resolve("es6promise resolved");
            } else {
                reject("es6promise reject");
            }
        }, 1000)
    })
}
es6   promise()
    .then(function(data) {
        console.log(data);
    })
    .catch(function(err) {
        console.log(err);
    });

// if(iWantResolve == true) output: es6promise resolved
// if(iWantResolve = false) output: es6promise reject
function commonJsPromise() {
    var deferred = $q.defer();
    $timeout(function() { //可以没有
        deferred.notify("commonJS notify");
        if (iWantResolve) {
            deferred.resolve("commonJS resolved");
        } else {
            deferred.reject("commonJS reject");
        }

    }, 500);
    return deferred.promise;
}

commonJsPromise()
    .then(function /** success callback**/ (data) {
        console.log(data);

    }, function /** error callback **/ (err) {
        console.log(err);
    }, function /** progress callback **/ (update) {
        console.log(update);
    });

// if(iWantResolve == true) output: commonJS notify commonJS resolved
// if(iWantResolve = false) output: commonJS notify commonJS reject
$q.all([es6promise(), commonJsPromise()])
    .then(function (dataArr) {
        console.log("$q.all: ", dataArr);
    }, function (err) {
        console.log("$q.all: ", err)
    }, function /** unnecessary **/ (update) {
        console.log("$q.all", update);
    });
// if(iWantResolve == true) output: $q.all:  ["es6promise resolved", "commonJS resolved"]
// if(iWantResolve = false) output: $q.all:  es6promise reject

大家都是到 jquery v1.5 之后$.ajax()返回的是一个deferred对象,而这个deferred对象和我们现在正在学习的Promise对象已经很接近了,但是还不一样。那么 ———— deferred对象能否转换成 ES6 的Promise对象来使用??答案是能!需要使用Promise.resolve来实现这一功能,请看以下代码:// 在浏览器环境下运行,而非node 环境

const jsPromise =
Promise.resolve($.ajax('/whatever.json'))
jsPromise.then(data => {
// ...
})//ajax 异步执行方法

其实,在我们的日常开发中,这种将thenable转换为Promise的需求并不多。真正需要的是,将一些异步操作函数(如fs.readFile)转换为Promise(就像文章一开始readFilePromise做的那样)而且then必须返回一个promise,同一个 promise 的then可以调用多次(链式)” ——— 这两句话说明了一个意思 ——— then肯定要再返回一个promise,要不然then后面怎么能再链式的跟一个then呢?

const w9991 = wait(1)
w9991.then((val) => {
console.log('ok 1'+val);// ok 1true ok 2true //2不是undefined
return w9991;
}).then((val) => {
console.log('ok 2'+val); 
})

eg:function readFilePromise() {
    return new Promise((resolve, reject) => {
        resolve(setTimeout(() => {
            console.log(1111);
        }));
    })
};

const readFileAsync = async function() {
        const f1 = await readFilePromise();
        const f2 = await readFilePromise();
        return 'done' // 先忽略,后面会讲到
    }
    // 执行
const result = readFileAsync() //1111 1111
result.then(data => {
    console.log(data) // done
})

使用async-await的不同和好处第一,await后面不能再跟thunk函数,而必须跟一个Promise对象(因此,Promise才是异步的终极解决方案和未来)。跟其他类型的数据也OK,但是会直接同步执行,而不是异步。第二,执行const result = readFileAsync()返回的是个Promise对象,而且上面代码中的return 'done'会直接被下面的then函数接收到

result.then(data => {
console.log(data) // done
})

第三,从代码的易读性来将,async-await更加易读简介,也更加符合代码的语意。而且还不用引用第三方库,也无需学习Generator那一堆东西,使用成本非常低。

总结:

异步操作 : promise \generator yield \async-await\$q

17、正则 18、window 对象 19、模块化封装方法(exports、 module exports 、export default)