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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,621评论 2 17
  • 单例模式 适用场景:可能会在场景中使用到对象,但只有一个实例,加载时并不主动创建,需要时才创建 最常见的单例模式,...
    Obeing阅读 2,017评论 1 10
  • 1. JavaScript中如何检测一个变量是一个String类型?请写出函数实现 2. 请用js去除字符串空格?...
    王帅同学阅读 233评论 0 0
  • 1.什么是闭包?举例说明从作用域链谈闭包闭包就是能够读取其他函数内部变量的函数,闭包实现累加效果 function...
    大佬_娜阅读 524评论 0 0
  • 1.如何准确判断一个变量是数组类型 2.写一个原型链继承的例子 3.描述new一个对象的过程 4.zepto(或其...
    晓綾阅读 5,694评论 0 1