【譯】避免這些常見的JavaScript錯誤

yuyurenjie發表於2017-07-29

原文:Avoid These Common JavaScript Mistakes
作者:JP Sio

在今天,JavaScript是最流行的程式語言之一,如果你希望鑽研JavaScript,這裡有幾個需要避免的問題

1.使用==而不是===

在剛開始學習JavaScript時,這是初學者最容易犯的錯誤。==會將型別轉換,而===卻不會。

// ==的例子

1 == "1" // true
"/t" == 0 // true
"34" == 2 // false
new Number(10) == 10 // true
Number(10) === 10 //true

//  ===的例子

1 === "1" // false
"/t" === 0 // false
"34" === 2 // false
10 === 10 //true

// where things get wierd....

Number(10) === 10 //true
new Number(10) === 10 //false, LHS will actually be an object!複製程式碼

通常,應該使用嚴格相等操作符===,這樣具有可預測性,查詢bug時候不會出現不必要的問題。

2.使用typeof

如果變數被定義了,你應該只使用typeof去檢查,否則,會出現不一致的行為。

console.log(typeof "foo" === "string"); //true 
console.log(typeof String("foo") === "string"); // true
console.log(typeof new String("foo") === "string"); // false,因為new操作產生的都是物件。
console.log(typeof 1.2 === "number"); //true
console.log(typeof new Date() === "Date"); //false ,Date是物件
console.log(typeof [1,2,3] === "array"); //false, 陣列也是物件

/** 正確檢測物件型別方法 **/

function is(type, obj) {
    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    return obj !== undefined && obj !== null && clas === type;
}

console.log(is('String', 'test')); // true
console.log(is('String', new String('test'))); // true
console.log(is('Date', new Date())); // true複製程式碼

正如你所看見的,替代方法可以處理更加常見例子,而且更加靈活。

3.類中不正確使用this

這可能是大家從Java轉向學習JavaScript普遍頭疼的問題。在Java中,this指向當時的物件,但在JavaScript事實並非如此。事實上,this有5種不同含義

// 1
console.log(this); //指向全域性物件也就是window
// 包含了prompt alert confirm方法...

// 2
function test() {
  console.log(this);
  this.method = function inner() {console.log(this)};
}; // 這裡this也是指向全域性物件
test();

//3
var obj = new test(); // 因為用的是構造器,this指向test物件

//4
obj.method(); //方法迫使this指向物件

// 5:使用call或apply迫使this指向明確的值
function foo(a, b, c) {
  this.a = a;
  this.b = b;
  this.c = c;
}

var bar = {};
foo.apply(bar, [1, 2, 3]); //第一個引數為this
console.log(bar) // 會輸出 a: 1, b: 2, c: 3

foo.call(bar, 4, 5, 6); //同樣的效果
console.log(bar) //也會輸出a: 4, b: 5, c: 6複製程式碼

這5個例子容易理解,然而第二個例子被認為是設計缺陷,因為會導致下面問題:

function test() {
  this.arr = [1,2,4];
  this.message = "I am here";
  this.fun = function() {
      this.arr.forEach(function(item) {
        console.log(this.message); //會輸出3次undefined,因為this指向全域性物件
    });
  }
}

var t = new test();
t.fun();

//上面例子中,this不會指向test物件,而指向全域性物件

//為了避免這種情況,可以使用變數儲存器this

//雖然this仍指向全域性物件,但是可以使用替代變數

function test2() {
  var self = this;
  self.arr = [1,2,4];
  self.message = "I am here";
    self.fun = function() {
      this.arr.forEach(function(item) {
        console.log(self.message); //會輸出I am here 3次
    });
  }
}


var t2 = new test2();
t2.fun();複製程式碼

4.不使用匿名包裝

JavaScript只有函式作用域,而且所有物件都分享在一個全域性名稱空間下,在大的專案中,這會帶來很大的問題。

var foo = 12;

function changeFoo() {
  foo = 34; //改變的是全域性作用域而不是區域性作用域
}

changeFoo();

console.log(foo);//輸出34

// 在下個例子顯然會出現問題

// Out here is the global scope

for(var i = 0; i < 10; i++) {
    innerLoop();
}

function innerLoop() {
    // 這是個不同的作用域
    for(i = 0; i < 10; i++) { // 缺少var語句,i指向全域性作用域
           console.log("this is will show 10 times and not 100.");//只會輸出10次
    }
}複製程式碼

為了避免這樣問題,可以使用所謂的匿名包裝器。實際上就是立即執行函式。

不止他們能避免命名衝突,而且也能幫助你更好的組織你的程式碼。

(
  // 將函式寫在圓括號中
  function(){}
  // 返回函式物件
)() // 立即呼叫

// 也可以使用下面同樣函式效果
!function(){}()
+function(){}()
(function(){}());

// 重要提醒
// 如果像下面這樣做,會重寫全域性物件 undefined

console.log(typeof undefined === "undefined") // true
var undefined = 123;
console.log(typeof undefined === "undefined") // this is false! undefined is overwritten and replaced by the number 123

// 可以通過匿名包裝

(function(undefined){console.log(undefined);})();複製程式碼

5.通過物件的鍵不正確迭代

有幾種方法迭代物件的屬性。可以使用Object.keys、Object.entriees或者for迴圈


// 給全域性物件增加一個屬性,所有物件都會繼承這個物件,
Object.prototype.WTF = "this should not be in your object";

function a() {
  this.unwanted = 10;
  this.crap = "dhjbjbfdjbf";
}

function child() {
  this.a = 1;
  this.b = 2;
}

//child會繼承自a
child.prototype = new a();

var obj = new child();

for (var property in obj) { //此方法不僅比Object.keys慢,而且還包含父屬性
    console.log(property + ": " + obj[property]);
}

for (var property in obj) { //這會返回適合的鍵,但是仍比Object.keys慢 
    if(obj.hasOwnProperty(property))
        console.log(property + ": " + obj[property]);
}

Object.keys(obj).map((e) => console.log(`key=${e}  value=${obj[e]}`)); // 最佳的方式
Object.entries(obj).forEach(([key, value]) => console.log(`key=${key} value=${value}`)); // 這也是不錯的方法複製程式碼

6.省略分號

如果忘寫分號,JavaScript會自動新增。但是這樣會弄亂你的程式碼並造成錯誤,這裡有兩個著名的例子:

/**
  這裡編譯器會在return後加分號,造成函式返回undefined
**/
function test(){
  var name = "Hello";
  return // 這裡會加分號
  {
    name: name
  }
}

/**
  這個例子更奇怪,由於大括號,所以不會加分號,最終會顯示型別錯誤,因為編譯器會認為console.log()是函式,而 (someList || []) 是引數
**/

function test2(){
  var someList = undefined

  console.log("Hi!") // does not add it here!
  (someList || []).map((item) => item) 
}複製程式碼

你應該使用linter確保分號不會忘記。除此之外,應該經常放置大括號在相應語句的同一行,避免出現意想不到的錯誤。


歡迎訂閱掘金專欄知乎專欄,關注個人部落格

相關文章