js面试

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 + '<br>'
    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 + '<br>'
        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 (
                <div>hello</div>
            )
        }
    
    }
    
    //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 = `<div>
                        <p>${name}</p>
                     </div>`
    console.log(html)
    
    
    • 解构赋值
    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

推荐阅读更多精彩内容