JavaScript一些规范

180 阅读5分钟

隐式全局变量和显式全局变量的区别

隐式的全局变量和显式定义的全局变量之间有着细微的差别,差别在于通过delete来删除它们的时候表现不一致。

  • 通过var创建的全局变量(在任何函数体之外创建的变量)不能被删除。
  • 没有用var创建的隐式全局变量(不考虑函数内的情况)可以被删除。 也就是说,隐式全局变量并不算是真正的变量,但他们是全局对象的属性成员。属性是可以通过delete运算符删除的,而变量不可以被删除。

枚举man对象的实例属性

另外一种的写法是通过Object.prototype 直接调用hasOwnProperty()方法,如:

for (var i in man) {
	if(Object.prototype.hasOwnProperty.call(man,i)){
		console.log(i, ":", man[i]);
	}
}
for (var i in man) {
    console.log(i, ":", man[i]);
}
var i,
    hasOwn = Object.prototype.hasOwnProperty;
for (i in man) {
    if (hasOwn.call(man, i)) { // filter
        console.log(i, ":", man[i]);
    }
}

eval()

new Function()的用法和eval()非常类似,应当特别注意。这种构造函数的方式很强大,但往往被误用。
如果你不得不使用eval(),你可以尝试用new Function()来代替。这有一个潜在的好处,在new Function()中运行的代码会在一个局部函数作用域内执行,因此源码中所有用var定义的变量不会自动变成全局变量。还有一种方法可以避免eval()中定义的变量转换为全局变量,即是将eval()包装在一个立即执行的匿名函数内。

console.log(typeof un);// "undefined"
console.log(typeof deux); // "undefined"
console.log(typeof trois); // "undefined"
var jsstring = "var un = 1; console.log(un);";
eval(jsstring); // logs "1"
jsstring = "var deux = 2; console.log(deux);";
new Function(jsstring)(); // logs "2"
jsstring = "var trois = 3; console.log(trois);";
(function () {
    eval(jsstring);
}()); // logs "3"
console.log(typeof un); // "number"
console.log(typeof deux); // "undefined"
console.log(typeof trois); // "undefined"

eval()和Function构造函数还有一个区别,就是eval()可以修改作用域链,而Function更像是一个沙箱。不管在什么地方执行Function,它只能看到全局作用域。因此它不会太严重的污染局部变量。在下面的示例代码中,eval()可以访问且修改其作用域之外的变量,而Function不能(注意,使用Function和new Function是完全一样的)。

(function () {
    var local = 1;
    eval("local = 3; console.log(local)"); // logs 3
    console.log(local); // logs 3
}());
(function () {
    var local = 1;
    Function("console.log(typeof local);")(); // logs undefined
}());

空格

适合使用空格的地方包括:

  • for循环中的分号之后,比如 for (var i = 0; i < 10; i += 1) {...}
  • for循环中初始化多个变量,比如 for (var i = 0, max = 10; i < max; i += 1) {...}
  • 分隔数组项的逗号之后,var a = [1, 2, 3];
  • 对象属性后的逗号以及名值对之间的冒号之后,var o = {a: 1, b: 2};
  • 函数参数中,myFunc(a, b, c)
  • 函数声明的花括号之前,function myFunc() {}
  • 匿名函数表达式function之后,var myFunc = function () {};
  • 在运算符和操作数之间添加空格。也就是说在+, -, *, =, <, >, <=, >=, ===, !==, &&, ||, +=符号前后都添加空格。

new()

当你通过关键字new来调用这个构造函数时,函数体内将发生这些事情:

  • 创建一个空对象,将它的引用赋给this,继承函数的原型。
  • 通过this将属性和方法添加至这个对象
  • 最后返回this指向的新对象(如果没有手动返回其他的对象)

对象

  1. 对象:使用直接量创建对象
// bad
var item = new Object();
// good
var item = {};
  1. 不要使用保留字作为键名
  2. 使用同义词替换需要使用的保留字。

数组

  1. 使用直接量创建数组。
// bad
var items = new Array();
// good
var items = [];
  1. 向数组增加元素时使用 Array#push 来替代直接赋值。
var someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
  1. 当需要拷贝数组时,使用 Array#slice。
var len = items.length;
var itemsCopy = [];
var i;
// bad
for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}
// good
itemsCopy = items.slice();
  1. 使用 Array#slice 将类数组对象转换成数组
function trigger(){
  var args=Array.prototype.slice.call(arguments);
}

字符串

  1. 使用单引号 '' 包裹字符串。
// bad
var name = "Bob Parr";
// good
var name = 'Bob Parr';
// bad
var fullName = "Bob " + this.lastName;
// good
var fullName = 'Bob ' + this.lastName;
  1. 超过 100 个字符的字符串应该使用连接符写成多行。 注:若过度使用,通过连接符连接的长字符串可能会影响性能。
  2. 程序化生成的字符串使用 Array#join 连接而不是使用连接符。
var items;
var messages;
var length;
var i;
messages = [{
  state: 'success',
  message: 'This one worked.'
  }, {
  state: 'success',
  message: 'This one worked as well.'
  }, {
  state: 'error',
  message: 'This one did not work.'
  }];
length = messages.length;

// bad
function inbox(messages) {
  items = '<ul>';
  for (i = 0; i < length; i++) {
     items += '<li>' + messages[i].message + '</li>';
  }
  return items + '</ul>';
}

// good
function inbox(messages) {
  items = [];
  for (i = 0; i < length; i++) {
  // use direct assignment in this case because we're micro-optimizing.
   items[i] = '<li>' + messages[i].message + '</li>';
  }
  return '<ul>' + items.join('') + '</ul>';
}

函数

  1. 函数表达式:
// 匿名函数表达式
var anonymous = function() {
  return true;
};
// 命名函数表达式
var named = function named() {
  return true;
};
// 立即调用的函数表达式(IIFE)
(function () {
  console.log('Welcome to the Internet. Please follow me.');
}());
  1. 不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。
// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}
// good
var test;
if (currentUser) {
  test = function test() {
  console.log('Yup.');
  };
}
  1. 不要把参数命名为 arguments。这将取代函数作用域内的 arguments 对象。
// bad
function nope(name, options, arguments) {
  // ...stuff...
}
// good
function yup(name, options, args) {
  // ...stuff...
}

属性

  1. 使用 . 来访问对象的属性。
  2. 当通过变量访问属性时使用中括号 []。
var luke = {
  jedi: true,
  age: 28
};
function getProp(prop) {
  return luke[prop];
}
var isJedi = getProp('jedi');

变量

  1. 总是使用 var 来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。
  2. 使用 var 声明每一个变量。这样做的好处是增加新变量将变的更加容易,而且永远不用再担心调换错 ; ,
  3. 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。
// good
var items = getItems();
var goSportsTeam = true;
var dragonball;
var length;
var i;
  1. 在作用域顶部声明变量。这将帮你避免变量声明提升相关的问题。

提升

见变量提升篇章

注释规则

/**
* make() returns a new element 函数说明
* based on the passed in tag name
*
* @param {String} tag 参数说明
* @return {Element} element 返回值说明
*/

类型转换

  1. 在语句开始时执行类型转换。
//  => this.reviewScore = 9;
// bad
var totalScore = this.reviewScore + '';
// good
var totalScore = '' + this.reviewScore;
// bad
var totalScore = '' + this.reviewScore + ' total score';
// good
var totalScore = this.reviewScore + ' total score';
  1. 使用 parseInt 转换数字时总是带上类型转换的基数。
var val = parseInt(inputValue, 10);

布尔

var age = 0;
// bad
var hasAge = new Boolean(age);
// good
var hasAge = Boolean(age);
// good
var hasAge = !!age;

事件

当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法:

// bad
$(this).trigger('listingUpdated', listing.id);
...
$(this).on('listingUpdated', function (e, listingId) {
// do something with listingId
});

更好的写法:

// good
$(this).trigger(‘listingUpdated’, { listingId : listing.id });
…
$(this).on(‘listingUpdated’, function (e, data) {
// do something with data.listingId
});