原文: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确保分号不会忘记。除此以外,应该常常放置大括号在相应语句的同一行,避免出现意想不到的错误。