推广app的妙招,网站seo置顶,7k7k网页游戏入口,多个微信管理系统为什么80%的码农都做不了架构师#xff1f; 看到一篇文章#xff0c;http://www.csdn.net/article/2012-11-20/2811887-writing-fast-memory-efficient-javascript/2 里面有个网页针对 原型链#xff0c;模块化#xff0c;模块化加缓存#xff0c;进行了比… 为什么80%的码农都做不了架构师 看到一篇文章http://www.csdn.net/article/2012-11-20/2811887-writing-fast-memory-efficient-javascript/2 里面有个网页针对 原型链模块化模块化加缓存进行了比对测试http://jsperf.com/prototypal-performance/12 测试用例及测试结果如下chrome下 测试代码如下 scriptBenchmark.prototype.setup function() {Klass1 function() {}Klass1.prototype.foo function() {log(foo);}Klass1.prototype.bar function() {log(bar);}Klass2 function() {var foo function() {log(foo);},bar function() {log(bar);};return {foo: foo, bar: bar}}var FooFunction function() {log(foo);};var BarFunction function() {log(bar);};Klass3 function() {return {foo: FooFunction, bar: BarFunction}}};
/script 愚见 先不说这个测试案例是否真能体现性能。只从代码书写和学习过程来说。 如果能摒弃一个function写完一整个功能的做法进入模块化编程。这是个进步。 再说上述所谓缓存其实就是给函数闭包加了一个引用因为引用的存在所以闭包不会按通常情况被销毁。如果有人这样写大量的全局变量对于需要长期维护更新的项目来说这简直就是灾难。我自己就深受其害而且是被自己所坑。绝对不能推广这种做法。有了开头就没结尾养成习惯就难改了。如果是在一个规模庞大前端依然成体系的公司万一人家要求还是不得不写不过这种情况下就连命名都会有诸多规范和要求吧。 最推荐的自然是原型链的写法。不过真心表示需要根据项目需要和能力而为否则写出来的东西简直就是惨不忍睹。不管怎样学习中。 原文摘录如下 Addy Osmani是谷歌公司Chrome团队中的一名程序开发工程师。他是一位JavaScript爱好者曾经编写过一本开放源码方面的书籍《Learning JavaScript Design Patterns》以及《Developing Backbone Applications》。为Modernizr和jQuery社区贡献了开源项目目前正在从事‘Yeoman’项目旨在为开发者提供一系列健壮的工具、程序库和工作流帮助他们快速构建出漂亮、引人注目的Web应用。本文作者将带领大家探索高效编写代码的测试验证方法。文章内容如下 JavaScript引擎包括Google V8ChromeNode都是专为快速执行大型JavaScript程序而设计的。在开发过程中如果你在乎内存使用率和性能情况那么你应该会关心在用户的浏览器中JavaScript引擎背后是怎么样的。无论是V8、SpiderMonkey (Firefox)、Carakan (Opera)、Chakra (IE) 还是其他有了它们可以帮助你更好的优化应用程序。 我们应该时不时地询问自己 我还能做些什么使代码更加有效主流的JavaScript引擎做了哪些优化什么是引擎无法优化的我能期待利用垃圾回收进行清洁吗当涉及到编写高效的内存和快速创建代码时总会出现一些常见的弊端在这篇文章中我们将探索高效编写代码的测试验证方法。 一、JavaScript如何在V8中工作 如果你对JS引擎没有较深的了解开发一个大型Web应用也没啥问题就好比会开车的人也只是看过引擎盖而没有看过车盖内的引擎一样这里将Web网页比如成汽车。Chrome浏览器是我的优先选择这里我将谈下V8的核心组件 一个基本的编译器在代码执行前分析JavaScript、生成本地机器代码而非执行字节代码或是简单的解释该段代码之初不是高度优化的。V8用对象模型“表述”对象。在JavaScript中对象是一个关联数组但是V8中对象被“表述”为隐藏类这种隐藏类是V8的内部类型用于优化后的查找。运行时分析器监视正在运行的系统并优化“hot”活跃函数。比如终结运行已久的代码通过运行时分析器把优化编译器重新编译和被运行时分析器标识为“hot”的代码 这是一种有效的编译优化技术,例如用被调用者的主体替换函数调用的位置。V8支持去优化也就是说当你发现一些假设的优化代码太过乐观优化编译器可以退出已生成的代码。垃圾回收了解它是如何工作的如同优化JavaScript一样同等重要。二、垃圾回收 垃圾回收是内存管理的一种形式它试图通过将不再使用的对象修复从而释放内存占用率。垃圾回收语言比如JavaScript是指在JavaScript这种垃圾回收语言中应用程序中仍在被引用的对象不会被清除。手动消除对象引用在大多数情况下是没有必要的。通过简单地把变量放在需要它们的地方理想情况下尽可能是局部作用域即它们被使用的函数里而不是函数外层一切将运作地很好。 在JavaScript中强制执行垃圾回收是不取的当然你也不会想这么做因为垃圾回收进程被运行时控制着它知道什么时候才是适合清理代码的最好时机。 1.“消除引用”的误解De-Referencing Misconceptions 在JavaScript中回收内存在网上引发了许多争论虽然它可以被用来删除对象map中的属性key但有部分开发者认为它可以用来强制“消除引用”。建议尽可能避免使用delete在下面的例子中delete o.x 的弊大于利因为它改变了o的隐藏类使它成为通用的慢对象。 var o { x: 1 };
delete o.x; // true
o.x; // undefined 目的是为了在运行时避免修改活跃对象的结构JavaScript引擎可以删除类似“hot”对象并试图对其进行优化。如果该对象的结果没有太大改变超过生命周期删除可能会导致其改变。 对于null是如何工作也是有误解的。将一个对象引用设置为null并没有使对象变“空”只是将它的引用设置为空而已。使用o.x null比使用delete会更好些但可能也不是很必要。 var o { x: 1 };
o null;
o; // null
o.x // TypeError 如果这个引用是最后一个引用对象那么该对象可进行垃圾回收倘若不是那么此方法不可行。注意无论您的网页打开多久全局变量不能被垃圾回收清理。 var myGlobalNamespace {}; 当你刷新新页面时或导航到不同的页面关闭标签页或是退出浏览器才可进行全局清理当作用域不存在这个函数作用域变量时这个变量才会被清理即该函数被退出或是没有被调用时变量才能被清理。 经验法则 为了给垃圾回收创造机会尽可能早的收集对象尽量不要隐藏不使用的对象。这一点主要是自动发生这里有几点需要谨记 正如之前我们提到的手动引用在合适的范围内使用变量是个更好的选择而不是将全局变量清空只需使用不再需要的局部函数变量。也就是说我们不要为清洁代码而担心。确保移除不再需要的事件侦听器尤其是当DOM对象将要被移除时。如果你正在使用本地数据缓存请务必清洁该缓存或使用老化机制来避免存储那些不再使用的大量数据。2.函数Functions 正如我们前面提到的垃圾回收的工作原理是对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。下面的例子能够更好的说明这一点 function foo() {
var bar new LargeObject();
bar.someCall();
} 当foo返回时bar自动指向垃圾回收对象这是因为没被调用这里我们将做个对比 function foo() { var bar new LargeObject(); bar.someCall(); return bar;
}
// somewhere else
var b foo(); 这里有个调用对象且被一直调用着直到这个调用交给b或是超出b范围。 3.闭包Closures 当你看到一个函数返回到内部函数该内部函数可以访问外部函数即使外部函数正在被执行。这基本上是一个封闭的可以在特定的范围内设置变量的表达式。比如 function sum (x) { function sumIt(y) { return x y; }; return sumIt;
} // Usage
var sumA sum(4);
var sumB sumA(3);
console.log(sumB); // Returns 7 在sum调用上下文中生成的函数对象sumIt是无法被回收的它被全局变量sumA所引用并且可以通过sumA(n)调用。 这里有个示例演示如何访问largeStr var a function () { var largeStr new Array(1000000).join(x); return function () { return largeStr; };
}(); 我们可以通过a() var a function () { var smallStr x; var largeStr new Array(1000000).join(x); return function (n) { return smallStr; };
}(); 此时我们不能访问了因为它是垃圾回收的候选者。 4.计时器Timers 最糟糕的莫过于在循环中泄露或者在setTimeout()/setInterval()中但这却是常见的问题之一。 var myObj { callMeMaybe: function () { var myRef this; var val setTimeout(function () { console.log(Time is running out!); myRef.callMeMaybe(); }, 1000); }
}; 如果我们运行 myObj.callMeMaybe(); 在计时器开始前我们看到每一秒“时间已经不多了”这时我们将运行 myObj null; 三、当心性能陷阱 除非你真正需要否则永远不要优化代码。在V8中你能轻易的看到一些细微的基准测试显示比如N比M更佳但是在真实的模块代码中或是在实际的应用程序中测试这些优化所带来的影响要比你想象中要小的多。 创建一个模块这里有三点 采用本地的数据源包含ID数值绘制一个包含这些数据的表格添加事件处理程序当用户点击的任何单元格时切换单元格的css class如何存储数据如何高效的绘制表格并追加到DOM怎样处理表单上的事件 注意下面的这段代码千万不能做 var moduleA function () { return { data: dataArrayObject, init: function () { this.addTable(); this.addEvents(); }, addTable: function () { for (var i 0; i rows; i) { $tr $(tr/tr); for (var j 0; j this.data.length; j) { $tr.append(td this.data[j][id] /td); } $tr.appendTo($tbody); } }, addEvents: function () { $(table td).on(click, function () { $(this).toggleClass(active); }); } };
}(); 很简单但是却能把工作完成的很好。 请注意直接使用DocumentFragment和本地DOM方法生成表格比使用jQuery更佳事件委托通常比单独绑定每个td更具备高性能。jQuery一般在内部使用DocumentFragment但是在这个例子中通过内循环调用代码append() 因此无法在这个例子中进行优化但愿这不是一个诟病但请务必将代码进行基准测试。 这里我们通过opting for documentFragment提高性能事件代理对简单的绑定是一种改进可选的DocumentFragment也起到了助推作用。 var moduleD function () { return { data: dataArray, init: function () { this.addTable(); this.addEvents(); }, addTable: function () { var td, tr; var frag document.createDocumentFragment(); var frag2 document.createDocumentFragment(); for (var i 0; i rows; i) { tr document.createElement(tr); for (var j 0; j this.data.length; j) { td document.createElement(td); td.appendChild(document.createTextNode(this.data[j])); frag2.appendChild(td); } tr.appendChild(frag2); frag.appendChild(tr); } tbody.appendChild(frag); }, addEvents: function () { $(table).on(click, td, function () { $(this).toggleClass(active); }); } }; }(); 我们不妨看看其他提供性能的方法也许你曾读过使用原型模式或是使用JavaScript模板框架进行高度优化。但是使用这些仅针对可读的代码。此外还有预编译。我们一起来实践下 moduleG function () {}; moduleG.prototype.data dataArray;
moduleG.prototype.init function () { this.addTable(); this.addEvents();
};
moduleG.prototype.addTable function () { var template _.template($(#template).text()); var html template({data : this.data}); $tbody.append(html);
};
moduleG.prototype.addEvents function () { $(table).on(click, td, function () { $(this).toggleClass(active); });
}; var modG new moduleG(); 事实证明选择模板和原型并没有给我们带来多大好处。 四、V8引擎优化技巧 特定的模式会导致V8优化产生故障。很多函数无法得到优化你可以在V8平台使用--trace-opt file.js搭配d8实用程序。 如果你关心速度那么尽最大努力确保单态函数(functions monomorphic)确保变量包括属性数组和函数参数只适应同样的隐藏类包含的对象。 下面的代码演示了我们不可这么做 function add(x, y) { return xy;
} add(1, 2);
add(a,b);
add(my_custom_object, undefined); 未初始化时不要加载和执行删除操作因为它们并没有输出差异这样做反而会使程序变得更慢。不要编写大量函数函数越多越难优化。 1.Objects使用技巧 适应构造函数来创建对象。这将确保所创建的所有对象具备相同的隐藏类并有帮助避免更改这些类。 在程序或者复杂性上不要限制多种对象类型。原因长原型链中倾向于伤害只有极少数的对象属性得到一个特殊的委托对于活跃对象保持短原型链以及低字段计数。 2.对象克隆Object Cloning 对象克隆对于应用开发者来说是一种常见的现象。虽然在V8中这是实现各种类型问题的基准但是当你进行复制时一定要当心。当复制较大的程序时通常很会慢因此尽量不要这么做。在JavaScript循环中此举是非常糟糕的。这里有个最快的技巧方案你不妨学习下 function clone(original) { this.foo original.foo; this.bar original.bar;
}
var copy new clone(original); 3.模块模式中的缓存功能 在模块模式中使用缓存功能也许在性能方面会有所提升。请参阅下面的例子通过jsPerf test测试。注使用这种方法比依靠原型模式更佳。 推荐这是测试原型与模块模式性能代码 // Prototypal pattern Klass1 function () {} Klass1.prototype.foo function () { log(foo); } Klass1.prototype.bar function () { log(bar); } // Module pattern Klass2 function () { var foo function () { log(foo); }, bar function () { log(bar); }; return { foo: foo, bar: bar } } // Module pattern with cached functions var FooFunction function () { log(foo); }; var BarFunction function () { log(bar); }; Klass3 function () { return { foo: FooFunction, bar: BarFunction } } // Iteration tests // Prototypal var i 1000, objs []; while (i--) { var o new Klass1() objs.push(new Klass1()); o.bar; o.foo; } // Module pattern var i 1000, objs []; while (i--) { var o Klass2() objs.push(Klass2()); o.bar; o.foo; } // Module pattern with cached functions var i 1000, objs []; while (i--) { var o Klass3() objs.push(Klass3()); o.bar; o.foo; }
// See the test for full details 4.数组使用技巧 一般情况下我们不要删除数组元素。它是使数组过渡到较慢的内部表现形式。当密钥集变得稀疏时V8最终将切换到字典模式这是变慢的原因之一。 五、应用优化技巧 在Web应用领域里速度就是一切。没有用户希望在启动电子表格时需要等上几秒钟或者花上几分钟时间来整理信息。这也是为什么在性能方面需要格外注意的一点有人甚至将编码阶段称为至关重要的一部分。 理解和提升性能方面是非常有用的但它也有一定的难度。这里推荐几个步骤来帮你解决 测试在应用程序中找到慢的节点 (~45%)理解查找问题所在(~45%)修复:(~10%)当然还有许多工具或是技术方案帮助解决以上这些问题 1.基准测试。在JavaScript上有许多方法可进行基准测试。 2.剖析。Chrome开发工具能够很好的支持JavaScript分析器。你可以使用这些性能进行检测哪些功能占用的时间比较长然后对其进行优化。最重要的是即使是很小的改变也能影响整体的表现。关于这款分析工具这里有份详细的介绍。 3.避免内存泄露-3快照技术。谷歌开发团队通常会使用Chrome开发工具包括Gmail来帮助他们发现和修复内存泄露此外3 snapshot也是不错的选择。该技术允许在程序中记录一些行为、强制垃圾回收、查询如果DOM节点无法返回预期的基线上3 snapshot帮助分析确定是否存在内存泄露。 4.单页面程序上的内存管理。当你在编写单页程序时比如AngularJSBackboneEmber)内存管理非常重要他们从未得到刷新。这就意味着内存泄露很明显。在移动单页程序上存在着巨大的陷阱因为内存有限长期运行的程序比如email客户端或者社交网络应用。因此它肩负着巨大的责任。 Derick发表了这篇《memory pitfalls》教您如何使用Backbone.js以及如何进行修复。Felix Geisendouml;rfer的这篇在Node中调试内存泄露也值得一读。 5.最小化回流。回流是指在浏览器中用户阻止此操作所以它是有助于理解如何提高回流时间你可以使用DocumentFragment一个轻量级的文档对象来处理。 6.Javascript内存泄露检测器。由Marja Houml;lttauml;和Jochen Eisinger两人开发的这款工具你不妨试试。 7.V8 flags调试优化和内存回收。Chrome支持通过flags和js-flags flag获取更详细的输出 例如 /Applications/Google Chrome/Google Chrome --js-flags--trace-opt --trace-deopt Windows用户运行chrome.exe --js-flags--trace-opt --trace-deopt。 当开发应用时可以使用下面的V8 flags trace-opt –日志名称的优化功能显示优化跳过的代码trace-deopt –记录运行时将要“去优化”的代码。trace-gc – 对每次垃圾回收时进行跟踪结束语 正如上面提到的在JavaScript引擎中有许多隐藏的陷阱世界上没有什么好的银弹能够帮助你提高性能只有通过在测试环境中进行优化实现最大的性能收益。因此了解引擎如何输出和优化代码可以帮助你调整应用程序。 因此测试它、理解它、修复它如此往复 英文出自Coding.smashingmagazine 转载于:https://my.oschina.net/maomi/blog/92028