你不晓得的javascript
第二局部
一、回调
1.回调格调的缺点
利用回调函数存在的缺点:
1.大脑对于事件的打算时线性的、阻塞的、单线程大的语义。而回调表白异步流程的形式是非线性的、非程序的,这使得正确推导这样的代码难度较大,就不必书写代码时可能造成的回调天堂了。对于难了解的代码时坏代码,会导致坏bug
2.最重要的一点,回调函数会受到管制反转的影响,因为回调函数暗中把管制势力交给了第三方(通常是不受你管制的第三方工具)来调用你代码的continuation。这种管制转移导致了一系列麻烦的信赖问题,比方回调被调用的次数是否超出预期,调用的机会是否合乎预期,及第三方吞掉可能呈现的异样或者谬误。
2.罕用的回调格调
拆散回调:
function success(data){ alert(data);}function failure(err){ alert(err);}ajax('http://some.url.com/',success,failure);
谬误优先error-first(node格调):
ajax('http://some.url.com/',function(error,data){ if(err) { console.log(err); } else { console.log(result); }});
二、Promise
1.具备then办法的鸭子类型
辨认Promise(或者行为相似于Promise的货色)就被认定为某种称为thenable的货色,将其定义为任何具备then(..)办法的函数或者对象和函数。咱们认为,任何这样的值就是Promise统一的thenable。
依据一个值的状态(具备那些属性)对这个值得类型做出一些假设,这种类型检测(type check)个别用术语鸭子类型(duck typing)来示意——"如果它看起来项鸭子,叫起来像鸭子,那么它就是一只鸭子",于是,对thenalbe值的鸭子类型检测就大抵相似于:
let p = function () { };p.then = function (cb) { cb();}function duck_typing_thenable(thenable) { return !!thenable && ( typeof thenable === "object" || typeof thenable === "function" )&& typeof thenable.then === "function";}console.log(duck_typing_thenable(p));
应用Promise.resolve将thenalbe值规范化成一个Promise值:
var p = { then: function (cb, errcb) { cb(42); errcb('evil laugh'); }}p.then(function (data) { console.log(data);}, function (err) { // 如果时正真的promise,那么不会运行到这里 console.log('thenalbe:' + err); });Promise.resolve(p).then( function (data) { console.log(data); }, function (err) { // 这里不会运行 console.log('promise:' + err); })
2.Promise.race()
可用于解决一各可能永远不会决定的Promise。应用一中称为竞态的高级形象机制:
const timeoutPromise = function (delay) { return new Promise((resolve, reject) => { setTimeout(() => { reject('Promise Timeout'); }, delay); })}const p = new Promise((resolve, reject) => { setTimeout(() => { resolve('hello'); }, 2000);});const pt = Promise.race([p, timeoutPromise(3000)]);pt.then(console.log)
3.Promise默认的处理函数
当你调用的promise的then(..),只传入一个实现处理函数,一个默认的回绝处理函数就会顶替上来:
let p = new Promise(function (resolve, reject) { foo.baz(); reject('rejected data');})p.then(function fulfilled() { // 这里不会执行}, // function rejected(error) { // throw error; // })
如你所见,默认回绝处理函数只是把谬误从新抛出,这使得谬误客户以持续沿着Promise链流传上来。
当你没有给then(..)传递一个适当无效的函数作为解决函数参数,韩式会有作为代替的一个默认处理函数:
let p = new Promise(function (resolve, reject) { resolve('resolve data');})p.then( // function (data) { // return data; // }, null, function rejected() { // 永远不会执行到这里 })
4.Promise构造函数
应用Promise构造函数创立一个promise对象时,必须提供一个回调函数。这个回调是同步的或立刻调用的。这个函数接管到两个函数类型的参数,用以反对promise的决定,通常咱们发这两个函数称为resolve(..)和reject(..):
let p = new Promise(function(resolve,reject){ // resolve(..) 用于决定/实现这个promise // reject(..) 用于回绝这个promise});
5.promisory
将异步回调格调的函数,包装成promise格调,这个动作被称为"晋升"或者"promise工厂化",在某种意义上,产生promise的函数可一个看作一个promise工厂,书中将其命名为promisory ('Promise' + 'factory')。
自定义包装函数:
function ajax(url, cb) { // 模仿异步申请 setTimeout(function () { // cb(new Error('ajax Error'), 'hello'); Math.random() > 0.5 ? cb(null, 'no Error Data') : cb(new Error('ajax Error'), 'Error data'); // cb(null, 'no Error Data'); }, 1000);}// 未包装之前应用ajax('/api/user', function (err, data) { if (err) { console.log(err); } else { console.log(data); }})// 对回调会promise格调的函数进行包装if(typeof Promise !== 'undefined' && !Promise.wrap){ Promise.wrap = function(fn){ return function(){ var args = [].slice.call(arguments); return new Promise((resolve,reject)=>{ args.push(function(err,data){ err?reject(err):resolve(data); }); fn.apply(null,args); }) } }}// 包装之后应用promise_ajax = Promise.wrap(ajax);promise_ajax('/api/user').then(function (data) { console.log(data);}, function (err) { console.log(err);})
三、Generator
1.generator的根本应用
生成器是一类非凡的寒素,能够一次或屡次的启动或进行,并不一定非得实现,且it = foo()运算并没有执行生成器foo*(),而只是结构了一个迭代器(iterator),这个迭代器会管制它的执行。
function* foo(x) { let y = x * (yield 6); // (yield 6) 表达式的值,取决于下次调用 next() 时传入的参数 return y;}let it = foo(8);console.log(it.next()); // { value: 6, done: false }console.log(it.next(12));
2.迭代器
a.对象迭代器
在应用for..of遍历对象时,会像对象申请一个[Symbol.iterator]属性,获取一个迭代器对象(领有一个next属性(值为函数,返回{value:xx,done:false}))的iterable对象
let something = (function () { let nextValue; return { [Symbol.iterator]: function () { return this }, next: function () { nextValue = nextValue === undefined ? nextValue = 1 : (3 * nextValue + 1); return { value: nextValue, done: false }; } }})()// 这里没有应用for..of循环for (let count = 0; count < 10;) { let next = something.next(); console.log(next.value) count++;}
b.生成器迭代器
严格来说,生成器自身并不是iterable,当你执行了一个生成器也取得了一个迭代器:
function* foo() { let nextValue; while (true) { nextValue = nextValue === undefined ? 1 : nextValue * 3 + 1; yield nextValue; }}for(let n of gen){ if(n > 10000) break; console.log(n);}for(let n of gen){ // 这里并会迭代,gen迭代器在上次for..of的"异样完结后",这个迭代器就被终止了 if(n > 1000) break; console.log(n);}
进行生成器:for..of有一个暗藏的的个性"异样完结"(也就是"提前终止"),通常由break、return或者未捕捉的异样引起,回向生成器的迭代器发送一个信号使其终止。严格来说,在失常循环完结当前,也会一个终止信号。
应用return(..)向迭代器手工发送终止信号,并在迭代器外部应用try..finally语句检测接管这个信号:
function* foo() { try { let nextValue; while (true) { nextValue = nextValue === undefined ? 1 : nextValue * 3 + 1; yield nextValue; } } finally { // 当迭代器被终止后,代表try中的代码曾经被执行实现了,而后就会执行这里 console.log('lceaning up'); }}let gen = foo();for (let n of gen) { if (n > 1000) { console.dir( // 向迭代器发送终止信号,gen.return(newvalue) 返回{done:true,value:newvalue},也就达到了终止的目标,终止后,不会再运行下一个迭代循环 gen.return('hello world').value ); // 这里不须要break } console.log(n) }
3.异步迭代生成器
生成器yield暂停的个性意味着咱们不仅能从异步函数调用失去看似同步的返回值,还能够同步捕捉来自这些异步函数调用的谬误!将异步需要援用到生成器中:
function ajax(url, cb) { setTimeout(function () { cb(new Error('ajax Error'), 'hello'); }, 3000);}function foo(x, y) { ajax('/bar' + x + y, function (error, data) { if (!!error) { // 向*main抛出一个谬误 it.throw(error); } else { // 用收到的data复原*main(),使其继续执行 it.next(data); } })}function* main() { try { var text = yield foo('/user','/member'); console.log(text); }catch(e){ console.log(e); }}let it = main();it.next();
4.反对Promise的Generator Runner
须要应用到的工具函数:
function ajax(url, cb) { // 模仿异步申请 setTimeout(function () { // cb(new Error('ajax Error'), 'hello'); Math.random() > 0.5 ? cb(null, 'no Error Data') : cb(new Error('ajax Error'), 'Error data'); // cb(null, 'no Error Data'); }, 1000);}function request(url) { // 将回调格调的函数转换成promise return new Promise(function (resolve, reject) { ajax(url, function (err, data) { if (err) { reject(err); } else { resolve(data); } }); });}function foo(x, y) { return request('/api/foo?x=' + x + '&y=' + y);}function* main() { try { let text1 = yield foo('/user', '/member'); let text2 = yield foo('/user', '/order'); let text3 = yield foo('/user', '/address'); return text1 + text2 + text3; } catch (e) { console.log('error',e); }}
小小练习(生成器 + Promise):
let it = main();let nextResult = it.next(undefined);function handlenextResult(nextResult, cb) { if (nextResult.done) { cb(nextResult.value) } else { nextResult.value.then(function (data) { handlenextResult(it.next(data), cb); }, function (err) { it.throw(err); }); }}handlenextResult(nextResult, console.log);
封装应用反对Promise的Generator Runner:
function run(gen) {// 获取传给生成器的参数let args = [].slice.call(arguments, 1);// 在以后上下文中初始化一个generator对象let it = gen.apply(this, args);// 返回一个promise对象用于生成器实现return Promise.resolve().then(function handleNext(value) {// 对下一个yield的值运行 初始 value == undefinedlet next = it.next(value);return (function handleResult(next) { if (next.done) { // 生成器运行结束了吗?结束了就返回生成的return值 return next.value; } else { // 将yield的值转换成promise return Promise.resolve(next.value).then( // (data) => { // 这里的data就是promise决定胜利后的返回值 // return handleNext(data) // } // 简写成为 handleNext // 当生成器为完结时,继续执行 , function handleErr(err) { // 如果返回的promise被回绝了,就会调用这个函数 // 就把谬误传回給生成器进行出错解决 // 这里将谬误{done:true,value:undefined}包装成promise, return Promise.resolve(it.throw(err)).then( // 获取 // (data)=>{ // return handleResult(data); // } // 简写成为 handleResult的参数为{done:true,value:undefined},会完结本次的Generator Runner运行 handleResult ); }) } })(next);})}run(main).then(function (data) {console.log(data);}, function (err) {console.log(err);});console.log(1);
5.生成器中的Promise并发
让异步流程基于promise,特地是基于它们以工夫无关的形式治理状态的能力:
function request(url) { return new Promise(function promiseHandle(resolve, reject) { setTimeout(function timeHandler() { resolve('success'); }, 1000) })}function* foo() { // 让两个申请并行 let p1 = request('/api/user'); let p2 = request('/api/user'); // 期待两个promise都决定 let r1 = yield p1; let r2 = yield p2; var r3 = yield request('/api/user' + r1 + r2); console.log(r3);}// 应用后面的工具run(..)run(foo);
6.生成器委托
生成器委托次要的目标是组织代码,以达到与一般函数调用的对称,在一个生成器函数中调用另一个生成器。放弃生成器拆散有助于程序的可读性、可维护性、和调试性。
代码练习:
function* foo() { console.log('*foo() starting'); try { yield 'B'; } catch (e) { console.log('error caught inside *foo()', e); } yield 'C'; throw 'D';}function* bar() { yield 'A'; try { yield* foo(); } catch (err) { console.log('error caught inside *bar()', err); } yield 'E'; yield* baz(); // 这里baz*()抛出的异样没有被捕捉,所以bar*()、baz*()都会终止 // 注:不会达到这里 yield 'G'}function* baz() { throw 'F'; }let it = bar();console.log('outside', it.next().value); // Aconsole.log('outside', it.next(1).value); // Bconsole.log('outside', it.throw(2).value); // caught 2, Cconsole.log('outside', it.next(3).value); // caught 3, Etry { console.log('outside', it.next(4).value); } catch (err) { console.log('error catght outside:', err); // caught F}
四、程序性能
1.Web Worker
Web Worker是Web平台对HTML5新增的个性,这是浏览器(即宿主环境)的性能,实际上和JavaScript语言自身简直没有什么关系。也就是说,JavaScript以后并没有任何反对多线程的执行的性能。
尽管js是单线程运作的,然而像浏览器这样的环境,很容易提供多个JavaScript引擎实列,各自运行在本人的线程上,这样你能够在不同的线程上运行不同的程序。程序中么一个这样独立的多线程局部被称为一个(Web Worker)。这种类型的并行化被称为工作并行,因为其重点在于把程序划分为多个块来并发运行。Worker之间以及它们的主线程之间,不会共享任何作用域或资源。就没有了多线程编程的噩梦,而是通过几个根本的事件音讯机制互相分割。
// 主线程中let w1 = new Worker('./web_worker_01.js');w1.postMessage('Hello');// 作用于webwork运行addEventListener('message', function(e) { console.log('Message received from main script: ' + e.data);});
2.尾调用优化(Tail Call Optimization)
尾调用,就是呈现在另一个函数"结尾"处的函数调用。这个调用完结后,就没有其余的事件要做了(处了可能要返回后果值)
function foo(x) { return x + 1;}function bar(y) { return foo(y + 1); // 尾调用}function baz(y) { return foo(y) + 1; // 非尾调用 }
调用一个新的函数是须要额定的一块预留内存来治理调用栈,称为栈帧。但当反对TCO的引擎可能意识到foo(y + 1)调用位于bar函数的尾部,这意味着bar(...)基本上曾经实现了,那么在调用foo(..)时,他就不须要创立一个新的栈帧,而是能够重用已有的bar(..)的栈帧,这样不仅更快,也更节俭内存。在解决递归时,尤其有用。
// 这样并不能应用TCO调优// function factorial(n){// if(n==1){// return 1;// }// return n*factorial(n-1);// }// 这样能够应用TCO调优function factorial(n) { function fact(n, res) { if (n < 2) return res; return fact(n - 1, n * res); } return fact(n, 1);}console.log('f',factorial(5));
3.其它
a.asm.js
asm.js这个标签指JavaScript语言中能够高度优化的一个子集,通过小心防止某些难以优化的机制和模式(垃圾收集、类型强制转换、等等),asm.js格调的代码可一个JavaScript引擎辨认并进行特地激进的底层优化。
b.性能测试Beachmark.js
应用Benchmark.js的性能测试工具来测试js代码的运行性能比拟。名为jsPerf网站应用了Beachmark.js库来与逆行同级准确牢靠的测试。