狠狠撸

狠狠撸Share a Scribd company logo
Javascript 技巧与优化
kim
问题1
? Javascript 对于你来说是?
? Javascript 是 PHP 程序员最头痛的事
问题2
? 为什么要把 Javascript 放在 </body> 之前?
? IE经典错误“operation aborted”
? 不阻塞页面解析
问题3
? Javascript 为什么这么令人崩溃?
? 严苛的浏览器环境
? 变态的兼容问题,各种不同的内核、引擎
? 单线程,阻塞也得靠浏览器
技巧
? If、switch、for 语句都用 {} 括起来
? js文件几个注意的地方:
1. 统一用 UTF-8 编码
2. 文件头和尾都写上 “;”
3. 所有注释使用 /**/
4. 文件头写上 document.domain = "56.com";
方便跨域使用
? typeof (null) == 'object' 是 true
? var a;
? a == undefined 是 true
? null == undefined 是 true
? null === undefined 则是 false
? 字符串连接优先级高于算术运算:
? 1 + '1' + 1 = '111’
? '2' - 1 - 1 = 0
? switch 语句很快
? 把发生几率最高的 case 放在前面
? HTML5 是个无底洞
? js 没有函数重载概念:
function foo(a) {
alert(1);
}
function foo(a, b) {
alert(2);
}
var f = foo(100); // alert 2
? var a = '1';
? var b = {n:'kim'};
? var c = a; // 值复制
? var d = b; // 引用复制
? 初始化对象尽量使用字面量语法:
var obj = {
"name": "kim",
"age": 18
};
? RegExp构造函数中的反斜杠需要双重转义:
// 比如匹配 2.5、3.4 等小数
var e = /d.d/;
var e = new RegExp("d.d");
? 递归函数的定义:
var factorial = (function f(num){
if (num <= 1) {
return 1;
} else {
return num * f(num - 1);
}
});
? 理解 this 对象: 当前的环境对象:
window.name = 'win';
var obj = {name: "kim"};
function myEnv()
{
alert(this.name);
}
myEnv.call(window); // win
myEnv.call(obj) // kim
? 取 a-b 中间的随机数公式:
? var rand = Math.floor(Math.random() * b + a);
? prototype:定义对象的祖宗。
? 如果不了解 prototype,那就别使用它,一
般工厂模式或者单例模式就够用了
? 标准单例定义模式:
var singleton = function() {
var private = 'hey';
function somePrivateFunc() {
return 'hi';
}
var public = {
“name”: “kim”,
getPrivate: function() {
alert(private + somePrivateFunc());
}
}
return public;
}();
var s = singleton;
s.getPrivate();
? 跨浏览器取得窗口左上角位置:
? var left = (window.screenLeft ?
window.screenLeft : window.screenX);
? var top = (window.screenTop ?
window.screenTop : window.screenY);
? url跳转的几种方式:
window.location = 'http://example.com/';
location.href = 'http://example.com/';
location.assign('http://example.com/');
// 用replace的区别是取代history中的记录
location.replace('http://example.com/');
? 用 location 页内跳转:
? // url变成 http://example.com/#hash1
location.hash = '#hash1';
? // url变成 http://example.com/user/#hash1
location.pathname = 'user';
? // url变成 http://www.56.com/user/#hash1
location.hostname = 'www.56.com';
? 除非是紧急bug,或者小范围的修改,否则不要用
浏览器检测,而应该用功能检测:
// 正确
if (document.all) {
// do something with document.all
}
// 错误
if (browser.isIE) {
// do something with document.all
} else {
// do nothing with document.all
}
? 获取 html 元素:
document.documentElement
document.firstChild
document.childNodes[0]
? 2个子域页面共享js对象:
? document.domain = “example.com”
? document.write 输出 script 需注意:
? document.write("<script
type="test/javascript" src=/slideshow/javascript-24855277/24855277/"objcmt.js">" +
"</script>");
? 屏幕滚动到该元素:div.scrollIntoView();
? 站内js的兼容测试:必须按实际情况,根据
各浏览器的访问数据为依据
? 访问 iframe 中的 document:
? var iframe = document.getElementById('ifrm');
? var ifrmdoc = iframe.contentDocument
|| iframe.contentWindow.document;
? 谨记 DOM 事件流:
? 捕捉阶段:
? document -> html -> body -> ... -> div
? 冒泡阶段:
? div -> ... -> body -> html -> document
? setTimeout 高级定时器:
f = setTimeout(function () {
objcmt.ajaxFlvHeartbeat();
f = setTimeout(arguments.callee, 180000);
}, 180000);
? IE经典错误:
? “缺少标识符、字符串或数字”,一般就是多
了个逗号
? IE经典错误:
? “无效字符”,转义前面缺了引号,或者字符
串中的引号缺了转义
? 判断参数是否传入:
function foo(param1, param2)
{
if (param1) {
// bad
}
if (typeof param1 != 'undefined') {
// good, go on
}
if (param2 instanceof Function) {
// better, go on do with function
}
}
? instanceof, typeof 等等都不是100%正确的,
最实际的做法是直接判断该对象,或者该
方法是否可用,比如:
if (document.all) {
// do something with document.all
}
? 不要用各种 javascript 模板技术,从个人经
验来看,这些模板技术只会添乱
? 不要“添加、修改、重定义”已有实例或对象
的属性和方法
? 两者的区别?
? var obj1 = {"name": "kim"}
? var obj1 = {name: "kim"}
? JSONP 的原理:
function myCallback(rep) {
alert(rep);
}
var script = document.createElement('script');
script.src = /slideshow/javascript-24855277/24855277/"http:/comment.56.com/api/api.php?callback=myCallback";
document.body.appendChild(script);
// api.php
<?php
if (isset($_GET['callback'])) {
echo htmlspecialchars($_GET['callback'] . '()');
}
exit();
?>
? 尽量少用框架,或者基于框架开发的组件
? 网站流量统计:嵌入js方式的统计,比服务
器日志更准确,因为客户端有缓存。
? 用 <object> 先缓存js,再执行:
<script type="text/javascript">
// 先cache js,再执行
var myJs = "http://example.com/myJs.js";
function cachejs(script_filename){
var cache = document.createElement('object');
cache.data = script_filename;
cache.width = 0;
cache.height = 0;
document.body.appendChild(cache);
}
function loadjs(script_filename) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', script_filename);
script.setAttribute('id', 'script_id');
script_id = document.getElementById('script_id');
if(script_id){
document.getElementsByTagName('head')[0].removeChild(script_id);
}
document.getElementsByTagName('head')[0].appendChild(script);
}
cachejs(myJs);
loadjs(myJs);
</script>
? 使用 jQuery 深度复制来重置对象,比如要
在其它地方获取一个纯净的 oriObj 对象:
var pureObj = jQuery.extend(true, {}, oriObj);
function someFunc() {
oriObj = jQuery.extend(true, {}, pureObj);
}
someFunc();
// deal with oriObj
? 减少js依赖的方法:
1. 尽量不依赖别的 js 文件,插件或者引用都可以直接加
到文件中,作为本地代码
2. 使用本地存储来解耦,比如 cookie
3. 尽量不使用全局变量和全局函数,如必要,可以通过
统一使用一个全局变量来实现,比如:
var myGlobal = {
GLOBAL_CONSTANTS: "coco",
"globalName": "kim",
"globalFunc": function() {
alert('haha');
}
}
? 函数节流:
// 睡一会儿
function sleep(milliSeconds){
var startTime = new Date().getTime();
while (new Date().getTime() < startTime + milliSeconds);
}
var throttle = function(fn, delay) {
// 定时阀
var timer = null;
// 返回创建的函数
return function() {
var that = this,
args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(that, args);
}, delay);
};
};
i = 0;
var myFunc = function() {
// 每1.5秒加一点内容
sleep(1500);
document.body.innerHTML += i + '<br />';
i++;
}
window.onresize = throttle(myFunc, 100);
性能
? 尽量使用原生 javascript 方法
? 用逗号一次定义多个变量:
var a = 1,
b = 'hi',
c = [1,3];
? 初始化数组或对象都可以只用一条语句:
var arr = new Array();
arr[0] = 1;
arr[1] = 2;
// 完全等同以下:
var arr = [1, 2];
var obj = new Object();
obj.name = 'kim';
obj.age = 3;
// 完全等同于一条字面量定义:
var obj = {
"name": 'kim',
"age": 3
};
? 乘除2的操作都可以用位运算代替,比如:
var i = 2 * 4;
// 等价于
var i = 2 << 2;
? 三元运算符 “?:” 更简洁更快:
var min = Math.min(a, b); // 慢
var min = a < b ? a : b; // 快
? 直接数组追加比push要快:
arr.push(v); // 慢
arr[arr.length] = v; // 快
? 字符串连接:
// 慢,创建了一个临时字符串
str += 'x' + 'y';
// 更快,更少内存
str += 'x';
str += 'y';
? 尽量不要用 for-in 来遍历对象。
? for-in 会为对象的所有可枚举的属性创建一
个 list,并且在进行枚举之前检查重复属性。
? 最快的数组循环:
var i = arr.length - 1;
if (i > -1) {
do {
// do something with arr[i]
} while (--i >= 0);
}
? 尽量不用 with 语句,容易出 bug。
? with 延长了作用域,js编译时识别不了这个
附加作用域的,通常都可以直接用
obj.property 来代替。
? new String 的应用场景,如果需要多次调用字符串对象的属性时:
// 共创建21个变量,每次调用都会创建一个临时字符串变量
var s = '0123456789';
for (var i = 0; i < s.length; i++) {
s.charAt(i);
}
// 只创建一个对象
var s = new String('0123456789');
for (var i = 0; i < s.length; i++) {
s.charAt(i);
}
? 闭包:延长了作用域而且常驻内存。
? 对象、对象中的对象、闭包,在不用的时候就
设为 null ,等待内存回收,特别是当对象是
Dom 元素的时候。
obj1.obj2 = new Foo();
obj1.dom3 = document.getElementById('id1');
// some operations ...
obj1.obj2 = null;
obj1.dom3 = null;
obj1 = null;
? 尽量不用全局变量和全局函数:
1. 全局变量实际上是2个作用域,一个是当
前 js 脚本,一个 windows 。
2. 在整个js运行周期内都占用内存,并不像
局部变量,用完就可以被回收
? 函数内使用全局变量,尽量把全局变量变成局
部变量,或者通过参数传入函数内部,以保证
最短作用域链。
var foo = 'foo';
function bar()
{
var local = foo,
dm = document;
alert(local);
dm.write(local);
dm.write(local);
}
? try-catch 是低性能的,切忌用在循环里,“变量e”是个额外开销
for (var i = 0; i < obj.length; i++) {
try {
test[obj[i]].property = somevalue;
} catch(e) {
...
}
}
应该写在外面:
try {
for (var i = 0; i < obj.length; i++) {
test[obj[i]].property = somevalue;
}
} catch(e) {
...
}
? 更好的实践是根本不用 try-catch ,基本上
大多数情况下都可以用 if 来代替。
? js 这种弱类型的语言,用 if 能解决很多问题。
? setTimeout 和 setInterval 应该直接传入
function :
setTimeout("alert('hihi')", 10); // 慢
setTimeout(function() {
alert(‘hihi’);
}, 10); // 快
? getElementById 是最快的,仸何时候都是开
发首选。
? 所有类型的 selector 都是可以用指定 id 来替
代的。
? 尽量不使用 HTMLCollection 对象。
? 比如 getElementByTagName、
getElementsByName、document.images、
document.forms、jQuery('div') 等。
? innerHTML 和 outerHTML 的特点:
1. 很快,但不是所有元素都支持。
2. 需手动清除 innerHTML 里面 js 对象和属
性绑定,优化内存
? 避免重复设置 innerHTML,比如:
// 错误
for (var i = 0; i < 10; i++) {
div.innerHTML += 'hi';
}
// 正确
var htmltext = '';
for (var i = 0; i < 10; i++) {
htmltext += 'hi';
}
div.innerHTML(htmltext);
? 仸何情况下,遍历节点都是性能杀手,应
坚决避免,比如:
document.createNodeIterator
? eval == evil // 不解释
? 和 css 解耦:
1. 尽可能少的修改元素 style 属性
2. 不在 css 中使用 expression
3. 尽量通过修改 className 来修改样式
4. 通过 cssText 属性来设置样式值
// 进行了4次重新渲染
el.style.color = 'red;
el.style.height = '100px';
el.style.fontSize = '12px';
el.style.backgroundColor = 'white';
? 尽量不在元素中直接绑定事件处理,事件
处理程序的本质:
<input onclick="alert(this)" />
1. 开辟内存,创建一个函数
2. 函数内部封装了对该对象的引用,即 this
对象
3. 创建了局部变量 event 事件对象
4. 每定义一个函数处理程序就是对DOM的
一次访问
? 解决 inline script 阻塞页面的方法,排名先后:
1. window.onload:并行下载,渲染无阻
2. setTimeout:并行下载,部分浏览器渲染无
阻
3. 移到底部:并行下载,但是渲染还是阻塞
4. defer:并行下载,部分浏览器支持
? 设置元素的 position 为 absolute 或 fixed,可
以使元素从 DOM 树结构中脱离出来独立的
存在。
? 在元素的 position 为 static 和 relative 时,元
素处于 DOM 树结构当中,当对元素的某个
操作需要重新渲染时,浏览器会渲染整个
页面。
? 页面重复渲染,即 reflow 和 repaint 发生的情景:
1. DOM 元素的添加、修改(内容)、删除(Reflow +
Repaint)
2. 应用新的样式,或者修改任何影响元素外观属性的时
候(Reflow + Repaint)
3. 仅修改 DOM 元素的字体颜色(Repaint)
4. Resize 浏览器窗口、滚动页面(Reflow)
5. 读取元素的部分属性(offsetLeft、offsetTop、
offsetHeight、offsetWidth、scrollTop/Left/Width/Height、
clientTop/Left/Width/Height、getComputedStyle()、
currentStyle(in IE))(是的,读取属性也会引起repaint!)
? Reflow:简单的说,就是位置、大小变了
? Repaint:简单地说,就是字体、颜色变了
? 减少 reflow/repaint :
// 将元素的 display 设为 "none",完成修改后
再把 display 改为原来的值
var dv = document.getElementById('divId');
dv.style.display = 'none';
dv.innerHTML += 'hi ';
dv.innerHTML += 'kim';
dv.style.color = 'red';
dv.style.display = 'block';
? 减少 reflow/repaint :
// 使用 DocumentFragment 一次性创建多个DOM节点
var span1 = document.createElement('span');
var span2 = document.createElement('span');
span1.appendChild(document.createTextNode("hi"));
span2.appendChild(document.createTextNode("kim"));
var fragment = document.createDocumentFragment();
fragment.appendChild(span1);
fragment.appendChild(span2);
var div = document.getElementById("divContainer");
div.appendChild(fragment);
? 层与渲染的关系:
// 创建了2层
var newWidth = aDiv.offsetWidth + 10; // Read
aDiv.style.width = newWidth + 'px'; // Write
var newHeight = aDiv.offsetHeight + 10; // Read,清空了 reflow 队
列,导致新增一层
aDiv.style.height = newHeight + 'px'; // Write
// 只建了1层
var newWidth = aDiv.offsetWidth + 10; // Read
var newHeight = aDiv.offsetHeight + 10; // Read
aDiv.style.width = newWidth + 'px'; // Write
aDiv.style.height = newHeight + 'px'; // Write
? Javascript 终极优化 - 事件代理和委托
1. 减少 DOM 加载时间
2. 减少内存使用
3. 响应最快
/* 举一个实际应用中的例子:评论删除、回复、顶 */
"initListBindings": function()
{
var that = this;
/* 代理 click 时间 */
jq('#' + config.cmtTarget).bind('click', function(event){
event.stopPropagation(); /* 停止冒泡 */
var target = jq(event.target); /* 获取正在访问的对象 */
switch (target.attr('class')) {
case 'cmt_delete_link': /* 如果点击的是删除链接 */
that.openDelCommentPopup(); /* 打开删除对话框 */
break;
case 'cmt_reply_link': /* 如果点击的是回复链接 */
that.openReplyPopup(); /* 打开回复框 */
break;
case 'cmt_agree_link_em': /* 如果点击的是顶 */
target = target.parent();
case 'cmt_agree_link': /* 如果点击的是顶链接 */
that.ajaxIncNbAgree(event.target, ids[1], ids[2]); /* 执行“顶”动作 */
event.preventDefault(); // IE6 getJSON bug hack
break;
default:
break;
}
});
},
实例吐槽
Javascript 优化无止境
Faq
谢谢!

More Related Content

JavaScript 80+ Programming and Optimization Skills