一、DOM性能
1、尽量较少DOM节点的操作(增,删,改,查)。因此对于DOM的操作都尽量进行变量缓存。
2、使用HTML集合优化(类数组,但是没数组的方法)。
document.getElementsByClassName()
document.getElementsByName()
document.getElementsByTagName()
如果需要多次使用集合,可以考虑将集合复制到一个数组。(复制会遍历一次集合,因此要做合理考虑,是否值得复制)
var coll = document.getElementsByTagName('div');
var length = coll.length;
var arr = []
for (var i = 0; i < length; i ++) {
arr[i] = coll[i]
}
console.log(arr)
console.log(arr.push(3));
console.log(coll.push(3)); // coll.push is not a function
3、遍历children 要比childNodes更快,而在IE下更明显。
4、如果支持querySelectorAll(),尽可能采用它,该方法不反回集合,不会有集合的性能问题。
5、减少DOM重绘或重排次数。
- 增、删可见DOM
- 位置改变
- 尺寸改变
- 内容改变,文字变图片
- 浏览器窗口改变
5.1、优化方法一,合并
// 两次的样式改变,都会导致重绘
document.getElementById('add').style.padding = '1px';
document.getElementById('add').style.margin = '3px';
// 将多次操作合并为一次,只重绘一次
document.getElementById('add').style.cssText = 'padding: 1px;margin: 3px;'
5.2、优化方法二,对样式的操作,改为类名的变换
document.getElementById('add').className = 'change'
5.3、优化方法三、对于多次重复操作,采用从文档流摘除,多次操作,添加回文档
例:ul里有两条数据,现在需要对其进行宽度和高度的修改,并增加三条数据。
第一步:移除存在的li。活着ul的display改为none,(对隐藏的DOM修改,不会重绘)
第二步:(记住li的结构)用字符串拼接好这两个li,填写宽高的行内样式。并且与新增的三条拼接上,(这期间都是字符操作,并不会导致DOM重绘)
第三步:添加到ul里。
二、DOM原型继承
符合DOM标准的浏览器都支持HTMLElement类。DOM文档中所有元素都继承这个类,HTMLElement又继承于Element,Element继承于Node类。
// HTMLElement的prototype上,有着各种原生的DOM操作方法。而我们也可以写自己的方法。(ie是玩不了的,不过有高人提供了方法,有兴趣可以搜索)
HTMLElement.prototype.zzz = function () {
console.log(this.id)
}
document.getElementById('add').zzz();
三、鼠标拖放
mousemove
1、清楚几个坐标:按下鼠标时的指针坐标,移动中当前指针坐标,松开时的指针坐标,拖放元素的原始位置,拖动中的元素坐标。
2、各个位置,对应的关系,和兼容性。MDN
3、鼠标拖动事件,主要是基于mousedown 和mouseup 和 mousemove三个事件,用来处理不同时期的不同需求。
四、对象
1、原型链
Object.prototype.a = 1;
Function.prototype.a = 2;
console.log(Object.a); // 2
console.log(Function.a); // 2
var obj = {};
console.log(obj.a); // 1
var f = Object;
console.log(f.a); // 2
var f2 = new Function();
console.log(f2.a); // 2
var obj2 = new Object();
console.log(obj2.a); // 1
2、用hasOwnProperty()方法过滤原型属性。用typeof过滤方法。
3、封装类继承,
// 封装函数
function extant (Sub, Sup) {
// 定义一个空函数 ,用来实现功能中转,避免因为超类太大,实例产型大量的系统消耗。
var F = function () {};
F.prototype = Sup.prototype; // 设置空函数的原型为超类的原型
Sub.prototype = new F(); // 实例化原型,并把超类的原型引用传递给子类。
Sub.prototype.constructor = Sub; // 恢复子类构造器为自身。
Sub.Sup = Sup.prototype;
if (Sub.prototype.constructor == Object.prototype.constructor) {
Sup.prototype.constructor = Sup;
}
}
// 超类
function A (x) {
this.x = x;
}
A.prototype.add = function () {
return this.x + this.x
}
A.prototype.mul = function () {
return this.x * this.x
}
// 子类
function B (x) {
A.call(this, x);
}
// 调用封装
extant(B, A);
// 如果子类同样定义了,add方法,会导致超类的被覆盖。
// B.prototype.add = function () {
// return this.x + ' 4 ' + this.x
//}
// 采用该方式调用,避免耦合,Sub.Sup = Sup.prototype;该语句的作用
//B.prototype.add = function () {
// return B.Sup.add.call(this);
//}
var f = new B(5);
console.log(f.add()) // 10
console.log(f.mul()) // 25
4、重载:同名方法有多个实现。重载和覆盖是完全不同的东西。
对于那些提供可选参数的方法,都可以算是重载
// 可以实现任意个参数的求和,(多个实现)
function f () {
var sum = 0;
for (var i = 0; i < arguments.length; i ++) {
if (typeof arguments[i] === 'number') {
sum += arguments[i]
}
}
return sum;
}
console.log(f(3,5,7,9))
5、覆盖:子类中定义的方法与超类同名,并且参数类型和个数都相同,当子类实例化后,超类的同名方法将被隐藏。(反正就是你能理解的那种覆盖就对了)
五、错误捕获
函数内部发生了错误,如果自身没有捕获,错误就会沿着函数调用链向上抛出,直到被JavaScript引擎捕获,代码终止执行。
// 由于错误会沿着函数调用向上抛,所以不必所有函数都进行错误捕获,可以在主要的位置进行即可
function main(s) {
console.log('开始 main()');
try {
foo(s);
} catch (e) {
console.log(e);
}
// 如果该处为捕获的话,会导致代码终止结束无法输出。合理利用捕获,保证代码的健壮性。
console.log('结束');
}
function foo(s) {
console.log('开始 foo()');
func(s);
}
function func(s) {
console.log('开始 func()');
console.log(s.name);
}
main(null);
对于异步调用和js的绑定事件,不能直接在外部捕获。
无效示例:
var $btn = document.getElementById('btn');
try {
$btn.onclick = function () {
console.log(rrr);
};
} catch (e) {
console.log('error');
}
有效示例:
var $btn = document.getElementById('btn');
$btn.onclick = function () {
try {
console.log(rrr);
} catch (e) {
console.log('error');
}
};
表单
1、.value
方式可以应用于text、password、hidden以及select的值。
<input type="text" id="form">
var input = document.getElementById('form');
input.value;
2、checked
用于单选框和复选框的是否“勾上了”的选项,value属性返回的永远是HTML预设的值。
// html:
<label><input type="radio" name="relate" id="first">选择一</label>
<label><input type="radio" name="relate" id="second"> 选择二</label>
<div id="cli">点击</div>
// js:
var first = document.getElementById('first');
var second = document.getElementById('second');
var cli = document.getElementById('cli');
cli.onclick = function () {
console.log(first.checked); // true or false
console.log(second.checked); // true or false
}
没有name属性的的数据不会被提交。