JavaScript 编程语言的全新变化,Harmony、ES6 和 ES2015 它们只是同一事物的不同名称,重要的是,这些名称代表 JavaScript 编程语言的更新,经历了一些大刀阔斧的必要改进,随着这些改进,产生了一批新的关键字、编写函数的方法和异步简便方法等等。接下来我们将介绍 JS 编程语言添加的新功能,使我们可以更快捷、简洁和高效地编写代码。
let
和 const
从现在开始,建议放弃使用 var,改为使用 let 和 const
。为了理解为何添加了 let 和 const,我们先看一个示例,了解使用 var 会带来怎样的麻烦。
function getClothing(isCold) {
if (isCold) {
var freezing = "Grab a jacket!";
} else {
var hot = "It's a shorts kind of day.";
console.log(freezing);
}
}
getClothing(false);
// 输出:undefined
提升
是浏览器解析 JavaScript 的结果。本质上,在执行任何 JavaScript 代码之前,所有变量
都会被“提升”,也就是提升到函数作用域的顶部
。因此在运行时,getClothing() 函数实际上看起来如下所示…
function getClothing(isCold) {
var freezing, hot;
if (isCold) {
freezing = "Grab a jacket!";
} else {
hot = "It's a shorts kind of day.";
console.log(freezing);
}
}
getClothing(false);
// 输出:undefined
使用 let
和 const
声明的变量解决了这种提升问题,因为它们的作用域是到块
,而不是函数。之前,当你使用 var 时,变量要么为全局作用域,要么为本地作用域,也就是整个函数作用域。
如果在代码块(用花括号 { } 表示)中使用 let 或 const 声明变量,那么该变量会陷入暂时性死区
,直到该变量的声明被处理。这种行为会阻止变量被访问,除非它们被声明了。
function getClothing(isCold) {
if (isCold) {
let freezing = "Grab a jacket!";
} else {
let hot = "It's a shorts kind of day.";
console.log(freezing);
}
}
getClothing(false);
// 输出:Uncaught ReferenceError: haha is not defined
关于使用 let
和 const
的规则:
let
声明的变量可以重新赋值
,但是不能在同一作用域内重新声明
。const
声明的变量必须赋初始值
,但是不能在同一作用域内重新声明
,也无法重新赋值
。最大的问题是何时应该使用 let
和 const
?一般法则如下:
打算为变量重新赋值
时,使用 let
不打算为变量重新赋值
时,使用 const
。因为 const
是声明变量最严格
的方式,我们建议始终使用 const 声明变量,因为这样代码更容易读懂
,你知道标识符在程序的整个生命周期内都不会改变。如果你发现你需要更新变量或更改变量,则回去将其从 const 切换成 let
。
const CHARACTER_LIMIT = 255;
const posts = [
"#DeepLearning transforms everything from self-driving cars to language translations. AND it's our new Nanodegree!",
"Within your first week of the VR Developer Nanodegree Program, you'll make your own virtual reality app",
"I just finished @udacity's Front-End Web Developer Nanodegree. Check it out!"
];
// prints posts to the console
function displayPosts() {
for (let i = 0; i < posts.length; i++) {
console.log(posts[i].slice(0, CHARACTER_LIMIT));
}
}
displayPosts();
在 ES6 之前,将字符串连接到一起的旧方法是使用字符串连接运算符 (+
)。
const student = {
name: 'Richard Kalehoff',
guardian: 'Mr. Kalehoff'
};
const teacher = {
name: 'Mrs. Wilson',
room: 'N231'
}
let message = student.name + ' please see ' + teacher.name + ' in ' + teacher.room + ' to pick up your report card.';
作为字符串连接运算符 ( +
) 的替代方法,你可以使用字符串的 concat()
方法。但是这两种方式都比较笨拙。
模板字面量本质上是包含嵌入式表达式的字符串字面量
。
let message = `${student.name} please see ${teacher.name} in ${teacher.room} to pick up your report card.`;
模板字面量用倒引号 ( ` )(而不是单引号 ( ’ ) 或双引号( “” ))表示,可以包含用 ${expression}
表示的占位符
,这样更容易构建字符串。
// +
let note = teacher.name + ',\n\n' +
'Please excuse ' + student.name + '.\n' +
'He is recovering from the flu.\n\n' +
'Thank you,\n' +
student.guardian;
// ${}
let note = `${teacher.name},
Please excuse ${student.name}.
He is revovering from the flu.
Thank you,
${student.guardian}`;
/**
输出:
Mrs. Wilson,
Please excuse Richard Kalehoff.
He is recovering from the flu.
Thank you,
Mr. Kalehoff
*/
这是模板字面量的真正强大之处。在上述动画中,去掉了引号和字符串连接运算符,以及换行符 ( \n
)。这是因为模板字面量也将换行符当做字符串的一部分!
注意:模板字面量中的嵌入式表达式不仅仅可以用来引用变量
。你可以在嵌入式表达式中进行运算、调用函数和使用循环
!
在 ES6 中,你可以使用解构从数组和对象中提取值并赋给独特的变量
。
// 解构前提取数据
const point = [10, 25, -34];
const x = point[0];
const y = point[1];
const z = point[2];
console.log(x, y, z);
// 解构后提取数据
const point = [10, 25, -34];
const [x, y, z] = point;
console.log(x, y, z);
在此示例中,方括号 [ ]
表示被解构的数组
,x、y 和 z 表示要将数组中的值存储在其中的变量
。注意,你不需要指定要从中提取值的索引,因为索引可以暗示出来。
注意,在解构数组时,还可以忽略值
。例如,const [x, , z] = point
; 忽略了 y 坐标。
// 解构前提取数据
const gemstone = {
type: 'quartz',
color: 'rose',
karat: 21.29
};
const type = gemstone.type;
const color = gemstone.color;
const karat = gemstone.karat;
console.log(type, color, karat);
// 解构后提取数据
const gemstone = {
type: 'quartz',
color: 'rose',
karat: 21.29
};
const {type, color, karat} = gemstone;
console.log(type, color, karat);
在此示例中,花括号 { }
表示被解构的对象
,type、color 和 karat 表示要将对象中的属性存储到其中的变量
。注意不用指定要从其中提取值的属性。因为 gemstone 对象具有 type 属性,值自动存储在 type 变量中
。
注意,你还可以指定在解构对象时要选择的值
。例如,let {color} = gemstone
; 将仅选择 gemstone 对象中的 color 属性。
const circle = {
radius: 10,
color: 'orange',
getArea: function() {
return Math.PI * this.radius * this.radius;
},
getCircumference: function() {
return 2 * Math.PI * this.radius;
}
};
let {radius, getArea, getCircumference} = circle;
/**
undefined
radius
10
getArea
ƒ () {
return Math.PI * this.radius * this.radius;
}
getCircumference
ƒ () {
return 2 * Math.PI * this.radius;
}
getArea();
NaN
getCircumference();
NaN*/
在此示例中,调用 getArea() 将返回 NaN。在解构该对象并将 getArea() 方法存储到 getArea 变量中时,它无法再访问 circle 对象中的 this,得出面积 NaN
。
ES6 中经常出现的一个现象是删掉不必要的重复代码。通过删掉不必要的重复代码,代码更容易读懂,并且更简练。推出的新简写法(用来初始化对象并向对象中添加方法
)就是这一体现。
let type = 'quartz';
let color = 'rose';
let carat = 21.29;
const gemstone = {
type: type,
color: color,
carat: carat
};
console.log(gemstone);
看到重复的地方了吗?type: type
、color: color
和 carat:carat
不显得很冗长吗?
如果属性名称和所分配的变量名称一样
,那么就可以从对象属性中删掉这些重复的变量名称
。
let type = 'quartz';
let color = 'rose';
let carat = 21.29;
const gemstone = { type, color, carat };
console.log(gemstone);
还有一种向对象中添加方法的简写方式:
let type = 'quartz';
let color = 'rose';
let carat = 21.29;
const gemstone = {
type,
color,
carat,
calculateWorth: function() {
// 将根据类型(type),颜色(color)和克拉(carat)计算宝石(gemstone)的价值
}
};
// 简写
let gemstone = {
type,
color,
carat,
calculateWorth() { ... }
};
真的需要 function 关键字吗?在 ES6 中不需要!只需引用 gemstone
对象的 calculateWorth
属性以便调用该函数,因此关键字 function 是多余的,可以删掉。
通过上面的学习,我们学会了使用 let 和 const 声明变量
的新方法,如何编写模版字面量
以方便字符串插值,解构数组和对象
以及初始化对象
的一些便捷方法。