精读《你不知道的javascript》中卷

前言

《你不知道的 javascript》是一个前端学习必读的系列,让不求甚解的JavaScript开发者迎难而上,深入语言内部,弄清楚JavaScript每一个零部件的用途。本书《你不知道的javascript》中卷介绍了该系列的两个主题:“类型和语法”以及“异步与性能”。这两块也是值得我们反复去学习琢磨的两块只是内容,今天我们用思维导图的方式来精读一遍。(思维导图图片可能有点小,记得点开看,你会有所收获)

第一部分 作用域和闭包

类型

JavaScript 有 七 种 内 置 类 型: null 、 undefined 、 boolean 、 number 、 string 、 object 和 symbol ,可以使用 typeof 运算符来查看。

变量没有类型,但它们持有的值有类型。类型定义了值的行为特征。

很多开发人员将 undefined 和 undeclared 混 为 一 谈, 但 在 JavaScript 中 它 们 是 两 码 事。 undefined 是值的一种。 undeclared 则表示变量还没有被声明过。

遗憾的是, JavaScript 却将它们混为一谈, 在我们试图访问 "undeclared" 变量时这样报 错:ReferenceError: a is not defined, 并 且 typeof 对 undefined 和 undeclared 变 量 都 返 回 "undefined" 。

然而,通过 typeof 的安全防范机制(阻止报错)来检查 undeclared 变量,有时是个不错的办法。

JavaScript 中的数组是通过数字索引的一组任意类型的值。字符串和数组类似,但是它们的 行为特征不同, 在将字符作为数组来处理时需要特别小心。 JavaScript 中的数字包括“整 数”和“浮点型”。

基本类型中定义了几个特殊的值。

null 类型只有一个值 null , undefined 类型也只有一个值 undefined 。 所有变量在赋值之 前默认值都是 undefined 。 void 运算符返回 undefined 。

数 字 类 型 有 几 个 特 殊 值, 包 括 NaN ( 意 指“not a number” , 更 确 切 地 说 是“invalid number” )、 Infinity 、 -Infinity 和 -0 。

简单标量基本类型值(字符串和数字等)通过值复制来赋值 / 传递, 而复合值(对象等) 通过引用复制来赋值 / 传递。 JavaScript 中的引用和其他语言中的引用 / 指针不同,它们不 能指向别的变量 / 引用,只能指向值。

原生函数

JavaScript 为基本数据类型值提供了封装对象,称为原生函数(如 String 、 Number 、 Boolean 等)。它们为基本数据类型值提供了该子类型所特有的方法和属性(如: String#trim() 和 Array#concat(..) )。

对于简单标量基本类型值,比如 "abc" ,如果要访问它的 length 属性或 String.prototype 方法, JavaScript 引擎会自动对该值进行封装(即用相应类型的封装对象来包装它)来实现对这些属性和方法的访问。

强制类型转换

本章介绍了 JavaScript 的数据类型之间的转换,即强制类型转换:包括显式和隐式。

强制类型转换常常为人诟病, 但实际上很多时候它们是非常有用的。 作为有使命感的 JavaScript 开发人员,我们有必要深入了解强制类型转换,这样就能取其精华,去其糟粕。

显式强制类型转换明确告诉我们哪里发生了类型转换, 有助于提高代码可读性和可维 护性。

隐式强制类型转换则没有那么明显,是其他操作的副作用。感觉上好像是显式强制类型转 换的反面,实际上隐式强制类型转换也有助于提高代码的可读性。

在处理强制类型转换的时候要十分小心,尤其是隐式强制类型转换。在编码的时候,要知 其然,还要知其所以然,并努力让代码清晰易读。

语法

JavaScript 语法规则中的许多细节需要我们多花点时间和精力来了解。 从长远来看, 这有 助于更深入地掌握这门语言。

语句和表达式在英语中都能找到类比——语句就像英语中的句子,而表达式就像短语。表 达式可以是简单独立的,否则可能会产生副作用。

JavaScript 语法规则之上是语义规则(也称作上下文)。例如, { } 在不同情况下的意思不 尽相同,可以是语句块、对象常量、解构赋值(ES6)或者命名函数参数(ES6)。

JavaScript 详细定义了运算符的优先级(运算符执行的先后顺序)和关联(多个运算符的 组合方式)。只要熟练掌握了这些规则,就能对如何合理地运用它们作出自己的判断。

ASI(自动分号插入)是 JavaScript 引擎的代码解析纠错机制, 它会在需要的地方自动插 入分号来纠正解析错误。问题在于这是否意味着大多数的分号都不是必要的(可以省略), 或者由于分号缺失导致的错误是否都可以交给 JavaScript 引擎来处理。

JavaScript 中有很多错误类型, 分为两大类:早期错误(编译时错误, 无法被捕获)和运 行时错误(可以通过 try..catch 来捕获)。所有语法错误都是早期错误,程序有语法错误 则无法运行。

函数参数和命名参数之间的关系非常微妙。尤其是 arguments 数组,它的抽象泄漏给我们 挖了不少坑。因此,尽量不要使用 arguments ,如果非用不可,也切勿同时使用 arguments 和其对应的命名参数。

finally 中代码的处理顺序需要特别注意。它们有时能派上很大用场,但也容易引起困惑, 特别是在和带标签的代码块混用时。总之,使用 finally 旨在让代码更加简洁易读,切忌 弄巧成拙。

switch 相对于 if..else if.. 来说更为简洁。需要注意的一点是,如果对其理解得不够透 彻,稍不注意就很容易出错。

第二部分 this 和对象原型

异步:现在与将来

实际上, JavaScript 程序总是至少分为两个块:第一块 现在 运行;下一块 将来 运行, 以响 应某个事件。尽管程序是一块一块执行的,但是所有这些块共享对程序作用域和状态的访 问,所以对状态的修改都是在之前累积的修改之上进行的。

一旦有事件需要运行, 事件循环就会运行, 直到队列清空。 事件循环的每一轮称为一个 tick。 用户交互、IO 和定时器会向事件队列中加入事件。

任意时刻,一次只能从队列中处理一个事件。执行事件的时候,可能直接或间接地引发一 个或多个后续事件。

并发是指两个或多个事件链随时间发展交替执行,以至于从更高的层次来看,就像是同时 在运行(尽管在任意时刻只处理一个事件)。

通常需要对这些并发执行的“进程”(有别于操作系统中的进程概念)进行某种形式的交 互协调,比如需要确保执行顺序或者需要防止竞态出现。这些“进程”也可以通过把自身 分割为更小的块,以便其他“进程”插入进来。

回调

回调函数是 JavaScript 异步的基本单元。但是随着 JavaScript 越来越成熟,对于异步编程领域的发展,回调已经不够用了。

第一,大脑对于事情的计划方式是线性的、阻塞的、单线程的语义,但是回调表达异步流 程的方式是非线性的、非顺序的,这使得正确推导这样的代码难度很大。难于理解的代码 是坏代码,会导致坏 bug。

我们需要一种更同步、更顺序、更阻塞的的方式来表达异步,就像我们的大脑一样。

第二,也是更重要的一点,回调会受到控制反转的影响,因为回调暗中把控制权交给第三 方(通常是不受你控制的第三方工具!)来调用你代码中的 continuation。 这种控制转移导 致一系列麻烦的信任问题,比如回调被调用的次数是否会超出预期。

可以发明一些特定逻辑来解决这些信任问题,但是其难度高于应有的水平,可能会产生更 笨重、更难维护的代码,并且缺少足够的保护,其中的损害要直到你受到 bug 的影响才会 被发现。

我们需要一个通用的方案来解决这些信任问题。不管我们创建多少回调,这一方案都应可 以复用,且没有重复代码的开销。

我们需要比回调更好的机制。到目前为止,回调提供了很好的服务,但是未来的 JavaScript 需要更高级、功能更强大的异步模式。本书接下来的几章会深入探讨这些新型技术。

Promise

Promise 非常好,请使用。它们解决了我们因只用回调的代码而备受困扰的控制反转问题。

它们并没有摈弃回调,只是把回调的安排转交给了一个位于我们和其他工具之间的可信任 的中介机制。

Promise 链也开始提供(尽管并不完美)以顺序的方式表达异步流的一个更好的方法,这 有助于我们的大脑更好地计划和维护异步 JavaScript 代码。我们将在第 4 章看到针对这个 问题的一种更好的解决方案!

生成器

生成器是 ES6 的一个新的函数类型, 它并不像普通函数那样总是运行到结束。 取而代之 的是, 生成器可以在运行当中(完全保持其状态)暂停, 并且将来再从暂停的地方恢复 运行。

这种交替的暂停和恢复是合作性的而不是抢占式的,这意味着生成器具有独一无二的能力 来暂停自身,这是通过关键字 yield 实现的。不过,只有控制生成器的迭代器具有恢复生 成器的能力(通过 next(..) )。

yield / next(..) 这一对不只是一种控制机制,实际上也是一种双向消息传递机制。 yield .. 表 达式本质上是暂停下来等待某个值,接下来的 next(..) 调用会向被暂停的 yield 表达式传回 一个值(或者是隐式的 undefined )。

在异步控制流程方面,生成器的关键优点是:生成器内部的代码是以自然的同步 / 顺序方 式表达任务的一系列步骤。其技巧在于,我们把可能的异步隐藏在了关键字 yield 的后面, 把异步移动到控制生成器的迭代器的代码部分。

换句话说,生成器为异步代码保持了顺序、同步、阻塞的代码模式,这使得大脑可以更自 然地追踪代码,解决了基于回调的异步的两个关键缺陷之一。

程序性能

本部分的前四章都是基于这样一个前提:异步编码模式使我们能够编写更高效的代码,通 常能够带来非常大的改进。但是,异步特性只能让你走这么远,因为它本质上还是绑定在 一个单事件循环线程上。

因此,在这一章里,我们介绍了几种能够进一步提高性能的程序级别的机制。

Web Worker 让你可以在独立的线程运行一个 JavaScript 文件(即程序),使用异步事件在 线程之间传递消息。 它们非常适用于把长时间的或资源密集型的任务卸载到不同的线程 中,以提高主 UI 线程的响应性。

SIMD 打算把 CPU 级的并行数学运算映射到 JavaScript API, 以获得高性能的数据并行运算,比如在大数据集上的数字处理。

最后, asm.js 描述了 JavaScript 的一个很小的子集, 它避免了 JavaScript 难以优化的部分 (比如垃圾收集和强制类型转换),并且让 JavaScript 引擎识别并通过激进的优化运行这样 的代码。可以手工编写 asm.js, 但是会极端费力且容易出错,类似于手写汇编语言(这也 是其名字的由来)。实际上, asm.js 也是高度优化的程序语言交叉编译的一个很好的目标, 比如 Emscripten 把 C/C 转换成 JavaScript(https://github.com/kripken/emscripten/wiki) 。

JavaScript 还有一些更加激进的思路已经进入非常早期的讨论, 尽管本章并没有明确包含 这些内容,比如近似的直接多线程功能(而不是藏在数据结构 API 后面)。不管这些最终 会不会实现,还是我们将只能看到更多的并行特性偷偷加入 JavaScript, 但确实可以预见, 未来 JavaScript 在程序级别将获得更加优化的性能。

性能测试与调优

对一段代码进行有效的性能测试,特别是与同样代码的另外一个选择对比来看看哪种方案 更快,需要认真注意细节。

与其打造你自己的统计有效的性能测试逻辑,不如直接使用 Benchmark.js 库,它已经为你 实现了这些。但是,编写测试要小心,因为我们很容易就会构造一个看似有效实际却有缺 陷的测试,即使是微小的差异也可能扭曲结果,使其完全不可靠。

从尽可能多的环境中得到尽可能多的测试结果以消除硬件/ 设备的偏差, 这一点很重要。 jsPerf.com 是很好的网站,用于众包性能测试运行。

遗憾的是,很多常用的性能测试执迷于无关紧要的微观性能细节,比如 x 对比 x 。编 写好的测试意味着理解如何关注大局, 比如关键路径上的优化以及避免落入类似不同的 JavaScript 实现细节这样的陷阱中。

尾调用优化是 ES6 要求的一种优化方法。它使 JavaScript 中原本不可能的一些递归模式变 得实际。 TCO 允许一个函数在结尾处调用另外一个函数来执行,不需要任何额外资源。这意味着,对递归算法来说,引擎不再需要限制栈深度。

扩展

思维导图能比较清晰的还原整本书的知识结构体系,如果你还没用看过这本书,可以按照这个思维导图的思路快速预习一遍,提高学习效率。学习新事物总容易遗忘,我比较喜欢在看书的时候用思维导图做些记录,便于自己后期复习,如果你已经看过了这本书,也建议你收藏复习。如果你有神马建议或则想法,欢迎留言或加我微信交流:646321933,备注技术交流

精读《你不知道的javascript》上卷

精读《深入浅出Node.js》

精读《图解HTTP》

思维导图下载地址

你不知道的 javascript(中卷)PDF 下载地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367217.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

撸个微信小程序的省市区选择器

起因 微信小程序虽然已经有现成的封装好的省市区选择器给开发者使用,然鹅不幸的是,微信地址库的数据和公司用的地址库数据很难一一对上,那就只能撸起袖子自己写个组件了。 最终效果 思维导图 主要代码 组件 region-picker.js /* region-pic…

python之路_day6

本节内容&#xff1a;面向对象编程介绍为什么要用面向对象进行开发&#xff1f;面向对象的特性&#xff1a;封装、继承、多态类、方法、引子 你现在是一家游戏公司的开发人员&#xff0c;现在需要你开发一款叫做<人狗大战>的游戏&#xff0c;你就思考呀&#xff0c;人狗作…

MUI调用原生自定义方法实现计算缓存与清空缓存

由于项目需要最近在做webapp开发用的是MUI框架&#xff0c;自己本来是做原生开发的&#xff0c;在开发的时候有一个需求是实现计算缓存和清除缓存的功能&#xff0c;原生java方法实现轻轻松松&#xff0c;网上代码一大把&#xff0c;不过是webapp倒是不好搞&#xff0c;MUI自己…

Java中的多重继承与组合vs继承

有时我写了几篇有关Java 继承 &#xff0c; 接口和组成的文章。 在这篇文章中&#xff0c;我们将研究多重继承&#xff0c;然后学习组成优于继承的好处。 Java中的多重继承 多重继承是创建具有多个超类的单个类的能力。 与其他一些流行的面向对象的编程语言&#xff08;例如C …

开源|蚂蚁金服开源AntV F2:一个专注于移动,开箱即用的可视

小蚂蚁说&#xff1a;AntV 是蚂蚁金服全新一代数据可视化解决方案&#xff0c;主要子产品包括 G2、G6、F2。此前我们已经相继发布过AntV的相关开源消息与版本迭代&#xff0c;包括《蚂蚁金服开源&#xff1a;数据驱动的高交互可视化图形语法G2》&#xff0c;《开源 | 蚂蚁金服开…

Puppeteer入门初探

本文来自网易云社区作者&#xff1a;唐钊最近在看 node 爬虫相关的一些东西&#xff0c;我记得还是很久以前常用的 node 爬虫工具还是 superagengtcherrio,他们的思路是通过发起 http 请求然后截取 respone 的内容&#xff0c;但是随着前端mvvm等框架的盛行&#xff0c;现在更多…

访问量大如何增加服务器,服务器流量过大原因及解决方法

造成网站服务器流量过大的原因&#xff1a;1.网站规模较大(比如门户网站、网络商城等)&#xff0c;即网站本身访问量需求大&#xff0c;查看网站的Page View值、Hits值、日流量都很高。2.网站页面设计不合理&#xff0c;页面中包含大图片或音频、视频文件等文件&#xff0c;导致…

关于VUE项目地图开发中大量点标记绘制一些总结

问题说明 在地图开发中&#xff0c;当地图中绘制大量的标记点后&#xff0c;无论是拖动或者缩放&#xff0c;都会感觉到明显的卡顿现象。&#xff08;一般超过800个点后就比较明显了&#xff09;.在平时的工作业务中&#xff0c;由于公司的实时监控页面需要展现5000-20000车辆…

如何使用Java 5 Executor框架创建线程池

Java 5以Executor框架的形式在Java中引入了线程池&#xff0c;它允许Java程序员将任务提交与任务执行分离。 如果要使用Java进行服务器端编程&#xff0c;则线程池是维护系统可伸缩性&#xff0c;鲁棒性和稳定性的重要概念。 对于那些不熟悉Java中的线程池或这里的线程池的概念…

使用NetBeans 7.4 beta提示进行更好的基于JUnit的单元测试

在上一篇文章中 &#xff0c;我写了NetBeans 7.4 beta中提供的提示 &#xff0c;这些提示提高了开发人员避免Java异常处理带来的讨厌的运行时问题的能力。 在本文中&#xff0c;我将研究如何使用NetBeans 7.4 beta提供的另外两个提示使单元测试在执行单元测试期间更加正确和清晰…

Web 开发中 Blob 与 FileAPI 使用简述

本文节选自 Awesome CheatSheet/DOM CheatSheet&#xff0c;主要是对 DOM 操作中常见的 Blob、File API 相关概念进行简要描述。 Web 开发中 Blob 与 FileAPI 使用简述 Blob 是 JavaScript 中的对象&#xff0c;表示不可变的类文件对象&#xff0c;里面可以存储大量的二进制编…

服务器e系列和l的区别,i.e.和 e.g.的区别和使用方法

举例说明在很多文章中都有使用过&#xff0c;我想这个对大家应该并不陌生&#xff0c;但是大家知道ie和eg的区别吗&#xff0c;他们两个都是举例子的缩写词&#xff0c;但是他们之间的区别大家知道吗&#xff0c;今天我们就来介绍下这两个举例说明的缩写词到底有什么不一样。一…

通过基于JDBC的用户存储部署Identity Server

在这篇文章中&#xff0c;我将演示如何使用JDBC用户存储配置WSO2 Identity Server。 为了演示&#xff0c;我使用的是MySQL用户存储&#xff0c;但是相同的过程也适用于任何其他JDBC用户存储。 我的环境是 操作系统– Ubuntu 12.10 Java – 1.6 WSO2是4.5.0 设置MySQL数据库…

前端路由实现原理(history)

前端路由实现&#xff08;history&#xff09; 了解&#xff1a; HTML5 history新增了两个API:history.pushState和history.replaceState 两个api都接受三个参数 状态对象&#xff08;state object&#xff09;&#xff1a;一个JavaScript对象&#xff0c;与用pushState()方法…

unity 删除服务器项目,在吗?有个支持批量构建项目的好东西推荐给你

Unity Build Server是一种全新的项目构建辅助工具&#xff0c;它可以指定硬件设备&#xff0c;专门用于构建项目版本&#xff0c;帮助工作室大规模构建项目&#xff0c;提高团队生产力。很多人在选择Unity时并不会首先考虑到项目构建问题&#xff0c;而随着项目变得更大、更复杂…

使用WSO2 ESB构建制造服务总线(MSB)

在开始讨论本主题之前&#xff0c;我想介绍一些制造业中常用的术语。 术语制造执行系统&#xff08;MES&#xff09;由AMR Research于1990年提出&#xff0c;从先进的制造计算机信息系统的发展&#xff0c;MES概念已经发展了近三十年。 以下是制造执行系统协会&#xff08;MES…

mysql jion 实现原理_MySQL-join的实现原理、优化及NLJ算法

案例分析&#xff1a;selectc.*fromhotel_info_original cleft joinhotel_info_collection honc.hotel_typeh.hotel_typeandc.hotel_idh.hotel_idwhereh.hotel_idis null这个sql是用来查询出 c 表中有 h 表中无的记录&#xff0c;所以想到了用 left join 的特性(返回左边全部记…

python笔记30-docstring注释添加变量

前言 python里面添加字符串注释非常简单&#xff0c;如何将变量放入 python 的函数注释里面呢&#xff1f; docstring也就是给代码加注释的内容了&#xff0c;python可以给函数&#xff0c;类、方法&#xff0c;模块添加注释内容&#xff0c;注释标准格式一般是三个双引号&…

无线路由器在手机上如何连接服务器,192.168.10.1路由器手机怎么设置? | 192路由网...

问&#xff1a;192.168.10.1路由器手机怎么设置&#xff1f;答&#xff1a;192.168.10.1是一个C类的私有IP地址&#xff0c;目前国产的路由器中&#xff0c;睿因路由器使用192.168.10.1作为默认登录地址。鉴于此&#xff0c;下面鸿哥使用睿因路由器来进行演示介绍。温馨提示&am…

Java Mission Control 5.2终于来了! 欢迎7u40!

自从我们上次听说这个叫做任务控制的小东西已经有一段时间了。 它从JRockit一直到现在都被重命名为Java Mission Control。 这是从HotSpot和JRockit融合战略中幸存下来的部分之一。 使用今天的Java SE 7 Update 40&#xff0c;您实际上可以再次使用它。 Java Mission Control …