这里输入demo描述,引用代码段 if (src.indexOf('/'+ cPathArr[0] +'/') !== -1)
,对关键词语加粗,引用链接
if (!window.console) {
var names = "log debug info warn error assert dir dirxml group groupEnd time timeEnd count trace profile profileEnd".split(" ");
window.console = {};
for (var i = 0; i < names.length; ++i) window.console[names[i]] = function() {}
};
基础优化技巧
function eventHandler(e) {
//if (!e) e = window.event;
e = e || window.event;
}
if (myobj) {
doSomething(myobj);
}
可以替换为
myobj && doSomething(myobj);
// 多次操作在一个节点或者节点下子节点,对公共节点进行缓存
var myDiv = document.getElementById("myDiv");
myDiv.style.left = myDiv.offsetLeft + myDiv.offsetWidth + "px";
// for语句缓存终止条件
for(var i = 0, len = arr.length; i < len; i++){
var item = arr[i];
...
}
// 使用容器存放临时变更,最后再一次性更新DOM
list.style.display = "none"; //先隐藏,因为隐藏的节点不会触发重排
var fragment = document.createDocumentFragment(); //创建空片段容器
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
fragment.appendChild(item); //每一次for最后把节点放入空片段容器
}
list.appendChild(fragment); //最后在页面插入容器
list.style.display = ""; //显示节点
// 遇到多次使用全局变量,先进行缓存
function search() {
var location = window.location;
location.foo();
location.foo();
}
// 类型转换
var int = 5,
str = int + '', //数字转字符
int = str * 1; //字符转数字
// 往对象中插入新属性
var object = {name = 'franks', sex = 'man'}
object['age'] = '32';
//将条件分支,按可能性顺序从高到低排列,可以减少解释器对条件的探测次数.
//在同一条件子的多(>2)条件分支时,使用switch优于if:switch分支选择的效率高于if,在IE下尤为明显。4分支的测试,IE下switch的执行时间约为if的一半。
// 三目运算取代
var num = a > b ? a : b ;
函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用记录上方,还会形成一个B的调用记录。等到B运行结束,将结果返回到A,B的调用记录才会消失。如果函数B内部还调用函数C,那就还有一个C的调用记录栈,以此类推。所有的调用记录,就形成一个"调用栈"(call stack)。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用记录,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就可以了。
function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();
// 等同于
function f() {
return g(3);
}
f();
// 等同于
g(3);
上面代码中,如果函数g不是尾调用,函数f就需要保存内部变量m和n的值、g的调用位置等信息。但由于调用g之后,函数f就结束了,所以执行到最后一步,完全可以删除 f() 的调用记录,只保留 g(3) 的调用记录。
递归非常耗费内存,因为需要同时保存成千上百个调用记录,很容易发生"栈溢出"错误(stack overflow)。但对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误。
function tailFactorial(n, total) {
if (n === 1) return total;
return tailFactorial(n - 1, n * total);
}
function factorial(n) {
return tailFactorial(n, 1);
}
factorial(5) // 120
ES6函数默认值方法:
function factorial(n, total = 1) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5) // 120
function elClickHandler() {
……
}
function init() {
var el = document.getElementById('MyElement');
el.onclick = elClickHandler; //上下文
}
init();
或
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {
……
}
el = null; // 对象设为空,事件不再执行
}
init();
function foo() {
var val = 'hello';
function bar() {
function baz() {
global.val = 'world;'
}
baz();
console.log(val); //=> hello
}
bar();
}
foo();
baz()函数的执行在全局作用域中定义了一个全局变量val。而在bar()函数中,对val这一标识符进行访问时,按照从内到外厄德查找原则:在bar函数的作用域中没有找到,便到上一层,即foo()函数的作用域中查找。本次标识符访问在foo()函数的作用域中找到了符合的变量,便不会继续向外查找,故在baz()函数中定义的全局变量val并没有在本次变量访问中产生影响。
闭包就是能够读取其他函数内部变量的函数
function foo() {
var local = 'Hello';
function foo2(){
return local;
}
return foo2;
}
var result = foo();
console.log(result()); //=> Hello
闭包可以用在许多地方。它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
function foo(){
var n = 999;
nAdd = function(){ n += 1} //定义一个全局变量,使用了函数内的局部变量
function foo2(){
alert(n); //返回上级函数变量
}
return foo2; //返回下级函数
}
var result=foo();
result(); //=> 返回变量n默认值999
nAdd(); // 执行了一次n+1, n值变为1000
result(); //=> 返回变量n的值1000
在这段代码中,result实际上就是闭包foo2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数foo中的局部变量n一直保存在内存中,并没有在foo调用后被自动清除。
为什么会这样呢?原因就在于foo是foo2的父函数,而foo2被赋给了一个全局变量,这导致foo2始终在内存中,而foo2的存在依赖于foo,因此foo也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
var uniqueInteger = (function() {
var counter = 0;
return function() {
return counter++;
}
}());
uniqueInteger(); //=>0
uniqueInteger(); //=>1
uniqueInteger(); //=>2
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype = {
getName: function() {
return this.name;
},
getMessage: function() {
return this.message;
}
};
var newobj1=MyObject('x','hello');
var newobj2=MyObject('y','javascript');
newobj1.getName()==newobj2.getName();//true
或
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype.getName = function() {
return this.name;
};
MyObject.prototype.getMessage = function() {
return this.message;
};
<\p id="help">Helpful notes will appear here\p>
<\p>E-mail: <\input type="text" id="email" name="email">\input>\p>
<\p>Name: <\input type="text" id="name" name="name">\input>\p>
<\p>Age: <\input type="text" id="age" name="age">\input>\p>
unction showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function makeHelpCallback(help) {
return function() {
showHelp(help);
};
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus =
makeHelpCallback(item.help);
}
}
setupHelp();
有六个按钮,分别对应六种事件,当用户点击按钮时,在指定的地方输出相应的事件。
var btns = document.querySelectorAll('.btn'); // 6 elements
var output = document.querySelector('#output');
var events = [1, 2, 3, 4, 5, 6];
// Case 1
for (var i = 0, l = btns.length; i < l; i++) {
btns[i].onclick = function(evt) {
output.innerText += 'Clicked ' + events[i];
};
}
// Case 2
for (var i = 0, l = btns.length; i < l; i++) {
btns[i].onclick = (function(index) {
return function(evt) {
output.innerText += 'Clicked ' + events[index];
};
})(i);
}
// Case 3
for (var i = 0, l = btns.length; i < l; i++) {
btns[i].onclick = (function(event) {
return function(evt) {
output.innerText += 'Clicked ' + event;
};
})(events[i]);
}
这里第一个解决方案显然是典型的循环绑定事件错误,这里不细说,详细可以参照我给一个网友的回答;而第二和第三个方案的区别就在于闭包传入的参数。
第二个方案传入的参数是当前循环下标,而后者是直接传入相应的事件对象。事实上,后者更适合在大量数据应用的时候,因为在JavaScript的函数式编程中,函数调用时传入的参数是基本类型对象,那么在函数体内得到的形参会是一个复制值,这样这个值就被当作一个局部变量定义在函数体的作用域内,在完成事件绑定之后就可以对events变量进行手工解除引用,以减轻外层作用域中的内存占用了。而且当某个元素被删除时,相应的事件监听函数、事件对象、闭包函数也随之被销毁回收。
function square(input) {
var output;
eval('output=(input * input)');
return output;
}
eval 方法计算平方值并输出结果,但性能不好。此例使用字符串 output=(input*input) 作为 eval 方法的参数,无法利用 JavaScript 预编译。
function square(input) {
var output;
eval(new function() { output=(input * input)});
return output;
}
使用函数代替字符串作参数确保新方法中的代码能被 JavaScript 编译器优化。