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());
console.log(add(true));
注意,为什么add()
没有输出结果呢?因为变量提升。上面代码中,var b = 10
定义在if
中,由于es5没有块级作用域(函数体是es5中仅有的作用域块),所以变量b相当于在函数内部重新声明了一次,类似下面代码:
var a = 10;
var b = 20;
function add(flag) {
var b;
if (flag) {
b = 10;
return a + b;
}
return a + b;
}
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());
console.log(add(true));
值得一提的是,在for循环中使用var来定义变量,也会遇到变量提升的坑:
var arr = [];
for (var i = 0; i < 3; i++) {
arr.push(function () {
return i;
});
}
console.log(i);
for (var j = 0; j < 3; j++) {
console.log(arr[j]());
}
如果不使用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]());
}
使用es6,在for
循环中使用let
定义变量:
var arr = [];
for (let i = 0; i < 3; i++) {
arr.push(function () {
return i;
});
}
console.log(i);
for (var j = 0; j < 3; j++) {
console.log(arr[j]());
}
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());
p.getNameInCallback();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
注意到最后的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());
p.getNameInCallback();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
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;
setTimeout(function () {
console.log(ctx.firstname, ctx.lastname);
}, 1000);
}
var p = new Person('jianyong', 'lee');
console.log(p.getName());
p.getNameInCallback();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
如果使用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();