对于代码好坏应该多维度去看待(易维护性、效率等,不能只看执行速度)
1. 循环添加事件
在这样循环添加事件的情况下是添加失败的 i会输出相同的值
for(var i = 0;i < len; i++){
button[i].onclick = function() {
console.log(i)
}
}
实际上是因为闭包的原因,因为for循环只会申请一片空间进行使用,而function中的i引用了外部的i,在循环完成后i值为3,此时的函数如果触发那么函数上下文的AO中没有i这个值,就会向外部找,打个比方 在EC(G)中找到了i,然后使用了这个i;而不会去管是谁去触发这个事件的;
解决方案有很多,可以添加自执行函数:
for(var i = 0;i < len; i++){
(function(i){
button[i].onclick = function() {
console.log(i)
}
})(i)
}
for(var i = 0;i < len; i++){
button[i].onclick = (function(i) {
return function() {
console.log(i)
}
})(i)
}
这样使用自执行函数,会额外生成多个函数作用域去执行,每次函数执行时都能通过自身函数作用域中的AO或父级作用域链中父级的执行上下文中的AO去获取i,而不用去EC(G)中访问i;缺点是:因为最后onclick绑定的函数是在匿名函数上下文生成的,所以这些匿名函数的上下文无法被释放,这样做消耗了更多的空间;
除此之外也可以使用let关键字来声明i,let同样会生成块级作用域,每个块级作用域中都会使用各自的i;同样的因为用到了闭包的思想 所以同样会占用更多的空间
在其他就是添加自定义属性:每次循环时给item添加一个自定义属性,执行方法时使用自身的属性;(优点:相比使用闭包的思想减少了空间的占用)
事件委托: 没有使用闭包,也没有添加自定义的字段,相比来说是最好的
2. 变量局部化 和 减少访问层级
提高代码执行效率(减少了作用域链的查找路径)
3. 缓存数据
对于多次使用,但对值的实时要求低的情况可以缓存 减少作用域链或其他方式查找的消耗;
4. 防抖、节流
滚动事件、输入的模糊查询、点击操作
浏览器默认会有自己的监听事件间隔(4-6ms),上述事件不需要这么频繁的触发造成了资源浪费;
防抖:对于这些高频操作 只希望识别一次点击 可以第一次或最后一次;
节流:对于高频操作,可以自己设置频率,让高频的触发按照定义的频率触发;
const myDebounce = function (handle, immediate, wait) {
if (typeof handle !== "function")
return new Error("handle must be a function");
if (typeof immediate === "undefined") immediate = false;
if (typeof wait !== "number") wait = 300;
let timer = null;
return function (...args) {
let self = this;
clearTimeout(timer);
(immediate && !timer) ? handle.call(self, ...args) : 1;
timer = setTimeout(() => {
timer = null;
immediate || handle.call(self, ...args);
}, wait);
};
};
function myThrottle(handle, wait) {
if (typeof handle !== "function")
throw new Error("handle must be an function");
if (typeof wait === "undefined") wait = 400;
let previous = 0;
let timer = null;
return function (...args) {
let self = this;
let now = new Date();
let interval = wait - (now - previous);
if (interval <= 0) {
clearTimeout(timer);
timer = null;
// 这里清楚定时器 主要是防止触发事件同一时刻 之前settimeout的时间也到了 导致重复调用回调函数
// 距离上次触发超过了wait 执行
handle.call(self, ...args);
previous = new Date();
} else if (!timer) {
timer = setTimeout(() => {
timer = null;
handle.call(self, ...args);
previous = new Date();
}, interval);
}
};
}
5. 减少判断层级
不满足条件时提前return 减少多层判断的嵌套
6. 减少循环体内活动
减少循环体内公用的数据或代码(如length的获取可以提到前面)
7. 字面量和构造式
对于引用类型,采用构造方式创建和字面量创建({}[]...)速度相差不大 构造稍慢一些;
对于基本数据类型 采用构造方式创建 比 字面量创建速度慢很多
字面量方式创建的变量 在调用方法时 会隐式将其转为一个对象去调用它原型链上的方法
字面量形式创建的string会比构造出来的快