zl程序教程

您现在的位置是:首页 >  前端

当前栏目

使用 ES6 写更好的 JavaScript part I:广受欢迎的新特性

JavaScriptES6 特性 更好 Part 受欢迎 使用
2023-09-27 14:27:56 时间
创建一个 catch 块。 我绝对没哟开玩笑. 创建一个代码块。如果你用的是 ES2015,在一段代码块中用 let 或者 const 声明的变量会限制它们只在这个块中可见。这叫做_块级作用域_.

一个_代码块_就是你用花括号包起来的部分。 { 像这样 }。在 if/else 声明和 try/catch/finally 块中经常出现。如果你想利用块作用域的优势,你可以用花括号包裹任意的代码来创建一个代码块

考虑下面的代码片段。

// 在 Node 中你需要使用 strict 模式尝试这个

"use strict";

var foo = "foo";

function baz() {

 if (foo) {

 var bar = "bar";

 let foobar = foo + bar;

 // foo 和 bar 这里都可见 

 console.log("This situation is " + foo + bar + ". Im going home.");

 try {

 console.log("This log statement is " + foobar + "! It threw a ReferenceError at me!");

 } catch (err) {

 console.log("You got a " + err + "; no dice.");

 try {

 console.log("Just to prove to you that " + err + " doesnt exit outside of the above `catch` block.");

 } catch (err) {

 console.log("Told you so.");

baz();

try {

 console.log(invisible);

} catch (err) {

 console.log("invisible hasnt been declared, yet, so we get a " + err);

let invisible = "You cant see me, yet"; // let 声明的变量在声明前是不可访问的

还有些要强调的

注意 foobar 在 if 块之外是不可见的,因为我们没有用let 声明; 我们可以在任何地方使用 foo ,因为我们用 var 定义它为全局作用域可见; 我们可以在 baz 内部任何地方使用 bar, 因为 var-声明的变量是在定义的整个作用域内都可见。 用 let or const 声明的变量不能在定义前调用。换句话说,它不会像 var 变量一样被编译器提升到作用域的开始处。

const 与 let 类似,但有两点不同。


不能 改变const变量的值,只有在创建它时可以给它赋值。如果你试图改变它的值,会得到一个 TyepError。
let   const: Who Cares?

我们已经用 var 将就了二十多年了,你可能在想我们_真的_需要新的类型声明关键字吗?(这里作者应该是想表达这个意思)

问的好,简单的回答就是-- 不, 并不 真正 需要。但在可以用let 和 const 的地方使用它们很有好处的。


let 和 const 声明变量时都不会被提升到作用域开始的地方,这样可以使代码可读性更强,制造尽可能少的迷惑。 它会尽可能的约束变量的作用域,有助于减少令人迷惑的命名冲突。 这样可以让程序只有在必须重新分配变量的情况下重新分配变量。 const 可以加强常量的引用。

另一个例子就是 let 在 for 循环中的使用:

"use strict";

var languages = [Danish, Norwegian, Swedish];

//会污染全局变量!

for (var i = 0; i languages.length; i += 1) {

 console.log(`${languages[i]} is a Scandinavian language.`);

console.log(i); // 4

for (let j = 0; j languages.length; j += 1) {

 console.log(`${languages[j]} is a Scandinavian language.`);

try {

 console.log(j); // Reference error

} catch (err) {

 console.log(`You got a ${err}; no dice.`);

在 for循环中使用 var 声明的计数器并不会 真正 把计数器的值限制在本次循环中。 而 let 可以。

let 在每次迭代时重新绑定循环变量有很大的优势,这样每个循环中拷贝 自身 , 而不是共享全局范围内的变量。

"use strict";

// 简洁明了

for (let i = 1; i i += 1) {

 setTimeout(function() {

 console.log("Ive waited " + i + " seconds!");

 }, 1000 * i);

// 功能完全混乱

for (var j = 0; j j += 1) {

 setTimeout(function() {

 console.log("Ive waited " + j + " seconds for this!");

 }, 1000 * j);

第一层循环会和你想象的一样工作。而下面的会每秒输出 "Ive waited 6 seconds!"。

好吧,我选择狗带。

动态 this 关键字的怪异

JavaScript 的 this 关键字因为总是不按套路出牌而臭名昭著。

事实上,它的 规则相当简单。不管怎么说,this 在有些情形下会导致奇怪的用法

"use strict";

const polyglot = {

 name : "Michel Thomas",

 languages : ["Spanish", "French", "Italian", "German", "Polish"],

 introduce : function () {

 // this.name is "Michel Thomas"

 const self = this;

 this.languages.forEach(function(language) {

 // this.name is undefined, so we have to use our saved "self" variable 

 console.log("My name is " + self.name + ", and I speak " + language + ".");

polyglot.introduce();

在 introduce 里, this.name 是 undefined。在回调函数外面,也就是 forEach 中, 它指向了 polyglot 对象。在这种情形下我们总是希望在函数内部 this 和函数外部的 this 指向同一个对象。

问题是在 JavaScript 中函数会根据确定性四原则在调用时定义自己的 this 变量。这就是著名的 动态 this 机制。

这些规则中没有一个是关于查找 this 所描述的“附近作用域”的;也就是说并没有一个确切的方法可以让 JavaScript 引擎能够基于包裹作用域来定义 this的含义。

这就意味着当引擎查找 this 的值时,可以找到值,但却和回调函数之外的不是同一个值。有两种传统的方案可以解决这个问题。

在函数外面吧 this 保存到一个变量中,通常取名 self,并在内部函数中使用;或者 在内部函数中调用 bind 阻止对 this 的赋值。

以上两种办法均可生效,但会产生副作用。

另一方面,如果内部函数 没有 设置它自己的 this 值,JavaScript 会像查找其它变量那样查找 this 的值:通过遍历父作用域直到找到同名的变量。这样会让我们使用附近作用域代码中的 this 值,这就是著名的 词法 this 。

如果有样的特性,我们的代码将会更加的清晰,不是吗?

箭头函数中的词法 this

在 ES2015 中,我们有了这一特性。箭头函数 不会 绑定 this 值,允许我们利用词法绑定 this 关键字。这样我们就可以像这样重构上面的代码了:

"use strict";

let polyglot = {

 name : "Michel Thomas",

 languages : ["Spanish", "French", "Italian", "German", "Polish"],

 introduce : function () {

 this.languages.forEach((language) = {

 console.log("My name is " + this.name + ", and I speak " + language + ".");

. . . 这样就会按照我们想的那样工作了。

箭头函数有一些新的语法。

"use strict";

let languages = ["Spanish", "French", "Italian", "German", "Polish"];

// 多行箭头函数必须使用花括号, 

// 必须明确包含返回值语句

 let languages_lower = languages.map((language) = {

 return language.toLowerCase()

// 单行箭头函数,花括号是可省的,

// 函数默认返回最后一个表达式的值

// 你可以指明返回语句,这是可选的。

let languages_lower = languages.map((language) = language.toLowerCase());

// 如果你的箭头函数只有一个参数,可以省略括号

let languages_lower = languages.map(language = language.toLowerCase());

// 如果箭头函数有多个参数,必须用圆括号包裹

let languages_lower = languages.map((language, unused_param) = language.toLowerCase());

console.log(languages_lower); // ["spanish", "french", "italian", "german", "polish"]

// 最后,如果你的函数没有参数,你必须在箭头前加上空的括号。

(() = alert("Hello!"))();

MDN 关于箭头函数的文档 解释的很好。

简写属性和方法

ES2015 提供了在对象上定义属性和方法的一些新方式。

简写方法

在 JavaScript 中, method 是对象的一个有函数值的属性:

"use strict";

const myObject = {

 const foo = function () {

 console.log(bar);

在ES2015 中,我们可以这样简写:

"use strict";

const myObject = {

 foo () {

 console.log(bar);

 * range (from, to) {

 while (from to) {

 if (from === to)

 return ++from;

 else

 yield from ++;

注意你也可以使用生成器去定义方法。只需要在函数名前面加一个星号 (*)。

这些叫做 方法定义 。和传统的函数作为属性很像,但有一些不同:


我会在随后的几篇文章中讲到 super 关键字。如果你等不及了, Exploring ES6 中有关于它的干货。

简写和推导属性

ES6 还引入了 简写 和 推导属性 。

如果对象的键值和变量名是一致的,那么你可以仅用变量名来初始化你的对象,而不是定义冗余的键值对。

"use strict";

const foo = foo;

const bar = bar;

// 旧语法

const myObject = {

 foo : foo,

 bar : bar

// 新语法

const myObject = { foo, bar }

两中语法都以 foo 和 bar 键值指向 foo and bar 变量。 后面的方式语义上更加一致;这只是个语法糖。

当用揭示模块模式来定义一些简洁的公共 API 的定义,我常常利用简写属性的优势。

"use strict";

function Module () {

 function foo () {

 return foo;

 function bar () {

 return bar;

 // 这样写:

 const publicAPI = { foo, bar }

 /* 不要这样写:

 const publicAPI = {

 foo : foo,

 bar : bar

 } */ 

 return publicAPI;

这里我们创建并返回了一个 publicAPI 对象,键值 foo 指向 foo 方法,键值 bar 指向 bar 方法。

推导属性名

这是 不常见 的例子,但 ES6 允许你用表达式做属性名。

"use strict";

const myObj = {

 // 设置属性名为 foo 函数的返回值

 [foo ()] () {

 return foo;

function foo () {

 return foo;

console.log(myObj.foo() ); // foo

根据 Dr. Raushmayer 在 Exploring ES6中讲的,这种特性最主要的用途是设置属性名与 Symbol 值一样。

Getter 和 Setter 方法

最后,我想提一下 get 和 set 方法,它们在 ES5 中就已经支持了。

"use strict";

// 例子采用的是 MDNs 上关于 getter 的内容

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get

const speakingObj = {

 // 记录 “speak” 方法调用过多少次

 words : [],

 speak (word) {

 this.words.push(word);

 console.log(speakingObj says  + word + !);

 get called () {

 // 返回最新的单词

 const words = this.words;

 if (!words.length)

 return speakingObj hasn\t spoken, yet.;

 else

 return words[words.length - 1];

console.log(speakingObj.called); // speakingObj hasnt spoken, yet.

speakingObj.speak(blargh); // speakingObj says blargh!

console.log(speakingObj.called); // blargh

使用 getters 时要记得下面这些:

Getters 不接受参数; 属性名不可以和 getter 函数重名; 可以用 Object.defineProperty(OBJECT, "property name", { get : function () { . . . } }) 动态创建 getter

作为最后这点的例子,我们可以这样定义上面的 getter 方法:

"use strict";

const speakingObj = {

 // 记录 “speak” 方法调用过多少次

 words : [],

 speak (word) {

 this.words.push(word);

 console.log(speakingObj says  + word + !);

// 这只是为了证明观点。我是绝对不会这样写的

function called () {

 // 返回新的单词

 const words = this.words;

 if (!words.length)

 return speakingObj hasn\t spoken, yet.;

 else

 return words[words.length - 1];

Object.defineProperty(speakingObj, "called", get : getCalled ) 

除了 getters,还有 setters。像平常一样,它们通过自定义的逻辑给对象设置属性。

"use strict";

// 创建一个新的 globetrotter(环球者)!

const globetrotter = {

 // globetrotter 现在所处国家所说的语言 

 const current_lang = undefined,

 // globetrotter 已近环游过的国家

 let countries = 0,

 // 查看环游过哪些国家了

 get countryCount () {

 return this.countries;

 // 不论 globe trotter 飞到哪里,都重新设置他的语言

 set languages (language) {

 // 增加环游过的城市数

 countries += 1;

 // 重置当前语言

 this.current_lang = language; 

globetrotter.language = Japanese;

globetrotter.countryCount(); // 1

globetrotter.language = Spanish;

globetrotter.countryCount(); // 2

上面讲的关于 getters 的也同样适用于 setters ,但有一点不同:

getter 不接受 参数, setters 必须 接受 正好一个 参数。

破坏这些规则中的任意一个都会抛出一个错误。

既然 Angular 2 正在引入 TypeCript 并且把 class 带到了台前,我希望 get and set 能够流行起来. . . 但还有点希望它们不要:fire:起来。

结论

未来的 JavaScript 正在变成现实,是时候把它提供的东西都用起来了。这篇文章里,我们浏览了 ES2015 的三个很流行的特性:


关于 let,const,以及块级作用域的详细信息,请参考 Kyle Simpsons take on block scoping。这里有你快速练习需要的所有指导,参考 MDN 关于 let 和 const的详细信息。

Dr Rauschmayer 写了一片篇相当好的关于箭头函数和词法 this 的文章。如果你想了解关于这篇文章更深层次的信息,这绝对是一篇好文。

最后关于我们这里讨论的所有的更详细更深入的内容,请看 Dr Rauschmayer 的书 Exploring ES6,这是最好的关于 web 最好的一体化指导手册。

ES2015 的特性中哪个最让你激动? 有什么想让我在后面的文章中写入的新特性? 那就在下面或者在 Twitter 上 (@PelekeS) 评论吧 -- 我会尽最大的努力单独回复你的。

原文发布时间为:2016年06月12日 本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
JavaScript中的重要组成部分之ES6特性 随着互联网技术的不断发展,前端开发也变得越来越重要。在这样的背景下,ES6(ECMAScript 2015)作为 JavaScript 的一个重要版本,为前端开发带来了许多新特性和功能,本文将介绍一些常用的 ES6 特性。