原文:Avoid These Common JavaScript Mistakes
做者:JP Siojavascript
在今天,JavaScript是最流行的編程語言之一,若是你但願鑽研JavaScript,這裏有幾個須要避免的問題java
在剛開始學習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時候不會出現沒必要要的問題。數組
若是變量被定義了,你應該只使用typeof去檢查,不然,會出現不一致的行爲。app
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複製代碼
正如你所看見的,替代方法能夠處理更加常見例子,並且更加靈活。編程語言
這多是你們從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個例子容易理解,然而第二個例子被認爲是設計缺陷,由於會致使下面問題:oop
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();複製代碼
JavaScript只有函數做用域,並且全部對象都分享在一個全局命名空間下,在大的項目中,這會帶來很大的問題。post
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);})();複製代碼
有幾種方法迭代對象的屬性。可使用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}`)); // 這也是不錯的方法複製代碼
若是忘寫分號,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確保分號不會忘記。除此以外,應該常常放置大括號在相應語句的同一行,避免出現意想不到的錯誤。