前言
- 本文仅为笔者的读书笔记,加入笔者自己理解的大纲提炼,若有错误恳请指出。系统学习推荐阅读原书。
- 推荐指数: 🌟🌟🌟🌟
JavaScript非常特殊,只学习一小部分的话非常简单,但是想要完整的学习会很难(就算学到够用也不容易)。当开发者感到迷惑时,他们通常会责怪语言本身,而不是怪自己对语言缺乏了解。希望你能打心眼里欣赏这门语言。
思维导图
第一部分 | 类型和语法
第一章 | 类型
对于JavaScript,类型是值的内部特征,它定义了值的行为。
1.1 内置类型
- null
- undefined
- boolean
- number
- string
- object
- symbol
除了对象都是基本类型
使用复合条件检测null值的类型
var a = null
(!a && typeof a === 'object'); // true
函数的情况比较特殊
typeof function foo(){} === 'function' // true
但是函数其实只是object的一个“子类型”,是一个“可执行对象”,内部有一个[[call]]属性,使其可以被调用
1.1 值和类型
JavaScript中的变量是没有类型的,值才有。变量可以随时持有任何类型。
语言引擎不要求变量持有与初始值相同类型的值。
在对变量执行typeof操作的时候,其实判断的是该变量持有的值的类型,而不是变量的类型,变量没有类型。
-
1.3.1 undefined和undeclared
var a; a; // undefined b; // ReferenceError: bis not defined其实b报错成undeclared更加准确
typeof对这两个的处理人更让人抓狂
var a; typeof a; // "undefined" typeof b; // "undefined"这其实是一种安全机制,在做polyfill的时候可以检查变量是否存在而不会报错
ReferenceError
当然我们希望全局命名空间不应该有变量存在,所有东西都应该被封装到模块或者私有/独立的命名空间中。
第二章 | 值
2.1 数组
在JavaScript中数组可以容纳任何类型的值。
delete 删除数组单元不会改变数组长度,单元值为undefined
类数组(DOM元素列表,arguments对象)转化方法
// 方法一
Array.prototype.slice.call(auguments);
// 方法二(ES6)
Array.from(auguments);
2.2 字符串
JavaScript中字符串不是字符组,尽管看上去很像。他们都有length, indexOf()和concant()方法。
字符串是不可变的,数组是可变的
字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。
借用数组方法处理字符串
var str = "hello";
console.log(Array.prototype.join.call(str, '-')); // h-e-l-l-o
var result = Array.prototype.map.call(str, item => {
return item.toUpperCase() + '.';
}).join("");
console.log(result); // H.E.L.L.O.
字符串也可以通过构造函数产生
var str = new String('abc');
var str1 = 'abc';
console.log(typeof str); // object
console.log(str == str1); // true
console.log(str === str1); // false
但是即使是构造函数产生的str的原始值也是不可修改的。
2.3 数字
JavaScript使用的是“双精度”格式(64位二进制)
Javascript只有一种数值类型:number,包括“整数”和带小数的十进制。
- 语法
默认情况下小数最后面的的0被省略
var a = 50.00000;
var b = 50.00;
console.log(a === b); // true
toFixed输出为字符串
var num = 100;
console.log(typeof num.toFixed(3)); // string
对于.运算符需要特别注意。数字后面的点会被解析会小数点
// 无效的语法
42.toFixed(3)
// 下面的有效
(42).toFixed(3)
42..toFixed(3)
数字字面量还可以用其他格式表示,二进制,八进制和十六进制
0xf3 // 十六进制
0o56 // 八进制
0b0101 // 二进制
- 较小的数值
所有遵循IEEE754规范都有以下问题
0.1 + 0.2 === 0.3 // false
常见的比较方法是设置一个误差范围,通常称为“机器精度”
通过ES6常量Number.EPSLION比较两个数是否相等
console.log(Math.abs(0.1+0.2-0.3) < Number.EPSILON) // true
- 整数的安全范围
能被安全呈现最大整数是2^53 - 1。ES6中被定义为Number.MAX_SAFE_INTEGER,对应的最小值为Number.MIN_SAFE_INTEGER
- 整数检测
ES6方法
console.log(Number.isInteger(42)) // true
console.log(Number.isInteger(42.000)) // true
console.log(Number.isInteger(42.3)) // false
polypill
function isInteger(num) {
return typeof num === 'number' && num % 1 === 0
}
2.4 特殊数值
-
undefined不是关键字可以被赋值(但是不要要这样做) -
void运算符返回undefined(习惯是使用
void 0) -
NaN是一个警戒值,指出错误“执行数学运算没有成功,这是失败后的返回结果”
- 与自身不相等
Number.isNaN检测
-
无穷数
console.log(1/0); // Infinity console.log(-1/0); // -Infinity
2.5 值和引用
在许多编程语言中,赋值和参数传递可以通过值复制(value-copy)或者引用复制(reference-copy )来完成。
JavaScript中简单类型总是通过值复制方式来赋值/传递;而复杂类型(对象)总是通过引用复制来赋值/传递。
第三章 | 原生函数
3.1 内部属性[[class]]
这个属性无法直接访问,一般通过Object.prototype.toString查看。
var arr = [1, 2, 3]
console.log(Object.prototype.toString.call(arr)) // [object Array]
console.log(Object.prototype.toString.call(/[0-9]/g)) // [object RegExp]
console.log(arr.toString()) // 1,2,3 这是调用的Array对象上的toString方法
但是对于null和undefined也会返回同样的情况
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
对于基本类型有所不同,它们会被包装
3.2 封装对象包装
一般不推荐直接使用封装对象
var a = new Boolean(false)
if (a) {
console.log("Oops") // Oops
}
封装的对象是真值(truthy)
3.3 拆封
想要得到基本类型值一般可以使用valueof()函数
var a = new String('abc')
var b = new Number(39)
var c = new Boolean(true)
console.log(a.valueOf()) // abc
console.log(b.valueOf()) // 39
console.log(c.valueOf()) // true
var arr = [1,2,3]
var obj = {
a: 2
}
var regexp = /[1-9]/g
console.log(obj.toString()) // [object Object]
console.log(obj.valueOf() === obj) // true
console.log(arr.toString()) // 1,2,3
console.log(arr.valueOf() === arr) // true
console.log(regexp.toString()) // /[1-9]/g
console.log(arr.valueOf() === arr) // false
复杂对象略有不同
在需要用到基本值的地方会发生隐式拆封,也就是强制类型转换
var a = new String('abc')
var b = a + 'd' // 'abcd'
3.4 原声函数作为构造函数
对于数组,对象,函数和正则表达式我们喜欢以字面量的形式来创建它们。实际上使用字面量和构造函数的效果是一样的(创建的值都是通过封装对象来包装),我们应该尽量使用字面量模式。
- Array()
不用创建和使用空单元数组。
- Object()、Funtion()、RegExp()
尽量避免使用
- Date()和Error()
这两个没有字面量模式,所以更加常用
创建日期对象必须使用new Date()。可以传入一个时间戳构造Date对象,不传就是当前时间的Date对象。不带new会得到当前时间的字符串。
创建错误对象主要是为了获得当前运行栈上下文。错误对象通常和throw一起使用。并且通常至少包含一条message属性。对于原生的错误类型,构造很少使用
- Symbol
-
定义不使用new
-
属于基本类型
-
无法访问确切的值
- 原生原型
-
包含各自类型的行为特征。
-
不全是纯对象,是子类型。
console.log(typeof Function.prototype) // function
第四章 | 强制类型转换
4.1 值的类型转换
将值从一种类型转换为另一种类型通常称为类型转换(type casting),这是显式的情况;隐式的情况称为强制类型转换(coercion)。
JavaScript中的强制类型转换总是返回标量基本类型值。
也可以这样区分:类型转换发生在静态类型语言的编译阶段,而强制类型转换发生在动态类型语言的运行时(runtime)。
JavaScript中统称为强制类型转换,又可以分为隐式强制类型转换和显式强制类型转换。
var a = 42
var b = a + '' // 隐式
var c = String(a) // 显式
4.2 抽象值操作
ES5规范定义了一些抽象操作(仅供内部使用的操作)和转换规则。
4.2.1 ToString
负责处理非字符串到字符串的强制类型转换。
基本类型值的规则:null ==> 'null',undefined ==> 'undefined',true ==> 'true'。数字遵循通用规则,极大极小数使用指数形式。
对于普通对象来说,除非自定义,否则toString()(Object.prototype.toString)返回内部[[class]]属性的值。
如果对象有自己的toString方法,字符串化的时候就会调用该方法并使用返回值。
var arr = [1, 2, 4]
console.log(arr.toString()) // 1,2,4
JSON字符串化
工具函数JSON.stringify()在将JSON对象序列化时也用到了ToString。
所有JSON-safe的值都可以使用JSON.stringify字符串化。
不安全的的JSON值:undefined、function、symbol和包含循环引用的对象。
JSON.stringify在对象中遇到undefined、function和symbol会自动忽略;数组中会返回null,保证单元位置不受影响
var obj = {
a: 123,
b: undefined,
c: function() {},
d: Symbol('test')
}
var arr = [1, undefined, function() {}, Symbol('test')]
console.log(JSON.stringify(obj)) // {"a":123}
console.log(JSON.stringify(arr)) // [1,null,null,null]
对于对象中无法被序列化的值,可以自定义toJSON方法
var a = {}
console.log('toJSON' in a); // false
a.toJSON = function() {
console.log(222)
return 'something'
}
JSON.stringify(a) // 222
console.log('toJSON' in a); // true
注意其返回的是JSON-safe的值而不是一个字符串。
JSON.stringify可以接受第二个参数replacer,指定需要被序列化的属性。
replacer可以是字符串数组。
var obj = {
a: 123,
b: 'Neil',
c: [1, 3, 4]
}
console.log(JSON.stringify(obj, ['a', 'b'])) // {"a":123,"b":"Neil"}
也可以是一个函数
var obj = {
a: 123,
b: 'Neil',
c: [1, 3, 4]
}
console.log(JSON.stringify(obj, function(key, value) {
if (key !== 'c') {
return value
}
})) // {"a":123,"b":"Neil"}
JSON.stringify不是强制转换,只是涉及ToString的强制转换。如果被序列化对象定义了toJSON()方法,会先调用转换为JSON-safe的值。
4.2.2 ToNumber
将非数字转换为数字
true ==> 1,false ==> 0, undefined ==> NaN,null ==> 0
对象会首先被转换为相应的基本类型值,如果是非数字类型再强制转换为数字。
为了将值转换为相应的基本类型,抽象操作ToPrimitive会首先检查该值有没有valueOf(),如果有且返回基本类型就使用该值进行强制转换,如果没有就使用toString()的返回值进行强制转换。如果均不返回基本类型就会产生TypeError。比如Object.create(null)创建的对象。
Number("") // 0
Number([]) // 0 数组toString返回空字符串
Number({}) // NaN 对象toString返回字符串"[object Object]"
4.2.3 ToBoolean
- 假值(falsy value)
JavaScript中的值可以分为两类:
- 可以被强制转换为false的值
- undefined
- null
- false
- +0, -0, NaN
- ""(字符串中唯一假值)
- 其他被强制转换为true的值
- 假值对象(false value)
指封装了假值的对象
var a = new String("")
var b = new Boolean(false)
var c = new Number(0)
var d = Boolean(a && b && c) // true
4.3 显示强制类型转换
我们在编码时应该尽可能地将类型转换表达清楚。类型转换越清晰,代码可读性越高,更容易理解。
4.3.1 字符串与数字之间的显示转换
基本遵循前面的规则
在JavaScript开源社区一元运算符被认为是显式强制类型转换。不过最好不要用。
~位操作符(返回二进制的补位),大致等于~42 // -(42 + 1) ==> -43。
-1是个哨位值,大部分查找遵循这一惯例(indexOf)。所以当~-1等于0(falsy value)
var str = 'hello'
console.log(str.indexOf('lo'));
if (str.indexOf('lo') > -1) {
console.log('find!');
}
if (~str.indexOf('lo')) {
console.log('find!');
}
4.3.2 显式解析数字字符串
Number与parseInt()
Number()不允许出现非数字字符,parseInt()可以。
对于浮点数可以使用parseFloat()
4.3.3 显式转换为布尔值
Boolean()
4.4 隐式强制类型转换
4.4.2 字符串与数字
常见的会使用a + ""。对于对象而言不同于String()方法的是会先调用valueOf()方法,再调用toString()
4.4.3 布尔值到数字
判断只有一个真值时
function onlyOne (a,b,c) {
var sum = 0
for(var i = 0; i < arguments.length; i++) {
if (arguments[i]) {
sum ++
}
}
return sum === 1
}
4.4.4 隐式强制转换布尔值
下面情况会发生
- if()
- for()
- while()和do{}while()
- ?:三元表达式
- 逻辑运算符
4.4.5 || 和 &&
与其他语言有差异,叫“逻辑运算符”不太准确,“选择器运算符”更合适。它们返回的不是布尔值。
它们的返回值时两个操作数中的一个(有且仅有一个)。
console.log(43||32) // 43
console.log(null||32) // 32
console.log(null && 32); // null
console.log(42 && 32); // 32
常见用法:设置参数默认值
function foo (a, b) {
a = a || 'abc'
b = b || 'dce'
}
4.4.6 symbol的强制转换
ES6允许对symbol的显式强制类型转换,不允许隐式。
var sym = Symbol('cool');
console.log(String(sym)); // Symbol(cool)
console.log(sym + ""); // TypeError
4.5 宽松相等和严格相等
4.5.1 性能
===有更好的性能(不用隐式转换)
4.5.2 抽象相等 ==
-
比较对象的时候
==和===一样 -
字符串和数字比较字符串发生ToNumber操作
-
不要和布尔类型做比较
-
null和undefined在==中相等,除此之外不和其他值相等
-
对象和非对象比较
-
对象会先发生ToPrimitive操作
-
非对象中布尔值会转化为数字类型
console.log({} == false); // false console.log([] == false); // true console.log([0] == false); // true
-
4.5.3 少见的情况
[] == ![]
var i = 2
Number.prototype.valueOf = function() {
return i++
}
var a = new Number(2)
if (a == 2 && a == 3) {
console.log('crazy') // crazy
}
第五章 | 语法
本章节主要讨论语法(grammer),区别于词法(syntax)
5.1 语句和表达
语句(statement)和表达式(expression)是有区别的。语句相当于句子,表达式相当于短语。
var a = 3 * 6;
var b = a;
b;
其中var a = 3 * 6;和var b = a;称为声明语句(declaration statement)。
a = 3 *6称为赋值表达式
5.1.1 语句的结果值
语句都有一个结果值(statement completion value, undefined也算)
在控制台中分别执行以下语句可以获得返回值
a = 18; // 18
var a = 19; // undefined
var关键字声明变量返回值会被屏蔽掉,所以返回undefined
代码块也会返回值
var b;
if (true) {
b = 4 + 38 //
}
理论上返回42,但是语法不允许将语句结果值赋值给另一个变量(可以用eval实现)
5.1.2 表达式的副作用
大部分表达式没有副作用
常见的副作用表达式有函数调用
function foo () {
a = a + 1;
}
var a = 42;
foo(); // 结果值undefined 副作用:a的值被改变
其他
var a = 42;
var b = a++; // a的值被改变(b等于42)
括号也无法改变
var a = 42;
var b = (a++); // b还是等于42,逗号语句可以实现目标 b = (a++, a)
delete语句也有返回值
var obj = {
a: 1
};
delete obj.a; // true
链式赋值
var a, b, c;
a = b = c = 42;
5.1.3 上下文规则
大括号
-
对象字面量
-
代码块
[] + {} // [Object object] {} + [] // 0第一行的{}会被强制转换为[Object object],第二行的{}是个空代码块(不执行任何操作)+ []被强制转化
-
对象解构
-
if else
事实上JavaScript没有else if语句,只是else包含单条语句时省略了{}。
5.2 运算符优先级
&& 高于 || 高于三元表达式? :
console.log(true || false && false); // true
5.3 暂时性死区
-
let const 赋值
-
函数默认参数
5.4 try..finally
finally 中 return会覆盖try中return
第二部分 | 异步与性能
第一章 | 异步:现在与将来
概述了异步的概念以及异步中的各种问题。
- JavaScript执行基于调用栈:现在调用和将来调用。
- 事件循环模式。
- 异步函数的并行与并发的传统处理方式。
第二章 | 回调
JavaScript中的回调的缺陷:
- 逻辑上难以理解
- 回调嵌套
- 信任问题
- 多次调用
- 过早或者过晚调用
第三章 | Promise
3.1 基础概念
Promise是一种范式,希望获得反转控制的能力。
Promise一旦决议则不可更改。
类似于事件监听,思考下面伪代码
foo(x){
// do a long time job
}
foo(42);
on(foo 'completion'){
// next step
}
on(foo 'error') {
// catch error
}
很好的实现了控制反转和关注点分离
3.2 识别Promise对象
鸭子类型(duck typing)
3.3 Promise信任问题
-
解决了调用过早:因为即使是立即决议,也无法被同步观察到,.then()方法是异步调用
-
解决了调用过晚:
var p = Promise.resolve() p.then(function() { p.then(function() { console.log('C'); }) console.log('A'); }) p.then(function(){ console.log('B'); }) // A B C -
回调未调用
function timeoutPromise(delay) { return new Promise(function(resolve, reject){ setTimeout(function(){ reject('timeout'); }, delay) }) } Promise.race([ foo(), timeoutPromise(3000) ]).then( function(){ // foo completed }, function(err) { // err or timeout } ); -
不会出现多次调用
-
参数传递
Promise.resolve()只接受一个参数,其他会被忽略- 传入一个非Promise,非thenable的立即值会得到一个用这个值充填的Promise。下面两种行为一致
var p1 = new Promise(function(resolve, reject) { resolve(42); }) var p2 = Promise.resolve(42);- 传入一个Promsie会返回同一个Promise
var p1 = Promise.resolve(42); var p2 = Promise.resolve(p1); console.log(p1 === p2); // true- 传入一个thenable得到的也是一个真正的Promise
-
错误捕获
-
决议过程中的错误会被拒绝函数捕获,
.then()回调本身返回一个Promise,其中的错误会被拒绝处理函数捕获。
3.4 链式流
基于Promise特性
.then()返回一个Promise
链式流中的错误捕获
对于设置了拒绝处理函数的流会继续执行
Promise.resolve(42)
.then(()=>{
console.log(111); // 111
})
.then(() => {
throw new Error('Oops')
})
.then(() => {
console.log(222); // 不会执行
},err => {
console.log(err); // Error: Oops
})
.then(() => {
console.log(333); // 333
})
对于未捕获的错误会有一个默认的拒绝处理函数顶替上来,抛出错误终中断程序
function (err) {
throw err
}
3.5 错误处理
回调中的error-first风格
function foo(cb) {
setTimeout(function() {
try {
var x = baz.bar()
cb(null, x)
} catch (err) {
cb(err)
}
} ,100)
}
foo( function(err, value) {
if (err) {
console.log(err);
}
else {
console.log(value);
}
})
通常情况下开发者在链式流末尾使用catch捕获错误,但是catch本身返回一个Promise,它的错误(虽然几率很小)就会被忽略掉。
3.6 Promise模式
3.6.1 Promise.all([...])
- 实现了门(gate)机制。
- 任何一个返回拒绝Promise.all()就会拒绝,抛弃其他所有promis返回结果
- 返回指定顺序(与完成顺序无关)数组。
3.6.2 Promise.race([...])
- 门闩模式,Promise中称为竞态。
- 超时竞赛
Promise.race([
foo(),
timeoutPromise(3000)
]).then(
function() {
// foo 按时完成
},
function(err) {
// foo被拒绝或者超时
}
)
- finally收尾函数
let isLoading = true
Promise.resolve(42)
.then(res => {
// do something
})
.catch(err => {
// deal err
})
.finally(() => {
isLoading = false
})
finally也会返回一个promise对象,存在未处理拒绝问题。需要构造一个辅助工具查看决议。
3.6.3 并发迭代
var p1 = Promise.resolve(21)
var p2 = Promise.resolve(42)
var p3 = Promise.reject('good')
console.log('map' in Promise);
Promise.map = function (vals, func) {
return Promise.all(
vals.map(val => {
return new Promise(resolve => {
func(val, resolve)
})
})
)
}
Promise.map([p1, p2, p3], function (val, done) {
Promise.resolve(val)
.then(
res => {
return done(res * 2)
},
done
)
}).then(res => {
console.log(res); // [42 84 'good']
})
3.8 Promise的局限性
- 顺序错误处理,即Promise中的错误很容易被忽略。
- 无法取消
第四章 | 生成器
- Promise解决了控制反转的问题
- generator将解决异步不符合大脑逻辑问题
4.1 打破完整运行
ES6中指示暂停点的语法yield,礼貌的表达了一种合作式的控制放弃。
var x = 1;
function* foo() {
x++
console.log(x);
yield;
console.log(x);
}
function bar () {
x++
}
var iter = foo()
iter.next() // 2
bar()
iter.next() // 3
foo()运算并没有执行生成器,只是构造了一个迭代器
4.1.1 输入与输出
-
调用
.next()会返回一个对象{value: value, done: Boolean} -
yield是双向通信。暂停并返回一个值,重新启动时接受
.next(args)的参数
4.1.2 多个迭代器
每次构建一个迭代器,实际上就隐式构建了生成器的一个实例,通过这个迭代器来控制的是这个生成器实例。
表达式中暂停后对前面已读取值的变量重新复制不会改变读取结果
var a = 1
function *foo() {
var b = a + (yield);
console.log(b)
}
var ite = foo()
ite.next()
a = 12
ite.next(1) // 2
4.2 生成器产生值
假如要产生一系列值,每个值都与前面一个有特定的关系。
闭包实现
var gimme = (function () {
var val;
return function () {
if (val === undefined) {
val = 1
} else {
val = val * 3 + 6
}
debugger
return val
}
})()
console.log(gimme()); // 1
console.log(gimme()); // 9
console.log(gimme()); // 33
构造迭代器实现
var gimme = (function () {
var val;
return {
[Symbol.iterator]: function () {return this},
next: function() {
if (val === undefined) {
val = 1
} else {
val = val *3 + 6
}
return { value: val, done: false}
}
}
})()
console.log(gimme.next().value)
console.log(gimme.next().value)
console.log(gimme.next().value)
生产器迭代器
function *gimme() {
var val;
while(true) {
if (val === undefined) {
val = 1
}
else {
val = val * 3 + 6
}
yield val
}
}
var ite = gimme()
console.log(ite.next())
console.log(ite.next())
console.log(ite.next())
JavaScript中while(true)是糟糕的做法,yield让它变得可用。这里我们不再需要闭包保存变量。也不需要构造迭代器。代码更加清晰。
停止生成器
生成器的“异常结束”通常由break、return、或者未捕获异常引起。
如果生成器内有try...finally,它将总是运行(扫尾函数)。
4.3 异步迭代生成器
function request() {
setTimeout(() => {
ite.next(123)
}, 500)
}
function *main() {
try {
var text = yield request();
console.log(text);
}
catch (err) {
console.log(err);
}
}
var ite = main()
ite.next() // 123
同步错误处理
try {
ite.throw(err)
} catch (err) {
console.log(err)
}
4.4 生成器 + Promsie
构成了async/await语法
4.4.1 run()函数
自动运行传入的生成器,具体代码见书。
4.4.2 并发
function *foo() {
var p1 = request(url1)
var p2 = request(url2)
var r1 = yield p1
var r2 = yield p2
var r3 = yield request(url3 + r1 + r2)
console.log(r3)
}
实际上是实现了Promise.all([p1, p2])
很多时候抽象并不总是好事,很多时候会增加复杂度换取简洁性。
4.5 生成器委托
从一个生成器内部调用另一个生成器(也可以是迭代器),委托/转移控制权。
等于隐式的从调用者内部调用了run(generator)
4.5.1 委托的作用
主要目的是是代码组织,以达到与普通函数调用的对称。
4.5.2 消息委托
可以通过生成器迭代器实现双向通信。
4.5.3 异步委托
function *foo() {
var r2 = yield request("http://some.url.2");
var r3 = yield request("http://some.url.3/?v=" + r2);
return r3
}
function *bar() {
var r1 = yield request("http://some.url.1");
var r3 = yield *foo();
}
4.5.4 递归委托
function *foo(val) {
if(val > 1) {
val = yield *foo(val -1)
}
return val
}
function *bar () {
var a = yield *foo(3)
return a
}
var ite = bar()
console.log(ite.next()) // 1
4.6 生成器并发
runAll()
4.7 形实转换程序(thunk)
举例说明
function foo(x, y) {
return x + y
}
function fooThunk() {
return foo(3, 4)
}
// 将来调用
console.log(fooThunk()) // 7
与Promise构造对比(具体参见书)
4.8 手工变化(具体参见书)
第五章 | 程序性能
5.1 Web Worker
多线程架构的问题
- 副线程会不会影响主线程
- 能够访问共享的作用域和资源
- 如何通信
首先Worker的能力是宿主环境提供的,可以开启多个JavaScript引擎实例各自运行在自己的线程上。
Worker和主程序之间不会共享任何作用域或资源。
以下是如何通信
var wx = new Worker("http://some.url.1/mycoolworker.js")
// 侦听时间
w1.addEventlistener('message', function(evt) {
// evt.data
})
// 发送消息
w1.postMessage('something cool to say')
5.1.1 Worker环境
这是一个完全独立的线程。除了主程序的作用域无法共享,其他web提供的API基本都可以调用。
worker内部加载其他JavaScript文件是同步的,会阻塞该线程。
应用:
- 处理密集型数学计算
- 大数据集排序
- 数据处理(压缩、音频分析、图像处理
- 高流量网络通信
5.1.2 数据传递
- 结构化克隆算法
- Transferable对象
5.1.3 共享Worker
对于SPA应用不同组件间共享同一个Worker
var w1 = new ShareWork([path])
第六章 性能测试与调优
主要简单介绍了针对性能检测的几个库。
ES6建议尾部调用优化对支持TCO引擎友好。