前言

毕业到入职腾讯已经差不多一年的时光了,接触了很多项目,也积累了很多实践经验,在处理问题的方式方法上有很大的提升。随着时间的增加,愈加发现基础知识的重要性,很多开发过程中遇到的问题都是由最基础的知识点遗忘造成,基础不牢,地动山摇。所以,就再次回归基础知识,重新学习JavaScript相关内容,加深对JavaScript语言本质的理解。日知其所亡,身为有追求的程序员,理应不断学习,不断拓展自己的知识边界。本系列文章是在此阶段产生的积累,以记录下以往没有关注的核心知识点,供后续查阅之用。

2017

04/10

事实上,程序中现在运行的部分和将来运行的部分之间的关系就是异步编程的核心。 到底什么时候控制台 I/O 会延迟,甚至是否能够被观察到,这都是游移不定的。如果在调 试的过程中遇到对象在 console.log(..) 语句之后被修改,可你却看到了意料之外的结果, 要意识到这可能是这种 I/O 的异步化造成的。如果遇到这种少见的情况,最好的选择是在 JavaScript 调试器中使用断点, 而不要依赖控制台输出。次优的方案是把对象序列化到一个字符串中,以强制执行一次“快照”,比如通过 JSON.stringify(..)。

04/11

多线程编程是非常复杂的。因为如果不通过特殊的步骤来防止这种中断和交错运行的话,可能会得到出乎意料的、不确定的行为,通常这很让人头疼。
JavaScript 从不跨线程共享数据。
var res = []; // response(..)从Ajax调用中取得结果数组
function response(data) {
// 一次处理1000个
var chunk = data.splice(0, 1000);
// 添加到已有的res组
res = res.concat(
// 创建一个新的数组把chunk中所有值加倍
chunk.map(function(val) {
return val * 2;
}));
// 还有剩下的需要处理吗?
if (data.length > 0) {
// 异步调度下一次批处理
setTimeout(function() {
response(data);
}, 0);
}
}
// ajax(..)是某个库中提供的某个Ajax函数
ajax("http://some.url.1", response);
ajax("http://some.url.2", response);
我们把数据集合放在最多包含 1000 条项目的块中。这样,我们就确保了“进程”运行时 间会很短,即使这意味着需要更多的后续“进程”,因为事件循环队列的交替运行会提高 站点 /App 的响应(性能)。

05/10

让我们来简单总结一下使链式流程控制可行的 Promise 固有特性。
  • 调用 Promise 的 then(..) 会自动创建一个新的 Promise 从调用返回。
  • 在完成或拒绝处理函数内部,如果返回一个值或抛出一个异常,新返回的(可链接的) Promise 就相应地决议。
  • 如果完成或拒绝处理函数返回一个 Promise,它将会被展开,这样一来,不管它的决议 值是什么,都会成为当前 then(..) 返回的链接 Promise 的决议值。

05/11

Promise 局限性 :
顺序错误处理 : Promise 的设计局限性(具体来说,就 是它们链接的方式)造成了一个让人很容易中招的陷阱,即 Promise 链中的错误很容易被 无意中默默忽略掉。 关于 Promise 错误,还有其他需要考虑的地方。由于一个 Promise 链仅仅是连接到一起的成员 Promise,没有把整个链标识为一个个体的实体,这意味着没有外部方法可以用于观 察可能发生的错误。 遗憾的是,很多时候并没有为 Promise 链序列的中间步骤保留的引用。因此,没有这样的引用,你就无法关联错误处理函数来可靠地检查错误。
单一值 :根据定义,Promise 只能有一个完成值或一个拒绝理由。在简单的例子中,这不是什么问题,但是在更复杂的场景中,你可能就会发现这是一种局限了。 一般的建议是构造一个值封装(比如一个对象或数组)来保持这样的多个信息。这个解决 方案可以起作用,但要在 Promise 链中的每一步都进行封装和解封,就十分丑陋和笨重了。
  • 分裂值
  • 展开 / 传递参数
单决议:Promise 最本质的一个特征是:Promise 只能被决议一次(完成或拒绝)。在许多异步情况中,你只会获取一个值一次,所以这可以工作良好。 但是,还有很多异步的情况适合另一种模式——一种类似于事件和 / 或数据流的模式。
惯性:Promise 提供了一种不同的范式,因此,编码方式的改变程度从某处的个别差异到某种情 况下的截然不同都有可能。你需要刻意的改变,因为 Promise 不会从目前的编码方式中自 然而然地衍生出来。
// polyfill安全的guard检查
if (!Promise.wrap) {
Promise.wrap = function(fn) {
return function() {
var args = [].slice.call(arguments);
return new Promise(function(resolve, reject) {
fn.apply(null, args.concat(function(err, v) {
if (err) {
reject(err);
} else {
resolve(v);
}
}));
});
};
};
}
无法取消的 Promise:一旦创建了一个 Promise 并为其注册了完成和 / 或拒绝处理函数,如果出现某种情况使得 这个任务悬而未决的话,你也没有办法从外部停止它的进程。
Promise 性能:更多的工作,更多的保护。这些意味着 Promise 与不可信任的裸回调相比会更慢一些。这是显而易见的,也很容易理解。

05/12

Promise 非常好,请使用。它们解决了我们因只用回调的代码而备受困扰的控制反转问题。 它们并没有摈弃回调,只是把回调的安排转交给了一个位于我们和其他工具之间的可信任的中介机制。 Promise 链也开始提供(尽管并不完美)以顺序的方式表达异步流的一个更好的方法,这有助于我们的大脑更好地计划和维护异步 JavaScript 代码。

05/14

并没有向第一个 next() 调用发送值,这是有意为之。只有暂停的 yield 才能接受这样一个通过 next(..) 传递的值,而在生成器的起始处我们调用 第一个 next() 时,还没有暂停的 yield 来接受这样一个值。规范和所有兼容浏览器都会默默丢弃传递给第一个 next() 的任何东西。传值过去仍然不 是一个好思路,因为你创建了沉默的无效代码,这会让人迷惑。因此,启动 生成器时一定要用不带参数的 next()。
如果你的生成器中没有 return 的话——在生成器中和在普通函数中一样,return 当然不是必需的——总有一个假定的 / 隐式的 return;(也就是 return undefined;)。

05/15

如果你只是想要迭代一个对象的所有属性的话(不需要保证特定的顺序),可以通过 Object.keys(..) 返回一个 array,类似于 for (var k of Object. keys(obj)) { .. 这样使用。这样在一个对象的键值上使用 for..of 循环与 for..in 循环类似,除了 Object.keys(..) 并不包含来自于 [[Prototype]] 链上的属性,而 for..in 则包含。
通常在实际的 JavaScript 程序中使用 while..true 循环是非常糟糕的主意,至 少如果其中没有 break 或 return 的话是这样,因为它有可能会同步地无限循 环,并阻塞和锁住浏览器 UI。但是,如果在生成器中有 yield 的话,使用这 样的循环就完全没有问题。因为生成器会在每次迭代中暂停,通过 yield 返 回到主程序或事件循环队列中。简单地说就是:“生成器把 while..true 带回 了 JavaScript 编程的世界!”

05/16

生成器 yield 暂停的特性意味着我们不仅能够从异步函数调用得到看似同步的返回值,还 可以同步捕获来自这些异步函数调用的错误!
ES6 中最完美的世界就是生成器(看似同步的异步代码)和 Promise(可信任可组合)的结合。
获得 Promise 和生成器最大效用的最自然的方法就 是 yield 出来一个 Promise,然后通过这个 Promise 来控制生成器的迭代器。

05/17

生成器是 ES6 的一个新的函数类型,它并不像普通函数那样总是运行到结束。取而代之的是,生成器可以在运行当中(完全保持其状态)暂停,并且将来再从暂停的地方恢复运行。 这种交替的暂停和恢复是合作性的而不是抢占式的,这意味着生成器具有独一无二的能力来暂停自身,这是通过关键字 yield 实现的。不过,只有控制生成器的迭代器具有恢复生 成器的能力(通过 next(..))。
yield/next(..) 这一对不只是一种控制机制,实际上也是一种双向消息传递机制。yield .. 表达式本质上是暂停下来等待某个值,接下来的 next(..) 调用会向被暂停的 yield 表达式传回 一个值(或者是隐式的 undefined)。 在异步控制流程方面,生成器的关键优点是:生成器内部的代码是以自然的同步 / 顺序方式表达任务的一系列步骤。其技巧在于,我们把可能的异步隐藏在了关键字 yield 的后面, 把异步移动到控制生成器的迭代器的代码部分。 换句话说,生成器为异步代码保持了顺序、同步、阻塞的代码模式,这使得大脑可以更自 然地追踪代码,解决了基于回调的异步的两个关键缺陷之一。  

05/18

在 Worker 内部是无法访问主程序的任何资源的。这意味着你不能访问它的任何全局变量, 也不能访问页面的 DOM 或者其他资源。记住,这是一个完全独立的线程。  
Web Worker 通常应用于哪些方面呢?
  • 处理密集型数学计算
  • 大数据集排序
  • 数据处理(压缩、音频分析、图像处理等)
  • 高流量网络通信
Transferable 对象(http:// updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast)。这时发生的是对象所 有权的转移,数据本身并没有移动。一旦你把对象传递到一个 Worker 中,在原来的位置 上,它就变为空的或者是不可访问的,这样就消除了多线程编程作用域共享带来的混乱。 当然,所有权传递是可以双向进行的。
异步编码模式使我们能够编写更高效的代码,通 常能够带来非常大的改进。但是,异步特性只能让你走这么远,因为它本质上还是绑定在 一个单事件循环线程上。
SIMD 打算把 CPU 级的并行数学运算映射到 JavaScript API,以获得高性能的数据并行运 算,比如在大数据集上的数字处理。

05/19

Benchmark.js :任何有意义且可靠的性能测试都应该基于统计学上合理的实践。
对于微小运算的绝大多数测试结果,比如 ++x 对比 x++ 的迷思,像出于性能考虑应该用 X 代替 Y 这样的结论都是不成立的。  这可以归结为一点,测试不真实的代码只能得出不真实的结论。如果有实际可能的话,你 应该测试实际的而非无关紧要的代码,测试条件与你期望的真实情况越接近越好。只有这 样得出的结果才有可能接近事实。 像 ++x 对比 x++ 这样的微观性能测试结果为虚假的可能性相当高,可能我们最好就假定它们是假的。
jsPerf.com (http://jsperf.com):它使用 Benchmark.js 库来运行统计上精确可靠的测试,并把测试结果放在一个公开 可得的 URL 上,你可以把这个 URL 转发给别人。  
在考虑对代码进行性能测试时,你应该习惯的第一件事情就是你所写的代码并不总是引擎真正运行的代码。不要试图和 JavaScript 引擎比谁聪明。对性能优化来说,你很可能会输。“没有比临时 hack 更持久的了”。很有可能你现在编写的用来绕过一些性能 bug 的代码可能 比浏览器的性能问题本身存在得更长久。我们应该关注优化的大局,而不是担心这些微观性能的细微差别。
高德纳——计算访谈 6(1974 年 12 月) :程序员们浪费了大量的时间用于思考,或担心他们程序中非关键部分的速度,这 些针对效率的努力在调试和维护方面带来了强烈的负面效果。我们应该在,比如 说 97% 的时间里,忘掉小处的效率:过早优化是万恶之源。但我们不应该错过 关键的 3% 中的机会。

05/21

尾调用优化 :ES6 包含了一个性能领域的特殊要求-尾调用优化(Tail Call Optimization,TCO)。  
简单地说,尾调用就是一个出现在另一个函数“结尾”处的函数调用。这个调用结束后就 没有其余事情要做了(除了可能要返回结果值)。调用一个新的函数需要额外的一块预留内存来管理调用栈,称为栈帧。然而,如果支持 TCO 的引擎能够意识到一个函数调用位于尾部,这意味着外部函数基本上已经完成了,那么在调用 函数时,它就不需要创建一个新的栈帧,而是可以重用已有的栈帧。这样不仅速度更快,也更节省内存。
递归是 JavaScript 中一个纷繁复杂的主题。因为如果没有 TCO 的话,引擎需要实现一 个随意(还彼此不同!)的限制来界定递归栈的深度,达到了就得停止,以防止内存耗 尽。有了 TCO,尾调用的递归函数本质上就可以任意运行,因为再也不需要使用额外的内存! ES6 之所以要求引擎实现 TCO 而不是将其留给引擎自由决定,一个原因是缺乏 TCO 会导 致一些 JavaScript 算法因为害怕调用栈限制而降低了通过递归实现的概率。

温故而知新--JavaScript书摘(三)的更多相关文章

  1. 温故而知新--JavaScript书摘(一)

    前言: 毕业到入职腾讯已经差不多一年的时光了,接触了很多项目,也积累了很多实践经验,在处理问题的方式方法上有很大的提升.随着时间的增加,愈加发现基础知识的重要性,很多开发过程中遇到的问题都是由最基础的 ...

  2. 温故而知新--JavaScript书摘(二)

    前言 毕业到入职腾讯已经差不多一年的时光了,接触了很多项目,也积累了很多实践经验,在处理问题的方式方法上有很大的提升.随着时间的增加,愈加发现基础知识的重要性,很多开发过程中遇到的问题都是由最基础的知 ...

  3. 学习javascript数据结构(三)——集合

    前言 总括: 本文讲解了数据结构中的[集合]概念,并使用javascript实现了集合. 原文博客地址:学习javascript数据结构(三)--集合 知乎专栏&&简书专题:前端进击者 ...

  4. JavaScript的三种工业化调试方法

    JavaScript的三种工业化玩法 软件工程中任何的语言如果想要写出健壮的代码都需要锋利的工具,当然JavaScript也不例外,很多朋友刚入门的时候往往因为工具选的不对而事半功倍,JavaScri ...

  5. 初探JavaScript(三)——JS带我"碰壁"带我飞

    已经写了两篇关于小白的JavaScript之行,不可否认,每一种语言都有其精华与糟粕之处,来不及细细体味其精华奥妙,也没法对其评头论足,只能先了解,后深入.到目前为止已经看完<JavaScrip ...

  6. 前端笔记知识点整合之JavaScript(三)关于条件判断语句、循环语句那点事

      一.条件分支语句 条件分支语句,也叫作条件判断语句,就是根据某种条件执行某些语句,不执行某些语句. JS中有三种语法是可以表示条件分支的 1.1 if……else…… 条件分支的主力语法,这个主力 ...

  7. Emscripten教程之连接C++和JavaScript(三)

    本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏.也可以去作者的博客阅读文章.欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522. E ...

  8. JavaScript中有三个可以对字符串编码的函数,分别是: escape(),encodeURI(),encodeURIComponent()

    JavaScript中有三个可以对字符串编码的函数,分别是: escape,encodeURI,encodeURIComponent,相应3个解码函数:unescape,decodeURI,decod ...

  9. 【转】SVG与HTML、JavaScript的三种调用方式

    原文:https://www.cnblogs.com/guohu/p/5085045.html SVG与HTML.JavaScript的三种调用方式 一.在HTMl中访问SVG的DOM 1 2 3 4 ...

随机推荐

  1. C# Winform 按回车键查找下一个可设置焦点的组件

    private void frmLogin_KeyPress(object sender, KeyPressEventArgs e) { //按回车键查找下一个可设置焦点的组件. if (e.KeyC ...

  2. Exception in thread "main" java.util.InputMismatchException

    今天写代码来了一个异常 /** * 需求分析:根据输入的天数是否是周六或是周日, * 并且天气的温度大于28摄氏度,则外出游泳,否则钓鱼 * @author chenyanlong * 日期:2017 ...

  3. Java_myBatis_XML代理_延迟加载

    使用mybatis的延迟加载,需要两个步骤: 1.在全局配置文件中添加一下语句(lazyLoadingEnabled默认为false,aggressiveLazyLoading默认为true) < ...

  4. Python基础【day02】:元组和购物车练习的知识点

    一.元组 元组其实跟列表差不多,也是存一组数,只不是它一旦创建,便不能再修改,所以又叫只读列表 用途:一般情况下用于自己写的程序能存下数据,但是又希望这些数据不会被改变,比如:数据库连接信息等 1.元 ...

  5. java日期相关

    JAVA中获得一个月最大天数的方法 参考博客:http://www.cnblogs.com/relucent/p/4566582.html Calendar 类是一个抽象类,为日历字段之间的转换提供了 ...

  6. NET 4 中 内存映射文件

    原文链接 : http://blogs.msdn.com/salvapatuel/archive/2009/06/08/working-with-memory-mapped-files-in-net- ...

  7. Git与GitHub学习笔记(五)一次提交失败的记录

    代码已经跟踪了,添加注释说明,但是总是添加不了 error: pathspec 'live-page'' did not match any file(s) known to git. 重复了好多遍, ...

  8. HTTP协议(下午茶)

    http://www.kancloud.cn/kancloud/tealeaf-http/43840   下午茶

  9. fatal error C1083: 无法打开包括文件: “SDKDDKVer.h”: No such file or directory(转)

    fatal error C1083: 无法打开包括文件: “SDKDDKVer.h”: No such file or directory 解决办法:(Vs2013中) 项目--右键--属性--配置属 ...

  10. Nginx 日志处理

    . nginx日志统计独立ip的个数: awk '{print $1}' /access.log | sort | uniq | wc -l . 查询访问最多的前10个ip awk . 查看某段时间的 ...