JavaScript规范总结
1、全局命名空间污染与IIFE
总是将代码包裹成一个 IIFE(Immediately-Invoked Function Expression),用以创建独立隔绝的定义域。这一举措可防止全局命名空间被污染。IIFE 还可确保你的代码不会轻易被其它全局命名空间里的代码所修改。
不推荐
var x = 10,
y = 100;
console.log(window.x + '' + window.y);
推荐
(function(log, w, undefined){
'use strict';
var x = 10,
y = 100;
log((w.x === undefined) + '' + (w.y === undefined));
}(window.console.log, window));
2、IIFE(立即执行的函数表达式)
立即执行的函数表达式的执行括号应该写在外包括号内。虽然写在内还是写在外都是有效的,但写在内使得整个表达式看起来更像一个整体,因此推荐这么做。
不推荐
(function() {})();
推荐
(function() {}());
可以用下面写法来格式化IIFE代码:
(function() {
'use strict';
//code goes here
} ());
如果想要引用全局变量或者外层IIFE的变量,可以通过下列方式传参:
(function(){
'use strict';
$(function(){
w.alert(d.querySelectorAll('div').length);
});
} (jQeury, window, document));
3、严格模式
ECMAScript 5 严格模式可在整个脚本或独个方法内被激活。它对应不同的 JavaScript 语境会做更加严格的错误检查。严格模式也确保了 JavaScript 代码更加的健壮,运行的也更加快速。严格模式会阻止使用在未来很可能被引入的预留关键字。
不推荐
//Script starts here
'use strict';
(function(){
//your code starts here
}());
推荐
(function(){
'use strict';
//your code starts here
}());
4、变量声明
总是使用var
来声明变量。如果不指定var,变量将会被隐式地声明为全局变量,如果没有声明,变量处于什么定义域就变得不清楚。
推荐
var x = 10,
y = 100;
5、理解Javascript的定义域和定义域提升
在 JavaScript 中变量和方法定义会自动提升到执行之前。JavaScript 只有 function 级的定义域,而无其他很多编程语言中的块定义域,所以使得你在某一 function 内的某语句和循环体中定义了一个变量,此变量可作用于整个 function 内,而不仅仅是在此语句或循环体中,因为它们的声明被 JavaScript 自动提升了。
(function(log){
'use strict';
var a = 10;
for (var i = 0; i < a; i++) {
var b = i * i ;
log(b);
}
if (a === 10) {
var f = function (){
log(a);
};
f();
}
function x() {
log('Mr. X!');
}
x();
}(window.console.log));
被JS提升过后
(function(log){
'use strict';
/* All variables used in the closure will be hoisted to the top of the function */
var a, i, b, f;
// All functions in the closure will be hoisted to the top
function x() {
log('Mr. X!');
}
a = 10;
for (i = 0; i < a; i ++) {
b = i * i;
log(b);
}
if (a === 10) {
f = function () {
log (a);
};
f();
}
x();
}(window.console.log));
有效代码
(function(log){
'use strict';
var a = 10;
i = 5;
x();
for(var i; i < a; i++) {
log(b);
var b = i * i;
}
if (a === 10) {
f = function () {
log (a);
};
f();
var f;
}
function x() {
log ('Mr. X!');
}
}(window.cosole.log));
6、提升声明
为避免上述的变量和方法定义被自动提升造成误解,把风险降到最低,我们应该手动地显示地去声明变量与方法。也就是说,所有的变量以及方法,应当定义在 function 内的首行。
只用一个var
关键字声明,多个变量用逗号隔开。
不推荐
(function(log) {
'use strict';
var a = 10;
var b = 10;
for (var i = 0; i < 10; i++) {
var c = a * b * i;
}
function f() {
}
var d = 100;
var x = function() {
return d * d;
};
log(x());
}(window.console.log));
推荐
(function(log) {
'use strict';
var a = 10,
b = 10,
d = 100,
i,
c,
x;
function f() {
}
for(i = 0; i < 10; i ++) {
c = a * b * i;
}
x = function() {
return d * d;
};
log(x());
}(window.console.log));
7、总是使用带类型判断的比较===
总是使用===
精确的比较操作符,避免在判断的过程中,由JavaScript的强制类型转换造成的困扰,===
操作符必须是同一类型为前提的条件下才会相等。
(function(log){
'use strict';
log('0' == 0); // true
log('' == false); // true
log('1' == true); // true
log(null == undefined); // true
var x = {
valueOf: function() {
return 'X';
}
};
log(x == 'X');
}(window.console.log));
8、变量赋值时的逻辑操作
不推荐
if(!x) {
if(!y) {
x = 1;
} else {
x = y;
}
}
推荐
x = x || y || 1;
这个技巧可以用来给方法设定默认的参数
(function(log) {
'use strict';
function multiply(a, b) {
a = a || 1;
b = b || 1;
log('Result' + a * b);
}
multiply(); // Result 1
multiply(10); //Result 10
multiply(3, NaN); //Result 3
multiply(9, 5); //Result 45
}(window.console.log));
9、注意分号
var foo = function() {
return true;
}; //函数表达式有分号
function foo() {
return true;
} //函数没有分号
10、语句块内的函数声明
切勿在语句块内声明函数,在 ECMAScript 5 的严格模式下,这是不合法的。
函数声明应该在定义域的顶层。但在语句块内可将函数申明转化为函数表达式赋值给变量。
不推荐
if (x) {
function foo() {}
}
推荐
if (x) {
var foo = function() {};
}
11、异常
在复杂的环境中,你可以考虑抛出对象而不仅仅是字符串(默认的抛出值)。
if(name === undefined) {
throw {
name: 'System Error',
message: 'A name should always be specified!'
}
}
12、简易的原型继承
(function(log) {
'use strict';
function Apple(name) {
this.name = name;
}
//defining a method of apple
Apple.prototype.eat = function() {
log ('Eating' + this.name);
};
//Constructor function
function GrannySmithApple() {
Apple.prototype.constructor.call(this, 'Granny Smith');
}
// Set parent prototype while creating a copy with Object.create
GrannySmithApple.prototype = Object.create(Apple.prototype);
GrannySmithApple.prototype.constructor = GrannySmithApple;
GrannySmithApple.prototype.eat = function() {
Apple.prototype.eat.call(this);
log('Poor Grany Smith');
};
var apple = new Apple('Test Apple');
var grannyApple = new GrannySmithApple();
log(apple.name); //Test Apple
log(grannyApple.name); //Granny Smith
//判断apple是否是Apple的实例
log(apple instanceof Apple); //true
log(apple instanceof GrannySmithApple); //false
log(grannyApple instanceof Apple);//true
log(grannyApple instanceof GrannySmithApple);//true
grannyApple.eat(); //Eating Granny Smith\nPoor Granny Smith
}(window.console.log));
13、切勿在循环中创建函数
在简单的循环语句中加入函数是非常容易形成闭包而带来隐患的。
不推荐
(function(log, w) {
var numbers = [1, 2, 3],
i;
for(i = 0; i < numbers.length; i++) {
w.setTimeout(function() {
w.alert('Index ' + i + ' with number ' + numbers[i]);
}, 0);
}
}(window.console.log, window));
不完全推荐
(function(log, w) {
var numbers = [1, 2, 3],
i;
function alertIndexWithNumber(index, number) {
return function() {
w.alert('Index ' + index + ' with number ' + number);
};
}
for(i = 0; i < numbers.length; i++) {
w.setTimeout(alertIndexWithNumber(i, number[i]), 0);
}
}(window.console.log, window));
推荐
(function(log, w) {
'use strict';
var numbers = [1, 2, 3],
i;
numbers.forEach(function(number, index) {
w.setTimeout(function() {
w.alert('Index' + index + 'with number' + number);
}, 0);
});
}(window.console.log, window));
14、函数式编程
例如:reduce()方法是ES5新增的数组逐项处理方法,接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终为一个值。
arr.reduce(callback[ ,intialValue])
参数:
- callback(一个在数组中每一项上调用的函数,接受四个函数:)
- previousValue(上一次调用回调函数时的返回值,或者初始值)
- currentValue(当前正在处理的数组元素)
- currentIndex(当前正在处理的数组元素下标)
- array(调用reduce()方法的数组)
- initialValue(可选的初始值。作为第一次调用回调函数时传给previousValue的值)
//forEach方法求和
var arr = [1, 2, 3, 4],
sum = 0;
arr.forEach(function(e){sum += e;});
//map实现
var arr = [1, 2, 3, 4],
sum = 0;
arr.map(function(obj){sum += obj}); //return undefined array
//reduce实现
var arr = [1, 2, 3, 4],
arr.reduce(function(pre,cur){return pre + cur}); //return 10
另一个例子通过某一规则对一个数组进行过滤匹配来创建一个新的数组。
不推荐
(function(log){
'use strict';
var numbers = [11, 3, 7, 9, 100, 20, 14, 10],
numbersGreaterTen = [],
i;
for(i = 0; i < numbers.length; i++) { if(numbers[i] > 10) {
numbersGreaterTen.push(numbers[i]);
}
}
log('From the list of numbers ' + numbers + ' only ' + numbersGreaterTen + ' are greater than ten');
}(window.console.log));
推荐
(function(log){
'use strict';
var numbers = [11, 3, 7, 9, 100, 20, 14, 10];
var numbersGrearterTen = numbers.filter(function(element) {
return element > 10;
});
log('From the list of numbers ' + numbers + ' only ' + numbersGreaterTen + ' are greater than ten');
}(window.console.log));
15、构造对象和数组
推荐
//构造数组
var a = [x1, x2, x3];
var a2 = [x1, x2];
var a3 = [x1];
var a4 = [];
//构造对象
var o = {};
var o2 = {
a: 0,
b: 1,
c: 2,
'strange key': 3
};
16、三元条件判断
return x ==== 10 ? 'valid' : 'invalid';