• 前端综合学习笔记---异步、ES6/7、Module、Promise同步 vs 异步


    个人bolg地址

    同步 vs 异步

    先看下面的 demo,根据程序阅读起来表达的意思,应该是先打印100,1秒钟之后打印200,最后打印300。但是实际运行根本不是那么回事

    console.log(100)
    setTimeout(function () {
        console.log(200)
    }, 1000)
    console.log(300)

    再对比以下程序。先打印100,再弹出200(等待用户确认),最后打印300。这个运行效果就符合预期要求。

    console.log(100)
    alert(200)  // 1秒钟之后点击确认
    console.log(300)

     这俩到底有何区别?—— 第一个示例中间的步骤根本没有阻塞接下来程序的运行,而第二个示例却阻塞了后面程序的运行。前面这种表现就叫做 异步(后面这个叫做 同步 ),即不会阻塞后面程序的运行。

    异步和单线程

    JS 需要异步的根本原因是 JS 是单线程运行的,即在同一时间只能做一件事,不能“一心二用”。

    一个 Ajax 请求由于网络比较慢,请求需要 5 秒钟。如果是同步,这 5 秒钟页面就卡死在这里啥也干不了了。异步的话,就好很多了,5 秒等待就等待了,其他事情不耽误做,至于那 5 秒钟等待是网速太慢,不是因为 JS 的原因。

    讲到单线程,我们再来看个真题:

    第一问:下面代码的执行过程和结果

    var a = true;
    setTimeout(function(){
        a = false;
    }, 100)
    while(a){
        console.log('while执行了')
    }

    这是一个很有迷惑性的题目,不少候选人认为100ms之后,由于a变成了false,所以while就中止了,实际不是这样,因为JS是单线程的,所以进入while循环之后,没有「时间」(线程)去跑定时器了,所以这个代码跑起来是个死循环!

    前端异步的场景

    • 定时 setTimeout setInverval
    • 网络请求,如 Ajax<img>加载

    Ajax 代码示例

    console.log('start')
    $.get('./data1.json', function (data1) {
        console.log(data1)
    })
    console.log('end')

    img 代码示例(常用于打点统计)

    console.log('start')
    var img = document.createElement('img')
    // 或者 img = new Image()
    img.onload = function () {
        console.log('loaded')
        img.onload = null
    }
    img.src = '/xxx.png'
    console.log('end')

    ES6/7 新标准的考查

    第二问:ES6 箭头函数中的this和普通函数中的有什么不同

    箭头函数

    箭头函数是 ES6 中新的函数定义形式,function name(arg1, arg2) {...}可以使用(arg1, arg2) => {...}来定义。示例如下:

    // JS 普通函数
    var arr = [1, 2, 3]
    arr.map(function (item) {
        console.log(index)
        return item + 1
    })
    
    // ES6 箭头函数
    const arr = [1, 2, 3]
    arr.map((item, index) => {
        console.log(index)
        return item + 1
    })

    箭头函数存在的意义,第一写起来更加简洁,第二可以解决 ES6 之前函数执行中this是全局变量的问题,看如下代码

    function fn() {
        console.log('real', this)  // {a: 100} ,该作用域下的 this 的真实的值
    var arr = [1, 2, 3] // 普通 JS arr.map(function (item) { console.log('js', this) // window 。普通函数,这里打印出来的是全局变量,令人费解 return item + 1 }) // 箭头函数 arr.map(item => { console.log('es6', this) // {a: 100} 。箭头函数,这里打印的就是父作用域的 this return item + 1 }) } fn.call({a: 100})

    第三问:ES6 模块化如何使用?

    ES6 中模块化语法更加简洁,直接看示例。

    如果只是输出一个唯一的对象,使用export default即可,代码如下

    // 创建 util1.js 文件,内容如
    export default {
        a: 100
    }
    
    // 创建 index.js 文件,内容如
    import obj from './util1.js'
    console.log(obj)

    如果想要输出许多个对象,就不能用default了,且import时候要加{...},代码如下

    // 创建 util2.js 文件,内容如
    export function fn1() {
        alert('fn1')
    }
    export function fn2() {
        alert('fn2')
    }
    
    // 创建 index.js 文件,内容如
    import { fn1, fn2 } from './util2.js'
    fn1()
    fn2()

    第四问:ES6 class 和普通构造函数的区别

    class

    class 其实一直是 JS 的关键字(保留字),但是一直没有正式使用,直到 ES6 。 ES6 的 class 就是取代之前构造函数初始化对象的形式,从语法上更加符合面向对象的写法。例如:

    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())

    用 ES6 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())

    注意以下几点,全都是关于 class 语法的:

    • class 是一种新的语法形式,是class Name {...}这种形式,和函数的写法完全不一样
    • 两者对比,构造函数函数体的内容要放在 class 中的constructor函数中,constructor即构造器,初始化实例时默认执行
    • class 中函数的写法是add() {...}这种形式,并没有function关键字

    使用 class 来实现继承就更加简单了,至少比构造函数实现继承简单很多。看下面例子

    JS 构造函数实现继承

    // 动物
    function Animal() {
        this.eat = function () {
            console.log('animal eat')
        }
    }
    //
    function Dog() {
        this.bark = function () {
            console.log('dog bark')
        }
    }
    Dog.prototype = new Animal()
    // 哈士奇
    var hashiqi = new Dog()

    ES6 class 实现继承

    class Animal {
        constructor(name) {
            this.name = name
        }
        eat() {
            console.log(`${this.name} eat`)
        }
    }
    
    class Dog extends Animal {
        constructor(name) {
            super(name) //调用父对象的构造函数
            this.name = name
        }
        say() {
            console.log(`${this.name} say`)
        }
    }
    const dog = new Dog('哈士奇')
    dog.say()
    dog.eat()

    注意以下两点:

    • 使用extends即可实现继承,更加符合经典面向对象语言的写法,如 Java
    • 子类的constructor一定要执行super(),以调用父类的constructor

    第五问:ES6 中新增的数据类型有哪些?

    Set 和 Map

    Set 和 Map 都是 ES6 中新增的数据结构,是对当前 JS 数组和对象这两种重要数据结构的扩展。由于是新增的数据结构,目前尚未被大规模使用,但是作为前端程序员,提前了解是必须做到的。先总结一下两者最关键的地方:

    • Set 类似于数组,但数组可以允许元素重复,Set 不允许元素重复
    • Map 类似于对象,但普通对象的 key 必须是字符串或者数字,而 Mapkey 可以是任何数据类型

    Set

    // 例1
    const set = new Set([1, 2, 3, 4, 4]);
    console.log(set) // Set(4) {1, 2, 3, 4}
    
    // 例2
    const set = new Set();
    [2, 3, 5, 4, 5, 8, 8].forEach(item => set.add(item));
    for (let item of set) {
      console.log(item);
    }
    // 2 3 5 4 8

    Set 实例的属性和方法有

    • size:获取元素数量。
    • add(value):添加元素,返回 Set 实例本身。
    • delete(value):删除元素,返回一个布尔值,表示删除是否成功。
    • has(value):返回一个布尔值,表示该值是否是 Set 实例的元素。
    • clear():清除所有元素,没有返回值。
    const s = new Set();
    s.add(1).add(2).add(2); // 添加元素
    
    s.size // 2
    
    s.has(1) // true
    s.has(2) // true
    s.has(3) // false
    
    s.delete(2);
    s.has(2) // false
    
    s.clear();
    console.log(s);  // Set(0) {}

    Set 实例的遍历,可使用如下方法

    • keys():返回键名的遍历器。
    • values():返回键值的遍历器。不过由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys()values()返回结果一致。
    • entries():返回键值对的遍历器。
    • forEach():使用回调函数遍历每个成员。
    let set = new Set(['aaa', 'bbb', 'ccc']);
    
    for (let item of set.keys()) {
      console.log(item);
    }
    // aaa
    // bbb
    // ccc
    
    for (let item of set.values()) {
      console.log(item);
    }
    // aaa
    // bbb
    // ccc
    
    for (let item of set.entries()) {
      console.log(item);
    }
    // ["aaa", "aaa"]
    // ["bbb", "bbb"]
    // ["ccc", "ccc"]
    
    set.forEach((value, key) => console.log(key + ' : ' + value))
    // aaa : aaa
    // bbb : bbb
    // ccc : ccc

    Map

    Map 的用法和普通对象基本一致,先看一下它能用非字符串或者数字作为 key 的特性。

    const map = new Map();
    const obj = {p: 'Hello World'};
    
    map.set(obj, 'OK')
    map.get(obj) // "OK"
    
    map.has(obj) // true
    map.delete(obj) // true
    map.has(obj) // false

    需要使用new Map()初始化一个实例,下面代码中set get has delete顾名即可思义(下文也会演示)。其中,map.set(obj, 'OK')就是用对象作为的 key (不光可以是对象,任何数据类型都可以),并且后面通过map.get(obj)正确获取了。 Map 实例的属性和方法如下:

    • size:获取成员的数量
    • set:设置成员 key 和 value
    • get:获取成员属性值
    • has:判断成员是否存在
    • delete:删除成员
    • clear:清空所有
    const map = new Map();
    map.set('aaa', 100);
    map.set('bbb', 200);
    
    map.size // 2
    
    map.get('aaa') // 100
    
    map.has('aaa') // true
    
    map.delete('aaa')
    map.has('aaa') // false
    
    map.clear()

    Map 实例的遍历方法有:

    • keys():返回键名的遍历器。
    • values():返回键值的遍历器。
    • entries():返回所有成员的遍历器。
    • forEach():遍历 Map 的所有成员。
    const map = new Map();
    map.set('aaa', 100);
    map.set('bbb', 200);
    
    for (let key of map.keys()) {
      console.log(key);
    }
    // "aaa"
    // "bbb"
    
    for (let value of map.values()) {
      console.log(value);
    }
    // 100
    // 200
    
    for (let item of map.entries()) {
      console.log(item[0], item[1]);
    }
    // aaa 100
    // bbb 200
    
    // 或者
    for (let [key, value] of map.entries()) {
      console.log(key, value);
    }
    // aaa 100
    // bbb 200

    Promise

    PromiseCommonJS 提出来的这一种规范,有多个版本,在 ES6 当中已经纳入规范,原生支持 Promise 对象,非 ES6 环境可以用类似 BluebirdQ 这类库来支持。

    Promise 可以将回调变成链式调用写法,流程更加清晰,代码更加优雅。

    简单归纳下 Promise三个状态两个过程一个方法,快速记忆方法:3-2-1

    三个状态:pendingfulfilledrejected

    两个过程:

    • pending→fulfilled(resolve)
    • pending→rejected(reject)

    一个方法:then

    当然还有其他概念,如catch、 Promise.all/race,这里就不展开了。

    关于 ES6/7 的考查内容还有很多,本小节就不逐一介绍了,如果想继续深入学习,可以在线看《ES6入门》。

  • 相关阅读:
    彻底领悟javascript中的exec与match方法
    javascript doT 使用
    pluginstorage 插件
    html5离线储存,application cache,manifest使用体验
    window.location.hash属性介绍 ajax后退按钮失效问题
    控制textarea光标移到末尾
    webkitanylink 谷歌浏览器CSS之A:HOVER
    正则表达式详细参考文档
    复杂应用的 CSS 性能分析和优化建议
    seaJs api 帮助文档
  • 原文地址:https://www.cnblogs.com/yuanziwen/p/8533590.html
Copyright © 2020-2023  润新知