js面试技巧
作用域和闭包
- 函数声明
fn() //不会报错
function fn(){
// 声明. 变量提升
}
fn1(). //报错 undefined
var fn1 = function(){
// 表达式
}
-
执行上下文
- 范围:一段script或者一个函数,针对这个范围都会生成一个执行上下文
- 全局:变量定义,函数声明(js执行之前会把变量定义,函数声明先拿出来)
- 函数:变量定义,函数声明,this,arguments(函数执行之前,会把变量定义,函数声明,this,arguments拿出来)
console.log(a) //undefined
var a = 100
fn('zhang') //zhang 20
function fn(name){
age = 20
console.log(name,age)
var age
}
-
this
- this要在执行时才能确认值,定义时无法确认
var a = { name : 'A' fn : function(){ console.log(this.name) } } a.fn() // this === a a.fn.call({name:'b'}) //this === {name : 'b'} var fn1 = a.fn fn1() // this === window
- 作为构造函数执行
function Foo(name){ // 相当于生成了一个this的空对象 this = {} this.name = name return this } var f = new Foo('zhang')
- 作为对象属性执行
var obj = { name :'a' printName : function(){ console.log(this.name) } } obj.printName() //作为对象属性执行,this指向这个对象
- 作为普通函数执行
function fn(){ console.log(this) } fn() //this === window
- call apply bind
function fn1(name,age){ console.log(this) } fn1.call({x:100},'zhang',20) //{x:100} fn1.apply({x:100},['zhang',20]) var fn2 = function (name,age){ alert(name) console.log(this) }.bind({y:200}) fn2('zhang',20)
-
作用域
- js没有块级作用域
- 只有函数和全局作用域
if (true){ var name = 'zhang' } console.log(name) // zhang //函数和全局作用域 var a = 100 function fn(){ //函数外部是不能得到函数中的a var a = 200 console.log('fn',a) } console.log('global',a) //100 fn() // 200
- 作用域链
var a = 100 function fn(){ var b = 200 //当前作用域没有定义的变量,即“自由变量”,那么到函数的父级作用域寻找,这个是在函数定义的时候,寻找父级作用域,并不是在函数执行的时候寻找 console.log(a) console.log(b) } fn()
- 作用域链 一个自由变量,一直向父级作用域寻找
```js
var a = 100
function f1(){
var b = 200
function f2(){
var c = 300
console.log(a) //自由变量
console.log(b) //自由变量
console.log(c)
}
f2()
}
f1()
```
```js
var i,a
for ( i = 0; i < 10 ; i ++){
a = document.createElement('a')
a.innerHTML = i + '
'
a.addEventListener('click',function(e){
e.preventDefault()
alert(i) // 这个地方每一个i都是10
})
document.body.appendChild(a)
}
var i
for ( i = 0; i <10; i ++){
(function(i){ // 将每一个i传值到这个函数中,这里是一个函数作用域,声明了10个函数
var a = document.createElement('a')
a.innnerHTML = i + '
'
a.addEventListener('click',function(e){
e.preventDefault()
alert(i)
})
})(i)
}
```
- 如何理解作用域
- 自由变量
- 作用域链,即自由变量的查找
- 闭包的两个场景
```js
//封装变量,收敛权限
function isFirstLoad(){
var _list = []
return function(id){
if (_list.indexOf(id) >= 0){
return false
}else{
_list.push(id)
return true
}
}
}
var firstLoad = isFirstLoad()
firstLoad(10) //true
firstLoad(10) //false
firstLoad(20) //true
```
原型
-
构造函数
- 构造函数,大写字母开头
- this创建一个空对象,对this做了赋值后将this返回,返回后赋值给变量
- new => 构造函数形成一个实例的过程
function Foo(name,age){
this.name = name
this.age = age
this.class = 'class-1'
//return this // 默认有这一行
}
var f = new Foo('zhang',10)
var f1 = new Foo('lisi',22) //创建多个对象
- 构造函数-扩展
- var a = {} 其实是var a = new Object()的语法糖
- var a = [] 其实是var a = new Array()的语法糖
- function Foo(){} 其实是 var Foo = new Function()
- 使用instanceof判断一个函数是否是一个变量的构造函数(判断一个变量是否是数组,a instanceof Array)
-
原型规则和实例
- 所有的引用类型(数组,对象,函数)都具有对象特性,即可以自由扩展属性(除了null)
var obj = {}; obj.a = 100; var arr = []; arr.a = 100; function fn(){} fn.a = 100;
- 所有的引用类型(数组,对象,函数)都有一个proto属性(隐式原型),属性值是一个普通对象
console.log(obj.__proto__) // 对象上的方法 console.log(arr.__proto__) //打印出来是数组上的方法,slice,sort。。。。等 console.log(fn.__proto__) //ƒ () { [native code] }
- 所有的函数,都有一个prototype(显示原型)属性,属性值也是一个普通的对象
console.log(fn.prototype) //arguments, caller,constructor
- 所有的引用类型(数组,对象,函数),proto属性值指向它的构造函数的 prototype 属性值
console.log(obj.__proto__ === Object.prototype)
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的proto(即它的构造函数的prototype)中寻找。
function Foo(name,age){ this.name = name } Foo.prototype.alertName = function(){ alert(this.name) } var f = new Foo('zhangsan') f.printName = function(){ console.log(this.name) } f.printName() f.alertName() //通过对象属性的方式执行函数的时候,this指向这个对象 function Student(name) { this.name = name; this.hello = function () { alert('Hello, ' + this.name + '!'); } } let xiaoming = new Student('xiaoming') let xiaohong = new Student('xiaohong') xiaoming.hello === xiaohong.hello. //false 如果我们通过new Student()创建了很多对象,这些对象的hello函 数实际上只需要共享同一个函数就可以了,这样可以节省很多内存。 要让创建的对象共享一个hello函数,根据对象的属性查找原则,我们 只要把hello函数移动到xiaoming、xiaohong这些对象共同的原型 上就可以了,也就是Student.prototype
* 循环对象自身的属性
```js
var item
for (item in f){
if (f.hasOwnProperty(item)){ // 得到f自身的属性
console.log(item)
}
}
```
* constructor 这个原型对象prototype自己还有个属性constructor,指向构造函数本身。通过构造函数创建的对象,从原型链上获得这个属性,指向构造函数本身
```js
xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true
xiaoming.constructor === Student; // true
```
- 原型链
//构造函数
function Foo(name,age){
this.name = name
}
Foo.prototype.alertName = function(){
alert(this.name)
}
//创建实例
var f = new Foo('zhang')
f.printName = function(){
console.log(this.name)
}
f.printName()
f.alertName()
f.toString() // 要去f.__proto__.__proto__中查找
[图片上传失败...(image-85c995-1548123711764)]
- instanceof 用于判断引用类型属于哪个构造函数的方法
- 使用f instanceof Foo
- f的proto一层一层往上,能否对应到Foo.prototype
- 再试判断f instanceof Object
- 如何准确判断一个变量是数组类型
var arr = []
arr instanceof Array //true
typeof arr //object
-
原型链的继承
- call方法
function Student(props) { this.name = props.name || 'Unnamed'; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); } function PrimaryStudent(props) { // 调用Student构造函数,绑定this变量: Student.call(this, props); this.grade = props.grade || 1; } let a = new PrimaryStudent({name : 'xiaohong'}) console.log(a.name) //xiaohong a.hello() //报错 没有得到Student.prototype中的方法
需要把原型链修改为new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null
- 中间对象可以用一个空函数F来实现
// PrimaryStudent构造函数: function PrimaryStudent(props) { Student.call(this, props); this.grade = props.grade || 1; } // 空函数F: function F() { } // 把F的原型指向Student.prototype: F.prototype = Student.prototype; // 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype: PrimaryStudent.prototype = new F(); // 把PrimaryStudent原型的构造函数修复为PrimaryStudent: PrimaryStudent.prototype.constructor = PrimaryStudent; // 继续在PrimaryStudent原型(就是new F()对象)上定义方法: PrimaryStudent.prototype.getGrade = function () { return this.grade; }; // 创建xiaoming: var xiaoming = new PrimaryStudent({ name: '小明', grade: 2 }); xiaoming.name; // '小明' xiaoming.grade; // 2 // 验证原型: xiaoming.__proto__ === PrimaryStudent.prototype; // true xiaoming.__proto__.__proto__ === Student.prototype; // true // 验证继承关系: xiaoming instanceof PrimaryStudent; // true xiaoming instanceof Student; // true //方法的封装 function inherits(Child,Parent){ var F = function(){} F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; }
- 拷贝继承 父对象所有的属性和方法,都拷贝到子对象
function Animal(){} Animal.prototype.spices = 'animal' function extend(child,parent){ var p = parent.prototype var c = parent.prototype for ( var i in p){ c[i]=p[i] } }
- 写一个原型链继承的例子
function Animal(){
this.eat = function(){
console.log('animal eat')
}
}
function Dog(){
this.bark = function(){
console.log('dog bark')
}
}
Dog.prototype = new Animal()
var haha = new Dog()
function Elem(id){
this.elem = document.getElementById(id)
}
Elem.prototype.html = function(val){
var elem = this.elem;
if ( val){
elem.innerHTML = val
return this //链式操作
}else{
return elem.innerHTML
}
}
Elem.prototype.on = function(type,on){
var elem = this.elem
elem.addEventListener(type,fn)
return this
}
var div1 = new Elem('div1')
div1.html('哈哈哈').on('click',function(){
console.log('哈哈哈哈')
})
-
描述new一个对象的过程
- 创建一个新的对象
- this指向这个新对象
- 执行代码,对this赋值
- 返回this
zepto源码中如何使用原型链
ES6语法
- 浏览器环境支持不好(需要开发环境的编译)
问题
- ES6模块化如何使用,开发环境如何打包
- 模块化的基本语法.
```js
util1.js
export default{
a : 100
}
util2.js
export function fun1(){
alert('fn1')
}
export function fun2(){
alert('fn2')
}
index.js
import util1 from './util1.js'
import {fun1,fun2} from './utils2.js'
console.log(util1)
fun1()
fun2()
```
## 开发环境配置
* 开发环境--babel编译
```js
在终端输入npm init 创建包
npm install --save-dev babel-core babel-preset- es2015 babel-preset-latest --registry=https:// registry.npm.taobao.org(下载babel的依赖插件)
```
项目中新建.babelrc文件
```js
{
"preset" : ["es2015","latest"],
"plugins": [
]
}
```
全局安装babel
sudo npm install -g babel-cli
babel 文件地址(查看转译后的代码)
* 关于js众多模块化标准
webpack -- 处理模块化的工具
```js
webpack.config.js 文件内容
module.exports={ //现在没有会自动创建
entry :'./babel.js',
output:{
path : __dirname,
filename : './build/bundle.js'
},
module:{
rules : [{ //所有js的文件除了node_modules中的文件通过babel来编译
test : /\.js?$/,
exclude : /(node_modules)/,
loader:'babel-loader'
}]
}
}
```
```js
package.json
{
"name": "babel-test",
"version": "1.0.0",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^8.0.4",
"babel-preset-es2015": "^6.24.1",
"babel-preset-latest": "^6.24.1",
"webpack": "^4.28.0"
},
"scripts": {
"start":"webpack"
}
}
npm start 命令就是在运行npm webpack的操作
```
rollup.js
* npm init
* npm i rollup rollup-plugin-node-resolve rollup-plugin-babel babel-plugin-external-helpers babel-preset-latest --save-dev
* 可以尽量简化输出之后的大小
* 配置 .babelrc 文件
* 配置rollup.config.js
* rollup功能单一(打包模块化)webpack功能强大
* 工具尽量功能单一,可集成,可扩展
* gulp + rollup wangEditor
```js
.babelrc
{
"presets":[
["latest",{
"es2015":{
"modules" : false //并不关心第三插件的内容,只编译自己的内容
}
}]
],
"plugins":["external-helpers"]
}
rollup.config.js
import babel from 'rollup-plugin-babel'
import resolve from 'rollup-plugin-node-resolve'
export default{
entry :'src/index.js', //入口文件
format : 'umd', // 文件格式,兼容性的规范,umd 文件支持amd的方式,支持common.js的方式
plugins : [
resolve(),
babel ({ // babel编译排除node_modules的文件
exclude:'node_modules/**'
})
],
dest : 'build/bundle.js' //将文件编译到这个文件中
}
```
* 模块化的总结
* 没有模块化
* AMD成为标准,require.js(也有CMD)
* 前端打包工具,是的node.js模块化可以被使用
* ES6出现,想统一现在所有的模块化标准
* 问题解答
* 语法:import export(注意有无default)
* 环境: babel编译ES6语法,模块化工具可用webpack和rollup
* 扩展:对模块化标准统一的期待
-
class和普通构造函数有何区别
- js构造函数
- class
- 语法糖--是一种语法糖的形式
- 继承
class Ad extends React.Component{ constructor(props){ super(props) this.state={ data : [] } } render(){ return (
hello) } } //js构造函数 function MathHandle(x,y){ //构造函数 this.x = x; this.y = y; } MathHandle.prototype.add = function(){ //原型的扩展 return this.x + this.y } var m = new MathHandle(1,2) //实例 console.log(m.add()) //原型中的方法,实例中都有这个方法 // class语法 class MathHandle{ constructor(x,y){ //构造器 this.x = x; this.y = y; } add(){ //相当于原型扩展中的内容 return this.x + this.y } } const m = new MathHandle(1,2) console.log(m.add()) typeof MathHandle //function,本身就是一个函数 MathHandle === MathHandle.prototype.constructor // true m.__proto__ === MathHandle.prototype //true
* class 继承
```js
class Animal{
constructor(name){
this.name = name
}
eat(){
console.log(`${this.name} eat`)
}
}
class Dog extends Animal{
constructor (name){
super(name) //在执行dog构造器之前,先执行animal的构造器
this.name = name
}
say(){
console.log(`${this.name} say`)
}
}
const dog = new Dog('hashiqi')
dog.say()
dog.eat()
```
* class和构造函数写法的区别
* class在写法上更贴合面向对象的写法
* class实现继承更加易读,易理解
* 本质是语法糖,使用prototype
-
Promise的基本使用和原理
- callback hell
function loadImg(src,callback,fail){ var img = document.createElement('img') img.onload = function(){ callback(img) } img.onerror = function(){ fail() } img.src = src } var src = 'https://www.' load(src,function(img){ console.log(img.width) },function(){ console.log('error') })
- promise 语法
function loadImg(src){ const promise = new Promise(function(resolve,reject){ var img = document.createElement('img') img.onload = function(){ resolve(img) } img.onerror = function(){ reject() } img.src = src }) return promise } var src = 'https://www....' var result = loadImg(src) result.then(function(img){ console.log(img.width) },function(){ console.log('failed') }) result.then(function(img){ console.log(img.height) })
-
总结ES6其他常用功能
- let,const
var i = 10; i = 100 var j = 20; j = 200; let i = 10; i = 1000; const j = 20; j = 200; //报错
- 多行字符串/模板变量
const name = 'zhangsan',age = 20 const html = `
${name}
- 解构赋值
const obj = {a:10,b : 20,c: 30} const {a,c} = obj console.log(a) //10 console.log(c) //30 const arr = ['xxx','yyy','zzz'] const [x,y,z] = arr console.log(x) //'xxx' console.log(y) //'yyy' console.log(z) //'zzz'
- 块级作用域
const obj = {a : 100 , b : 200} for ( let item in obj){ console.log(item) } console.log(item) //undefined let a = 10; if (a == 10){ let b = 10 } console.log(b) // b is not defined
* 函数默认参数
```js
function (a,b =0){
}
```
* 箭头函数
```js
const arr = [1,2,3]
arr.map (item => item+1)
arr.map((item,index)=>{
console.log(index)
return item + 1
})
function fn(){
consol.log('real',this) //{a:100}
var arr = [1,2,3]
arr.map(function(item){
console.log('js',this) // window
})
arr.map(function(item){
console.log('es6',this) //{a:100}
})
}
fn.call({a: 100})
```
### 异步
* 什么是单线程,和异步有什么关系
* 单线程 - 只有一个线程,只能做一件事,两段js不能同时执行
```js
// 循环运行期间,js执行和DOM渲染暂时卡顿
var i ,sum = 0;
for ( i =0; i < 10000000000; i++){
sum += i;
}
console.log(sum)
// alert不处理,js执行和DOM渲染暂时卡顿
console.log(1)
alert('hello')
console.log(2)
```
* 原因 - 避免DOM渲染的冲突
* 浏览器需要渲染DOM
* js可以修改DOM结构
* js执行的时候,浏览器DOM渲染会暂停
* 两段js也不能同时执行(都修改DOM就冲突了)
* H5中webworker支持多线程,但是不能访问DOM
* 解决方案 - 异步
```js
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
console.log(400)
console.log(100)
$.ajax({
url : 'xxx',
success:function(result){
console.log(result)
}
})
console.log(300)
console.log(400)
// 代码执行顺序和书写顺序不一致
```
* 异步存在的问题
* 问题一:没有按照书写方式执行,可读性差
* 问题二:callback中不容易模块化
* 什么是event-loop
* 文字解释
* 事件轮询,js实现异步的具体解决方案
* 同步代码,直接执行
* 异步函数放在异步队列中
* 待同步函数执行完毕,轮询执行异步队列的函数
* 实例分析
```js
setTimeout(function(){
// 异步队列
console.log(100)
},1000)
// 主进程
console.log(200)
```
```js
setTimeout(function(){
//隔100ms后放入异步队列
console.log(1)
},100)
setTimeout(function(){
// 立即被放入异步队列
console.log(2)
})
// 主线程
console.log(3)
//打印顺序 3 => 2 => 1
//轮询:主线程执行结束,查看异步队列,先执行console.log(2),再次查看异步队列,执行console.log(1)
```
```js
$.ajax({{
url : 'xxxx',
sunccess: function(result){
// ajax 结束后放入异步队列
console.log('a)
}
})
setTimeout(function(){
// 100ms后放入异步队列
console.log('b')
},100)
setTimeout(function(){
// 立即被放入异步队列
console.log('c')
})
// 主进程
console.log('d')
d => c => b => a 或者 d => c => a => b 或者 d => a => c => b(比较少)
```
* 是否用过jquery的Deferred
* jquery 1.5 的变化
```js
//1.5之前
var ajax = $.ajax({
url : './data.json',
success : function(){
console.log('success1')
console.log('success2')
console.log('success3') // 对修改开放,对扩展封闭
},
error : function(){
console.log('error')
}
})
console.log(ajax) // 返回一个XHR对象
//1.5之后
var ajax = $.ajax('data.json')
ajax.done (function(){
console.log('success1')
})
.fail(function(){
console.log('error') // 对扩展开放,对修改封闭
})
.done(function(){
console.log('success2')
})
1,各自管理自己的代码
2,不需要改动之后,将相关的模块全部都回归测试
console.log(ajax) // 返回一个deferred 对象
// 很像promise的写法
var ajax = $.ajax('data.json')
ajax.then(function(){
console.log('success1') // 成功的函数
},function(){
console.log('error1') // 失败的函数
})
.then(function(){
console.log('success2')
},function(){
console.log('error2')
})
```
1, 无法改变js异步和单线程的本质
2,只能从写法上杜绝callback这种形式
3, 是一种语法糖形式,但是解耦了代码
4, 很好的体现了: 开放封闭原则
* 使用jquery Deferred
```js
// 简单的异步操作
var wait = function(){
var tash = function(){
console.log('执行完成')
}
setTimeout(task,2000)
}
wait()
// 使用jquery Deferred
function waitHandle(){
var dtd = $.Deferred() //创建一个deferred对象
var wait = function(dtd){ // 要求传入一个deferred对象
var task = function(){
console.log('执行完成‘)
dtd.resolve() // 表示异步任务已经完成
// dtd.reject() // 表示异步任务失败或出错
}
setTimeout (task ,2000)
return dtd // 要求返回一个deferred 对象
}
// 这个地方一定要有返回值
return wait(dtd) //相当于dtd经过一个wait函数的加工然后又再次return出去
}
var w = waitHandle()
w.then(function(){
console.log('ok 1') // 成功的函数
},function(){
console.log('err 1') // 失败的函数
}).then(function(){
console.log('ok 2')
},function(){
console.log('err 2')
})
```
* 总结,dtd的api分成两类,用意不用
* 第一类: dtd.resolve, dtd.reject
* 第二类: dtd.then , dtd.done , dtd.fail
* 这两类应该分开,否则后果很严重
* 初步引入Promise 概念
```js
function waitHandle(){
var dtd = $.Deferred()
var wait = function (dtd){
var task = function(){
console.log('执行完成')
dtd.resolve()
}
setTimeout(task ,2000)
return dtd.promise() // 这里返货的是promise而不是deferred对象
}
return wait(dtd)
}
var w = waitHandle() // w 接收一个promise对象,这里是一个promise对象
$.when(w)
.then(function(){
console.log('ok 1')
})
.then(function(){
console.log('ok 2')
})
w.reject() // 执行这句话会直接报错
```
* promise 和Deferred的区别
* promise 只能被动监听,不能主动修改
* promise的基本使用和原理
* 基本语法回顾
```js
function loadImg(src){
const promise = new Promise(function(resolve,reject){
var img = document.createElement('img')
img.onload = function(){
resolve(img)
}
img.onerror = function(){
reject()
}
img.src = src
}
return promise
}
var src = 'https://'
var result = loadImg(src)
result.then(function (img){
console.log(img.width)
return img
},function(){
console.log('failed')
}).then(function(){
console.log(img.height)
})
// 部分网站不支持,到bootCDN 中下载bluebird,引用js
```
* 异常捕获
```js
// 规定: then只接受一个成功参数,最后统一用catch捕获异常
result.then(function(img){
console.log(img.width)
return img
}).then(function(img){
console.log(img.height)
}).catch(function(ex){
// 最后统一捕获异常 catch
console.log(ex)
})
try{
}catch{
}
```
* reject 的捕获
```js
function loadImg(src){
const promise = new Promise(function(resolve,reject){
var img = document.createElement('img')
img.onload = function(){
resolve(img)
}
img.onerror = function(){
reject('图片加载失败')
}
img.src = src
}
return promise
}
var src = 'https://'
var result = loadImg(src)
result.then(function(img){
console.log(img.width)
return img
}).then(function(img){
console.log(img.height)
}).catch(function(ex){
// 最后统一捕获异常 catch
console.log(ex) // 打印信息图片加载失败
})
```
* 多个串联
```js
var src1 = 'https:...'
var src2 = 'https:...'
var result1 = loadImg(src1)
var result2 = loadImg(src2)
// 链式操作
result1.then(function(img1){
console.log('第一张图片加载完毕')
return result2
}).then(function(img2){
console.log('第二张图片')
}).catch(function(ex){ // 只执行第一个异常做处理
console.log(ex)
})
```
* Promise.all 和 Promise.race
```js
// Promise.all 接受一个promise 对象的数组
// 待全部完成之后,统一执行success
Promise.all([result1,result2]).then(datas =>{
// 接受到的datas是一个数组,依次包含了多个promise返回的内容
console.log(datas[0])
console.log(datas[1])
})
// Promise.race 接受一个包含多个promise对象的数组
// 只要有一个完成,就执行success
Promise.race([result1,result2]).then(data=>{
// data即最先执行完成的promise的返回值
console.log(data)
})
// 一起执行的时候,race先执行了,然后all再次执行
```
* promise 标准
* 关于标准
* 任何技术推广使用都需要一套标准支撑
* 任何不符合标准的东西,终将被用户抛弃
* 不要挑战标准,不要自造标准
* 状态变化
* 三种状态: pending fulfilled rejected
* 初始状态是pending
* pending 变成fulfilled,或者是pending 变成rejected
* 状态变化不可逆
* then函数的参数传递和参数返回
* Promise 实例必须实现then这个方法
* then 必须可以接受两个函数作为参数
* then 返回的必须是一个Promise 实例(可以自定义返回一个promise的实例,或者就返回原来的promise实例)
* async/await ES7提案中
* then 只是将callback 拆分了
```js
var w = waitHandle()
w.then(function(){
console.log('ok1')
},function(){
console.log('err 1')
}).then(function(){
console.log('ok2')
},function(){
console.log('err 2')
})
```
* async/await 是最直接的同步写法
```js
const load = async function (){
const result1 = await loadImg(src1)
console.log(result1)
const result2 = await loadImg(src2)
console.log(result2)
}
load()
```
* 语法
* 使用await,函数必须用async标识
* await 后面跟的是一个promise实例
* 需要babel-polyfill
* 使用了promise,并没有和回调冲突
* 完全是同步的写法,再也没有回调函数
* 总结当前js解决异步的方案
* jquery Deferred
* Promise
* async/await
* Generator