es6改進es5中的一些坑
阿新 • • 發佈:2019-02-13
1. 塊級作用域
es5中有一個比較坑的地方就是變數提升(variable hoisting),variable hoisting的根本原因就是es5中沒有塊級作用域。看一下下面的程式碼:
var a = 10;
var b = 20;
function add(flag) {
if (flag) {
var b = 10;
return a + b;
}
return a + b;
}
console.log(add());// NaN
console.log(add(true));// 20
注意,為什麼add()
沒有輸出結果呢?因為變數提升。上面程式碼中,var b = 10
if
中,由於es5沒有塊級作用域(函式體是es5中僅有的作用域塊),所以變數b相當於在函式內部重新聲明瞭一次,類似下面程式碼:
var a = 10;
var b = 20;
function add(flag) {
var b; // undefined
if (flag) {
b = 10;
return a + b;
}
return a + b; // if flag flase, then b is undefined
}
console.log(add());
console.log(add(true));
使用es6中的let
var a = 10;
var b = 20;
function add(flag) {
if (flag) {
let b = 10;
return a + b;
}
return a + b;
}
console.log(add());// 30
console.log(add(true));// 20
值得一提的是,在for迴圈中使用var來定義變數,也會遇到變數提升的坑:
var arr = [];
for (var i = 0; i < 3; i++) {// i 是全域性變數
arr.push(function () {
return i;
});
}
console.log(i); // 3, 由此可見i為全域性變數
// arr中保持的三個函式的返回值均是全域性變數i
for (var j = 0; j < 3; j++) {
console.log(arr[j]()); // 3 3 and 3
}
如果不使用es6中的let
,需要使用javascript中的閉包來救火了:
var arr = [];
for (var i = 0; i < 3; i++) {
(function (i) {
arr.push(function () {
return i; // 區域性變數
});
})(i);
}
console.log(i);
for (var j = 0; j < 3; j++) {
console.log(arr[j]()); // return 0, 1, 2
}
使用es6,在for
迴圈中使用let
定義變數:
var arr = [];
for (let i = 0; i < 3; i++) {
arr.push(function () {
return i; // 區域性變數
});
}
console.log(i); // error, 在全域性中找不到變數i
for (var j = 0; j < 3; j++) {
console.log(arr[j]()); // return 0, 1, 2
}
2. 箭頭函式
在es5中,this
關鍵字像幽靈一般跳來跳去,不同的場景下this
指向的物件不同。常見的坑是在回撥函式中使用this
,this
會脫離當前物件的上下文,而指向全域性變數window
物件。看下面掉進坑的程式碼:
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
Person.prototype.getName = function () {
return this.firstname + this.lastname;
}
Person.prototype.getNameInCallback = function () {
setTimeout(function () {
console.log(this.firstname, this.lastname);
}, 1000);
}
var p = new Person('jianyong', 'lee');
console.log(p.getName()); // jianyonglee
p.getNameInCallback(); // undefined
注意到最後的p.getNameInCallback()
輸出undefined。這個問題如何解決呢?如果不是使用es6來救火的話,可以考慮使用下面兩個技巧:
1. 使用bind()
函式,為回撥函式繫結上下文:
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
Person.prototype.getName = function () {
return this.firstname + this.lastname;
}
Person.prototype.getNameInCallback = function () {
setTimeout(function () {
console.log(this.firstname, this.lastname);
}.bind(this), 1000);
}
var p = new Person('jianyong', 'lee');
console.log(p.getName()); // jianyonglee
p.getNameInCallback(); // jianyong lee
2.使用其他變數代理this
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
Person.prototype.getName = function () {
return this.firstname + this.lastname;
}
Person.prototype.getNameInCallback = function () {
var ctx = this; // 用ctx變數記錄當前物件
setTimeout(function () {
console.log(ctx.firstname, ctx.lastname); // 這裡使用ctx
}, 1000);
}
var p = new Person('jianyong', 'lee');
console.log(p.getName()); // jianyonglee
p.getNameInCallback(); // jianyong lee
如果使用es6中的箭頭函式就沒這麼多事:
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
Person.prototype.getName = function () {
return this.firstname + this.lastname;
}
Person.prototype.getNameInCallback = function () {
setTimeout(() => {
console.log(this.firstname, this.lastname);
}, 1000);
}
var p = new Person('jianyong', 'lee');
console.log(p.getName());
p.getNameInCallback();