个人bolg地址

同步 vs 异步

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

  1. console.log(100)
  2. setTimeout(function () {
  3. console.log(200)
  4. }, 1000)
  5. console.log(300)

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

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

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

异步和单线程

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

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

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

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

  1. var a = true;
  2. setTimeout(function(){
  3. a = false;
  4. }, 100)
  5. while(a){
  6. console.log('while执行了')
  7. }

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

前端异步的场景

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

Ajax 代码示例

  1. console.log('start')
  2. $.get('./data1.json', function (data1) {
  3. console.log(data1)
  4. })
  5. console.log('end')

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

  1. console.log('start')
  2. var img = document.createElement('img')
  3. // 或者 img = new Image()
  4. img.onload = function () {
  5. console.log('loaded')
  6. img.onload = null
  7. }
  8. img.src = '/xxx.png'
  9. console.log('end')

ES6/7 新标准的考查

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

箭头函数

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

  1. // JS 普通函数
  2. var arr = [1, 2, 3]
  3. arr.map(function (item) {
  4. console.log(index)
  5. return item + 1
  6. })
  7.  
  8. // ES6 箭头函数
  9. const arr = [1, 2, 3]
  10. arr.map((item, index) => {
  11. console.log(index)
  12. return item + 1
  13. })

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

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

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

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

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

  1. // 创建 util1.js 文件,内容如
  2. export default {
  3. a: 100
  4. }
  5.  
  6. // 创建 index.js 文件,内容如
  7. import obj from './util1.js'
  8. console.log(obj)

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

  1. // 创建 util2.js 文件,内容如
  2. export function fn1() {
  3. alert('fn1')
  4. }
  5. export function fn2() {
  6. alert('fn2')
  7. }
  8.  
  9. // 创建 index.js 文件,内容如
  10. import { fn1, fn2 } from './util2.js'
  11. fn1()
  12. fn2()

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

class

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

JS 构造函数的写法

  1. function MathHandle(x, y) {
  2. this.x = x;
  3. this.y = y;
  4. }
  5. MathHandle.prototype.add = function () {
  6. return this.x + this.y;
  7. };
  8.  
  9. var m = new MathHandle(1, 2);
  10. console.log(m.add())

用 ES6 class 的写法

  1. class MathHandle {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6.  
  7. add() {
  8. return this.x + this.y;
  9. }
  10. }
  11. const m = new MathHandle(1, 2);
  12. console.log(m.add())

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

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

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

JS 构造函数实现继承

  1. // 动物
  2. function Animal() {
  3. this.eat = function () {
  4. console.log('animal eat')
  5. }
  6. }
  7. // 狗
  8. function Dog() {
  9. this.bark = function () {
  10. console.log('dog bark')
  11. }
  12. }
  13. Dog.prototype = new Animal()
  14. // 哈士奇
  15. var hashiqi = new Dog()

ES6 class 实现继承

  1. class Animal {
  2. constructor(name) {
  3. this.name = name
  4. }
  5. eat() {
  6. console.log(`${this.name} eat`)
  7. }
  8. }
  9.  
  10. class Dog extends Animal {
  11. constructor(name) {
  12. super(name) //调用父对象的构造函数
  13. this.name = name
  14. }
  15. say() {
  16. console.log(`${this.name} say`)
  17. }
  18. }
  19. const dog = new Dog('哈士奇')
  20. dog.say()
  21. dog.eat()

注意以下两点:

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

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

Set 和 Map

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

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

Set

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

Set 实例的属性和方法有

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

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

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。不过由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys()和values()返回结果一致。
  • entries():返回键值对的遍历器。
  • forEach():使用回调函数遍历每个成员。
  1. let set = new Set(['aaa', 'bbb', 'ccc']);
  2.  
  3. for (let item of set.keys()) {
  4. console.log(item);
  5. }
  6. // aaa
  7. // bbb
  8. // ccc
  9.  
  10. for (let item of set.values()) {
  11. console.log(item);
  12. }
  13. // aaa
  14. // bbb
  15. // ccc
  16.  
  17. for (let item of set.entries()) {
  18. console.log(item);
  19. }
  20. // ["aaa", "aaa"]
  21. // ["bbb", "bbb"]
  22. // ["ccc", "ccc"]
  23.  
  24. set.forEach((value, key) => console.log(key + ' : ' + value))
  25. // aaa : aaa
  26. // bbb : bbb
  27. // ccc : ccc

Map

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

  1. const map = new Map();
  2. const obj = {p: 'Hello World'};
  3.  
  4. map.set(obj, 'OK')
  5. map.get(obj) // "OK"
  6.  
  7. map.has(obj) // true
  8. map.delete(obj) // true
  9. 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:清空所有
  1. const map = new Map();
  2. map.set('aaa', 100);
  3. map.set('bbb', 200);
  4.  
  5. map.size //
  6.  
  7. map.get('aaa') //
  8.  
  9. map.has('aaa') // true
  10.  
  11. map.delete('aaa')
  12. map.has('aaa') // false
  13.  
  14. map.clear()

Map 实例的遍历方法有:

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。
  • forEach():遍历 Map 的所有成员。
  1. const map = new Map();
  2. map.set('aaa', 100);
  3. map.set('bbb', 200);
  4.  
  5. for (let key of map.keys()) {
  6. console.log(key);
  7. }
  8. // "aaa"
  9. // "bbb"
  10.  
  11. for (let value of map.values()) {
  12. console.log(value);
  13. }
  14. //
  15. //
  16.  
  17. for (let item of map.entries()) {
  18. console.log(item[0], item[1]);
  19. }
  20. // aaa 100
  21. // bbb 200
  22.  
  23. // 或者
  24. for (let [key, value] of map.entries()) {
  25. console.log(key, value);
  26. }
  27. // aaa 100
  28. // bbb 200

Promise

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

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

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

三个状态:pendingfulfilledrejected

两个过程:

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

一个方法:then

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

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

前端综合学习笔记---异步、ES6/7、Module、Promise同步 vs 异步的更多相关文章

  1. .NET Core学习笔记(4)——谨慎混合同步和异步代码

    原则上我们应该避免编写混合同步和异步的代码,这其中最大的问题就是很容易出现死锁.让我们来看下面的例子: private void ButtonDelayBlock_Click(object sende ...

  2. spring mvc 及NUI前端框架学习笔记

    spring mvc 及NUI前端框架学习笔记 页面传值 一.同一页面 直接通过$J.getbyName("id").setValue(id); Set值即可 二.跳转页面(bus ...

  3. Webpack4 学习笔记三 ES6+语法降级为ES5

    前言 此内容是个人学习笔记,以便日后翻阅.非教程,如有错误还请指出 Webpack 将es6.es7语法降级为es5 需要通过 babel JavaScript编译器. 安装: npm i babel ...

  4. ArcGIS API for JavaScript 4.2学习笔记[7] 鹰眼(缩略图的实现及异步处理、Promise、回调函数、监听的笔记)

    文前说明:关于style就是页面的css暂时不做评论,因为官方给的例子的样式实在太简单了,照抄阅读即可. 这篇文章有着大量AJS 4.x版本添加的内容,如监听watch.Promise对象.回调函数. ...

  5. bootstrap 前端框架学习笔记

    下面是一个基于 bootstrap 前端架构的最最基本的模板: (这里添加慕课网的学习笔记.) 1.认识一下 bootstrap 带来的优雅效果: 代码: <!DOCTYPE html> ...

  6. handsontable前端excel学习笔记

    暂时没有好的中文资料,大概找了三遍随便看看,之后重点研究其github 1.Handsontable 学习笔记-Methods 2. Handsontable通用方法 3.handsontable的核 ...

  7. [深度学习] pytorch学习笔记(4)(Module类、实现Flatten类、Module类作用、数据增强)

    一.继承nn.Module类并自定义层 我们要利用pytorch提供的很多便利的方法,则需要将很多自定义操作封装成nn.Module类. 首先,简单实现一个Mylinear类: from torch ...

  8. ES6学习笔记(十五)Generator函数的异步应用

    1.传统方法 ES6 诞生以前,异步编程的方法,大概有下面四种. 回调函数 事件监听 发布/订阅 Promise 对象 Generator 函数将 JavaScript 异步编程带入了一个全新的阶段. ...

  9. 前端学习笔记之ES6快速入门

    0x1 let和const let ES6新增了let命令,用于声明变量.其用法类似var,但是声明的变量只在let命令所在的代码块内有效. { let x = 10; var y = 20; } x ...

随机推荐

  1. css 图片增加模糊效果

    img{ -webkit-filter: blur(5px); -moz-filter: blur(5px); -ms-filter: blur(5px); filter: blur(5px); }

  2. 洛谷P1854 花店橱窗布置 分析+题解代码

    洛谷P1854 花店橱窗布置 分析+题解代码 蒟蒻的第一道提高+/省选-,纪念一下. 题目描述: 某花店现有F束花,每一束花的品种都不一样,同时至少有同样数量的花瓶,被按顺序摆成一行,花瓶的位置是固定 ...

  3. CENTOS6.6上搭建单实例ORACLE12C

    本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn 摘要: 自己在centos6.6上搭建的单实例oracle12c 由 ...

  4. 5、flask之信号和mateclass元类

    本篇导航: flask实例化参数 信号 metaclass元类解析 一.flask实例化参数 instance_path和instance_relative_config是配合来用的:这两个参数是用来 ...

  5. sql server两个时间段内,求出周末的量

    公司有个表记录了出差(加班)的初始时间和截止时间,现在要计算出加班时间,之前的设计并没有考虑到这部分,因此本人通过sql重新计算周末数 表formmain starttime endtime 使用游标 ...

  6. bcache的使用

    一.前提:内核中需要配置bcache模块 1.1 检查 - 是否存在于内核中:检查/sys/fs/bcache目录是否存在,没有说明内核中没有bcache - 是否以内核模块方式存在:检查/lib/m ...

  7. Maven常用命令及在Eclipse中的应用

    1.常用命令 mvn archetype:generate--构建项目 mvn clean--项目清理 mvn test--项目单元测试的编译 mvn compile--项目源代码的编译 mvn pa ...

  8. Spring Boot 2.0(二):Spring Boot 2.0尝鲜-动态 Banner

    Spring Boot 2.0 提供了很多新特性,其中就有一个小彩蛋:动态 Banner,今天我们就先拿这个来尝尝鲜. 配置依赖 使用 Spring Boot 2.0 首先需要将项目依赖包替换为刚刚发 ...

  9. nyoj 1022 合纵连横 经典并查集

    思路:关键在于并查集的删点操作. 给每个诸侯国一个另外的编号,比如box[i]表示诸侯国i现在处于第box[i]个联盟,可以随时改变它的联盟编号,并且让box[i] = k, 实现删除操作.以前联盟中 ...

  10. Java中的自定义数组队列

    在Java中,作为所有数据结构中存储和获取速度最快的一种,数组凭借其这种简单易用的优势在各个方面都能大显神威.但是数组也有自身的局限性.数组的长度必须是固定的一旦定义之后就无法动态的更改,这就会造成这 ...