1. 程式人生 > >es6改進es5中的一些坑

es6改進es5中的一些坑

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指向的物件不同。常見的坑是在回撥函式中使用thisthis會脫離當前物件的上下文,而指向全域性變數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();