一、带有 src 属性的<script>元素不该该在其<script>和</script>标签之间再包含额外的 JavaScript 代码。若是包含了嵌入的代码,则只会下载并执行外部脚本文件,嵌入的代码会被忽略。通常都把所有 JavaScript 引用放在<body>元素中页面内容的后面。node
二、循环引用:对象 A 中包含一个指向对象 B 的指针,而对象 B 中也包含一个指向对象 A 的引用:浏览器
var element = document.getElementById("some_element"); var myObject = new Object(); myObject.element = element; element.someObject = myObject;
IE8及之前版本中有一部分对象并非原生 JavaScript 对象,而是使用 C++以 COM(Component Object Model,组件对象模型)对象的形式实现的的,而 COM 对象的垃圾收集机制因为采用了引用计数策略,因此会有循环引用的问题,而循环引用会致使即便将例子中的 DOM 从页面中移除,它也永远不会被回收,所以会致使内存泄露。因此一旦数据再也不有用,最好经过将其值设置为 null 来释放其引用:闭包
myObject.element = null; element.someObject = null;
三、未初始化的变量会自动被赋予 undefined 值,但显式地初始化变量依然是明智的选择,当 typeof 操做符返回"undefined"值时,咱们就知道被检测的变量尚未被声明,而不是还没有初始化。函数
四、建立对象推荐组合使用构造函数模式和原型模式:this
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true
组合使用构造函数模式和原型模式中构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。spa
其余模式的缺点:prototype
工厂模式:虽然解决了建立多个类似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型);指针
构造函数模式:每一个方法都要在每一个实例上从新建立一遍;code
原型模式:原型中全部属性是被不少实例共享的,这种共享对于函数很是合适,然而对于包含引用类型值的属性来讲问题比较大。orm
五、JavaScript中的继承可使用组合继承(也叫伪经典继承):
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ //继承属性 SuperType.call(this, name); this.age = age; } //继承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27
组合继承使用原型链实现对原型属性和方法的继承,经过借用构造函数来实现对实例属性的继承,从而避免了原型链和借用构造函数的缺陷。可是组合继承也有不足,即不管什么状况下,都会调用两次超类型构造函数:一次是在建立子类型原型的时候(new SuperType()),另外一次是在子类型构造函数内部(SuperType.call(this, name)),寄生组合式继承克服了这个缺点,基本模式以下:
function object(o){ function F(){} F.prototype = o; return new F(); } function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //建立对象 prototype.constructor = subType; //加强对象 subType.prototype = prototype; //指定对象 }
所谓寄生组合式继承,即经过借用构造函数来继承属性,经过原型链的混成形式来继承方法。其背后的基本思路是:没必要为了指定子类型的原型而调用超类型的构造函数,咱们所须要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,而后再将结果指定给子类型的原型。咱们就能够用调用 inheritPrototype()函数的语句,去替换前面例子中为子类型原型赋值的语句,例如:
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); };
六、因为耦合的问题,在编写递归调用时,使用 arguments.callee 总比使用函数名更保险:
function factorial(num){ if (num <= 1){ return 1; } else { return num * arguments.callee(num-1); //不要使用return num * factorial(num-1); } }
七、因为闭包会携带包含它的函数的做用域,所以会比其余函数占用更多的内存,过分使用闭包可能会致使内存占用过多。
八、因为 JavaScript 没有块级做用域,所以可使用匿名函数来模仿块级做用域:
(function(){ //这里是块级做用域 })();
这种作法能够减小闭包占用的内存问题,由于没有指向匿名函数的引用。这种技术也常常在全局做用域中被用在函数外部,从而限制向全局做用域中添加过多的变量和函数。
九、尽可能不要使用间歇调用,由于在不加干涉的状况下,间歇调用将会一直执行到页面卸载,并且后一个间歇调用可能会在前一个间歇调用结束以前启动,最好使用超时调用来模拟间歇调用,对好比下两段代码:
间歇调用: var num = 0; var max = 10; var intervalId = null; function incrementNumber() { num++; //若是执行次数达到了 max 设定的值,则取消后续还没有执行的调用 if (num == max) { clearInterval(intervalId); alert("Done"); } } intervalId = setInterval(incrementNumber, 500);
超时调用模拟间歇调用: var num = 0; var max = 10; function incrementNumber() { num++; //若是执行次数未达到 max 设定的值,则设置另外一次超时调用 if (num < max) { setTimeout(incrementNumber, 500); } else { alert("Done"); } } setTimeout(incrementNumber, 500);
十、为了确保跨浏览器兼容,最好仍是将 nodeType 属性与数字值进行比较,由于IE没法访问 Node 类型。
十一、使用cloneNode()方法时在复制以前最好先移除事件处理程序,由于IE 在此存在一个 bug,它会复制事件处理程序。
十二、尽可能减小访问 NodeList 的次数。由于每次访问 NodeList,都会运行一次基于文档的查询。
1三、因为老版本的浏览器不支持,所以在有特殊须要时再使用事件捕获,能够放心地使用事件冒泡。
1四、由于HTML 与 JavaScript 代码紧密耦合,所以不要使用 HTML 事件处理程序,可使用 JavaScript 指定事件处理程序。
1五、使用事件委托,在DOM 树中尽可能最高的层次上添加一个事件处理程序,没必要给每一个可单击的元素分别添加事件处理程序,由于事件委托利用了事件冒泡,只指定一个事件处理程序,就能够管理某一类型的全部事件。最适合采用事件委托技术的事件包括 click、mousedown、mouseup、keydown、keyup 和 keypress。
1六、调用 submit()方法的形式提交表单时,不会触发 submit 事件,所以要记得在调用此方法以前先验证表单数据。与调用 submit()方法不一样,调用 reset()方法会像单击重置按钮同样触发 reset 事件。
1七、读取或设置文本框的值时不建议使用标准的 DOM 方法,而是使用 value 属性:
var textbox = document.forms[0].elements["textbox1"]; textbox.value = "Some new value";
换句话说,不要使用 setAttribute()设置<input>元素的 value 特性,也不要去修改<textarea>元素的第一个子节点。缘由很简单:对 value 属性所做的修改,不必定会反映在 DOM 中。所以,在处理文本框的值时,最好不要使用 DOM 方法。
1八、不建议使用常规的 DOM 功能来访问 option 元素的数据,由于效率比较低,最好是使用特定于选项的属性,由于全部浏览器都支持这些属性:
var selectbox = document.forms[0].elements["location"]; //不推荐 var text = selectbox.options[0].firstChild.nodeValue; //选项的文本 var value = selectbox.options[0].getAttribute("value"); //选项的值 //推荐 var text = selectbox.options[0].text; //选项的文本 var value = selectbox.options[0].value; //选项的值