| 1 2 3 4 5 6 7 8 9 10 11 | function test(){ try{ console.log(1); }finally{ condole.log(2); } } console.log(test()); <!-- 1 2 --> //结果:它按顺序执行了 |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //在 try 中加入 return 语句 function test(){ try{ console.log(1); return 'try'; }catch(e){ //TODO }finally{ console.log(2); } } console.log(test()); <!-- 1 2 try --> //在 try和 catch的代码块中,如果碰到 return语句,那么在 return之前,会先执行finally中的内容 |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //在 finally中也加入 return语句 function test(){ try{ console.log(1); return 'try'; }catch(e){ //TODO }finally{ console.log(2); return 'finally'; } } console.log(test()); <!-- 1 2 finally --> //按照上一条规则,finally是会先执行的,所以如果 finally里有 return语句,那么就真的return了。 |
- 不管有没有异常,finally 块中代码都会执行
- 当 try 和 catch 中有 return 时,finally 仍然会执行
- finally 是在 return 后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管 finally 中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),
所以函数返回值是在 finally 执行前确定的。- finally 中最好不要包含 return,否则程序会提前退出,返回值不是 try 或 catch 中保存的返回值。
内部机制任何执行 try 或者 catch 中的 return 语句之前,都会先执行 finally 语句(如果 finally 存在的话)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //测试 function test(){ var x = 1; try{ x++; return x; } finally{ ++x; } } console.log(test()); <!-- 2 --> |
同步编程:即是一种典型的请求——响应模型,当请求调用一个函数或方法后,需等待其响应返回,然后执行后续代码。
异步编程:不同于同步编程的请求——响应模式,其是一种事件驱动编程,请求调用函数或方法后,无需立即等待响应,可以继续执行其他任务,而在之前任务响应返回后可以通过状态、通知和回调来通知调用者。
通常实现异步方式是多线程,如 C#,即同时开启多个线程,不同操作能并行执行。
并发模型JavaScript 语言试单线程,单线程在程序执行时,所走的程序路径是按照连续顺序排下来的。前面必须处理好,后面才会执行。前面代码报错,后面的程序将不会执行。JavaScript 单线程异步编程可以实现多任务并发执行。 - 并行:指同一时刻内多任务同时进行 - 并发:指在同一时间段内,多任务同时进行着,但是某一时刻,只有某一任务执行。
事件循环
- 堆栈与队列
- 堆:内存中某一未被阻止的区域,通常存储对象(引用类型);
- 栈:后进先出的顺序存储数据结构,通常存储函数参数和基本类型数值变量(按值访问);
- 队列:先进先出顺序存储数据结构
setTimeout
- 宿主环境为 JavaScript 创建线程时,会创建堆(heap)和栈(stack),堆内存储 JavaScript 对象,栈内存储执行上下文;
- 栈内执行上下文的同步任务按序执行,执行完即退栈,而当异步任务执行时,该异步任务进入等待状态(不入栈),同时通知线程:当触发该事件时(或该异步操作响应返回时),需向消息队列插入一个事件消息;
- 当事件触发或响应返回时,线程向消息队列插入该事件消息(包含事件及回调);
- 当栈内同步任务执行完毕后,线程从消息队列取出一个事件消息,其对应异步任务(函数)入栈,执行回调函数,如果未绑定回调,这个消息会被丢弃,执行完任务后退栈;
- 当线程空闲(即执行栈清空)时继续拉取消息队列下一轮消息(next tick,事件循环流转一次称为一次 tick)。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //定义主函数,回调函数作为参数 function A(callback) { callback(); console.log("我是主函数"); } //定义回调函数 function B() { setTimeout("console.log('我是回调函数')", 3000); //模仿耗时操作 } //调用主函数,将函数B传进去 A(B); //输出结果 我是主函数; 我是回调函数; |
10w 条记录的数组,一次性渲染到页面,如何处理可以不冻结 ul?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | (function() { const ulContainer = document.getElementById("list-with-big-data"); // 防御性编程 if (!ulContainer) { return; } const total = 100000; // 插入数据的总数 const batchSize = 4; // 每次批量插入的节点个数,个数越多,界面越卡顿 const batchCount = total / batchSize; // 批处理的次数 let batchDone = 0; // 已完成的批处理个数 function appendItems() { // 使用 DocumentFragment 减少 DOM 操作次数,对已有元素不进行回流 const fragment = document.createDocumentFragment(); for (let i = 0; i < batchSize; i++) { const liItem = document.createElement("li"); liItem.innerText = batchDone * batchSize + i + 1; fragment.appendChild(liItem); } // 每次批处理只修改 1 次 DOM ulContainer.appendChild(fragment); batchDone++; doAppendBatch(); } function doAppendBatch() { if (batchDone < batchCount) { // 在重绘之前,分批插入新节点 window.requestAnimationFrame(appendItems); } } // kickoff doAppendBatch(); // 使用 事件委托 ,利用 JavaScript 的事件机制,实现对海量元素的监听,有效减少事件注册的数量 ulContainer.addEventListener("click", function(e) { const target = e.target; if (target.tagName === "LI") { alert(target.innerText); } }); })(); |
| 欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) | 黑马程序员IT技术论坛 X3.2 |