ES6,更好的节点,Pt。 一世

介绍 ( Introduction )

With the ES2015 spec finalized and Node.js shipping with a substantial subset of its functionailty, it's safe to say it: The Future is Upon Us.

随着ES2015规范的最终确定以及Node.js附带的大部分功能失效,可以肯定地说:“未来就在我们身上”。

. . . I've always wanted to say that.

。 。 。 我一直想这么说。

But, it's true. The V8 Engine is swiftly approaching spec-compliance, and Node ships with a good selection of ES2015 features ready for production. It's this latter list of features that I consider the Essentials™, as it represents the set of feature we can use without a transpiler like Babel or Traceur.

但是,这是真的。 V8引擎正在Swift达到规范要求Node附带了可供选择的大量ES2015功能供生产使用 。 我考虑的是Essentials™的最新功能列表,因为它代表了无需BabelTraceur这样的编译器就可以使用的功能集。

This article will cover three of the more popular ES2015 features available in Node:

本文将介绍Node中可用的三种最受欢迎​​的ES2015功能:

  • Block scoping with let and const;

    letconst范围界定;
  • Arrow functions; and

    箭头功能; 和
  • Shorthand properties & methods.

    速记属性和方法。

Let's get to it.

让我们开始吧。

用let和const阻止作用域 ( Block Scope with let and const )

Scope refers to where in your program your variables are visible. In other words, it's the set of rules that determines where you're allowed to use the variables you've declared.

范围是指变量在程序中的可见位置。 换句话说,这是一组规则,用于确定允许在何处使用已声明的变量。

We've mostly all heard the claim that JavaScript only creates new scopes inside of functions. While a good 98% of the useful scopes you've created were, in fact, function scopes, there are actually three ways to create a new scope in JavaScript. You can:

我们几乎都听到过这样的说法,即JavaScript仅在函数内部创建新范围。 实际上,虽然您创建的有用范围中有98%是函数范围,但实际上有三种方法可以在JavaScript中创建新范围。 您可以:

  1. Create a function. You probably know this already.

    创建一个函数 。 您可能已经知道了。
  2. Create a catch block. I'm not kidding.

    创建一个catch我不是在开玩笑
  3. Create a code block. If you're writing ES2015. declaring variables with let or const within a code block restricts their visibility to that block only*. This is called *block scoping.

    创建一个代码块 。 如果您正在编写ES2015。 在代码块中用letconst声明变量将其可见性限制该块* 这称为* block scoping

A block is just a section of code wrapped in curly braces. { like this }. They appear naturally around if/else statements and try/catch/finally blocks. You can also wrap arbitrary sections of code in braces to create a code block, if you want to take advantage of block-scoping.

只是用大括号括起来的一段代码。 { like this } 。 它们自然出现在if / else语句周围iftry / catch / finally块。 如果要利用块作用域,还可以将任意代码段括在大括号中以创建代码块。

Consider this snippet.

考虑一下此片段。

// You have to use strict to try this in Node
"use strict";

var foo = "foo";
function baz() {
    if (foo) {
        var bar = "bar";
        let foobar = foo + bar;
    }
    // Both foo and bar are visible here
    console.log("This situation is " + foo + bar + ". I'm 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 + " doesn't exit outside of the above `catch` block.");
    } catch (err) {
        console.log("Told you so.");
    }
}

baz();

try {
    console.log(invisible);
} catch (err) {
    console.log("invisible hasn't been declared, yet, so we get a " + err);
}
let invisible = "You can't see me, yet"; // let-declared variables are inaccessible before declaration

A few things to note.

需要注意的几件事。

  • Notice that foobar isn't visible outside of the if block, because we declared it with let;

    注意, foobarif块之外不可见,因为我们用let声明了它;
  • We can use foo anywhere, because we defined it as a var in the global scope; and

    我们可以在任何地方使用foo ,因为我们在全局范围内将其定义为var 。 和
  • We can use bar anywhere inside of baz, because var-declared variables are accessible throughout the entirety of the scope they're defined.

    我们可以在baz内的任何地方使用bar ,因为var声明的变量在定义的整个作用域中都是可访问的。
  • We can't use let or const-declared variables before we've defined them. In other words, they're not hoisted by the compiler, as var-declarations are.

    在定义变量之前,不能使用letconst声明的变量。 换句话说,它们不会像var -declaration那样被编译器挂起。

The const keyword behaves similarly to let, with two differences.

const关键字的行为与let相似,但有两个区别。

  1. You must assign a value to a const-declared variable when you create it. You can't create it first and assign it later.

    创建变量时, 必须为const声明的变量分配一个值。 您不能先创建它,然后再分配它。
  2. You cannot change the vaue of a const-declared variable after you create it. If you try, you'll get a TypeError.

    创建变量后,您无法更改const声明的变量的值。 如果尝试,将得到TypeError

letconst :谁在乎? (let & const: Who Cares?)

Since we've gotten by just fine with var for a good twenty years, now, you might be wondering if we really need new variables.

既然我们在var相处已经二十年了,所以现在,您可能想知道我们是否真的需要新的变量。

Good question. The short answer -- no. Not really. But there are a few good reasons to use let and const where possible.

好问题。 简短的答案-不。 不是真的 。 但是有一些充分的理由在可能的地方使用letconst

  • Neither let nor const-declared variables are hoisted to the top of their scopes, which can make for more readable, less confusing code.

    letconst声明的变量都不会放在其作用域的顶部,这可以使代码更易读,更易混淆。
  • They limit your variables' visibility as much as possible, which helps prevent confusing namespace collisions.

    它们尽可能地限制了变量的可见性,这有助于防止混淆名称空间冲突。
  • It's easier to reason about programs that reassign variables only when absolutely necesary. const helps enforce immutable variable references.

    仅在绝对必要时才需要对重新分配变量的程序进行推理。 const帮助实施不可变的变量引用。

Another use case is that of let in for loops.

另一个用例是的是letfor循环。

"use strict";

var languages = ['Danish', 'Norwegian', 'Swedish'];

// Pollutes global namespace. Ew!
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.`);
}

Using var to declare the counter in a for loop doesn't actually keep the counter local to the loop. Using let instead does.

使用varfor循环中声明计数器实际上并没有使计数器在循环本地。 使用let代替。

let also has the major advantage of rebinding the loop variable on every iteration, so each loop gets its own copy, rather than sharing the globally-scoped variable.

let还具有在每次迭代时重新绑定循环变量的主要优点,因此每个循环都有自己的副本,而不是共享全局范围的变量。

"use strict";

// Simple & Clean
for (let i = 1; i < 6; i += 1) {
    setTimeout(function() {
        console.log("I've waited " + i + " seconds!");
    }, 1000 * i);
}

// Totally dysfunctional
for (var j = 0; j < 6; j += 1) {
        setTimeout(function() {
        console.log("I've waited " + j + " seconds for this!");
    }, 1000 * j);
}

The first loop does what you think it does. The bottom one prints "I've waited 6 seconds!", every second.

第一个循环按照您的想法进行。 底部每秒钟打印一次“我已经等了6秒!”。

Pick your poison.

选择你的毒药。

动态的怪癖 ( The Quirks of Dynamic this )

JavaScript's this keyword is notorious for doing basically everything except for you want it to.

JavaScript的this关键字臭名昭著,除了您希望执行的所有操作外,它几乎可以执行所有操作。

The truth is, the rules are really quite simple. Regardless, there are situations where this can encourage awkward idioms.

事实是, 规则确实非常简单 。 无论如何,在某些情况下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();

Inside of introduce, this.name is undefined. Right outside of the callback, in our forEach loop, it refers to the polyglot object. Often, what we want in cases like this is for this within our inner function to refer to the same object that this refers to in the outer function.

introducethis.nameundefined 。 在回调的外部,在我们的forEach循环中,它指向polyglot对象。 通常情况下,我们希望在这样的情况下,究竟是什么this我们内部函数内引用同一个对象, this是指在外部函数。

The problem is that functions in JavaScript always define their own this values upon invocation, according to a well-established set of four rules. This mechanim is known as dynamic this.

问题在于,JavaScript中的函数总是根据一套完善的四个规则来定义自己的this值。 这mechanim称为动态this

Not a single one of these rules involves looking up what this means "nearby"; there is no conceivable way for the JavaScript engine to define this based on its meaning within a surrounding scope.

没有这些规则中的单一一个涉及查找什么this意思是“附近”; JavaScript引擎无法根据其含义在周围范围内定义this方法。

This all means that, when the engine looks up the value of this, it will find one, but it will not be the same as the value outside of the callback. There are two traditional workarounds to the problem.

所有这些都意味着,当引擎查找的值this ,它找到一个,但它不会是相同的回调的价值之外。 有两种传统的解决方法。

  1. Save this in the outer function to a variable, usually called self, and use that within the inner function; or

    保存this在外部函数的变量,通常被称为self ,和使用该内部函数内; 要么
  2. Call bind on the inner function to permanently set its this value.

    在内部函数上调用bind以永久设置其this值。

These methods work, but they can be noisy.

这些方法有效,但可能会产生干扰。

If, on the other hand, inner functions did not set their own this values, JavaScript would look up the value of this just as it would look up the value of any other variable: By stepping through parent scopes until it finds one with the same name. That would let us use the value of this from "nearby" source code, and is known as lexical this.

如果,另一方面,内部功能没有设置自己的this值,JavaScript的将查找的价值this只是因为它会查找任何其他变量的值:由通过家长加强作用域,直到它找到一个具有相同名称。 这将使我们能够使用“附近”源代码中this的值,并且被称为词汇this

Quite a bit of code would be quite a bit cleaner if we had such a feature, don't you think?

如果具有这样的功能,相当多的代码会更简洁一些,您是不是认为呢?

词汇this与Arrow功能 (Lexical this with Arrow Functions)

With ES2015, we do. Arrow functions do not bind a this value, allowing us to take advantage of lexical binding of the this keyword. We can refactor the broken code from above like 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 + ".");
        });
    }
}

. . . And all would work as expected.

。 。 。 一切都会按预期进行。

Arrow functions have a few types of syntax.

箭头函数具有几种语法。

"use strict";

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

// In a multiline arrow function, you must use curly braces, 
//  and you must include an explicit return statement.
let languages_lower = languages.map((language) => {
    return language.toLowerCase()
});

// In a single-line arrow function, curly braces are optional,
//   and the function implicitly returns the value of the last expression.
//   You can include a return statement if you'd like, but it's optional.
let languages_lower = languages.map((language) => language.toLowerCase());

// If your arrow function only takes one argument, you don't need to wrap it in
//   parentheses. 
let languages_lower = languages.map(language => language.toLowerCase());

// If your function takes multiple arguments, you must wrap them in parentheses.
let languages_lower = languages.map((language, unused_param) => language.toLowerCase());

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

// Finally, if your function takes no arguments, you must include empty parentheses before the arrow.
(() => alert("Hello!"))();

The MDN docs on arrow functions are great for reference.

关于箭头功能的MDN文档非常适合参考。

速记属性和方法 ( Shorthand Properties & Methods )

ES2015 also gives us a few new ways to define properties and methods on objects.

ES2015还为我们提供了一些新的方法来定义对象的属性和方法。

速记方法 (Shorthand Methods)

In JavaScript, a method is a property on an object that has a function value:

在JavaScript中, 方法是具有函数值的对象的属性:

"use strict";

// Kudos to @_finico for catching a type in the first draft.
const myObject = {
    foo : function () {
        console.log('bar');
    },
}

In ES2015, we can simply write:

在ES2015中,我们可以简单地编写:

"use strict";

const myObject = {
    foo () {
        console.log('bar');
    },
    * range (from, to) {
        while (from < to) {
            if (from === to)
                return ++from;
            else
                yield from ++;
        }
    }
}

Note that you can use generators to define methods, too. All you need to do is prepend the function's name with an asterisk (*).

请注意,您也可以使用生成器来定义方法。 您需要做的就是在函数名称的前面加上星号(*)。

These are called method definitions. They're similar to traditional functions-as-properties, but have a few key differences:

这些称为方法定义 。 它们类似于传统的函数即属性,但有一些主要区别:

  • You can only call super from a method definition;

    只能方法定义中调用super ;
  • You are not allowed to call a method definition with new.

    不允许调用一个方法定义new

I'll cover classes and the super keyword in a later article. If you just can't wait, Exploring ES6 has all the goodies.

我将在以后的文章中介绍类和super关键字。 如果您迫不及待,请探索Exploring ES6

速记和计算属性 (Shorthand & Computed Properties)

ES6 also introduces shorthand and computed properties.

ES6还引入了速记计算属性

If the name of your object's keys are identical to the variables naming their values, you can initialize your object literal with just the variable names, rather than defining it as a redundant key-value pair.

如果对象键的名称与命名其值的变量相同,则可以仅使用变量名来初始化对象文字,而不必将其定义为冗余键值对。

"use strict";

const foo = 'foo';
const bar = 'bar';

// Old syntax
const myObject = {
    foo : foo,
    bar : bar
};

// New syntax
const myObject = { foo, bar }

Both syntaxes create an object with foo and bar keys that refer to the values of the foo and bar variables. The latter approach is semantically identical; it's just syntactically sweeter.

两种语法都使用指向foobar变量值的foobar键创建对象。 后一种方法在语义上是相同的。 在语法上更甜蜜。

I often take advantage of shorthand properties to write succinct definitions of public APIs when using the revealing module pattern.

在使用显示模块模式时,我经常利用速记属性来编写公共API的简洁定义。

"use strict";

function Module () {
    function foo () {
        return 'foo';
    }

    function bar () {
        return 'bar';
    }

    // Write this:
    const publicAPI = { foo, bar }

    /* Not this:
    const publicAPI =  {
       foo : foo,
       bar : bar
    } */ 

    return publicAPI;
};

Here, we create and return a publicAPI object, whose key foo refers to the foo method, and whose key bar refers to the bar method.

在这里,我们创建并返回一个publicAPI对象,其键foo指的是foo方法,其键bar指的是bar方法。

计算的属性名称 (Computed Property Names)

This is a bit of a niche case, but ES6 also allows you to use expressions as property names.

这是一个有点小生情况,但也ES6允许您使用表达式作为属性名称。

"use strict";

const myObj = {
  // Set property name equal to return value of foo function
    [foo ()] () {
      return 'foo';
    }
};

function foo () {
    return 'foo';
}

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

According to Dr. Raushmayer in Exploring ES6, the main use case for this feature is in setting property names equal to Symbol values.

根据Raushmayer博士在“ 探索ES6”中的介绍 ,此功能的主要用例是将属性名称设置为等于Symbol值。

获取和设置方法 (Getter & Setter Methods)

Finally, I'd like to remind you of the get and set methods, which have been around since ES5.

最后,我想提醒您一下getset方法,这些方法自ES5以来就存在。

"use strict";

// Example adapted from MDN's page on getters
//   https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
const speakingObj = {
    // Track how many times "speak" has been called 
    words : [],

    speak (word) {
        this.words.push(word);
        console.log('speakingObj says ' + word + '!');
    },

    get called () {
        // Returns latest word
        const words = this.words;
        if (!words.length)
            return 'speakingObj hasn\'t spoken, yet.';
        else
            return words[words.length - 1];
    }
};

console.log(speakingObj.called); // 'speakingObj hasn't spoken, yet.'

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

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

There are a few things to keep in mind when using getters:

使用getter时要牢记以下几点:

  • Getters can't take arguments;

    吸气剂不能争论。
  • You can't have properties with the same names as your getter functions;

    您不能拥有与getter函数同名的属性;
  • You can create a getter dynamically by using Object.defineProperty(OBJECT, "property name", { get : function () { . . . } })

    您可以使用Object.defineProperty(OBJECT, "property name", { get : function () { . . . } })动态创建吸气剂

As an example of this last point, we could have defined the above getter this way:

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

"use strict";

const speakingObj = {
    // Track how many times "speak" has been called 
    words : [],

    speak (word) {
        this.words.push(word);
        console.log('speakingObj says ' + word + '!');
    }
};

// This is just to prove a point. I definitely wouldn't write it this way.
function called () {
    // Returns latest word
    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 ) 

In addition to getters, we have setters. Unsurprsingly, they set properties on an object with custom logic.

除了getter,我们还有setter。 毫不奇怪,它们使用自定义逻辑在对象上设置属性。

"use strict";

// Create a new globetrotter!
const globetrotter = {
    // Language spoken in the country our globetrotter is currently in
    const current_lang = undefined,

    // Number of countries our globetrotter has travelled to
    let countries = 0,

    // See how many countries we've travelled to
    get countryCount () {
        return this.countries;
    }, 

    // Reset current language whenever our globe trotter flies somewhere new
    set languages (language) {
        // Increment number of coutnries our globetrotter has travelled to
        countries += 1;

        // Reset current language
        this.current_lang = language; 
    };
};

globetrotter.language = 'Japanese';
globetrotter.countryCount; // 1

globetrotter.language = 'Spanish';
globetrotter.countryCount; // 2

Everything we said about getters above applies to setters as well, with one difference:

我们上面所说的关于getter的所有内容也适用于setter,但有一个区别:

  • Unlike getters, which can take no arguments, setters must take exactly one argument

    不带参数的getter不同,setter 必须一个参数

Breaking either of these rules throws an error.

违反这些规则之一将引发错误。

Now that Angular 2 is bringing TypeScript and the class keyword to the fore, I expect get and set to spike in popularity. . . But I kind of hope they don't.

既然Angular 2将TypeScript和class关键字放在了首位,我希望getset会大受欢迎。 。 。 但是我希望他们不会。

结论 ( Conclusion )

Tomorrow's JavaScript is happening today, and it's high time to get a grip on what it has to offer. In this article, we've looked at three of the more popular features from ES2015:

明天JavaScript如今正在发生,现在是时候掌握它提供的功能了。 在本文中,我们研究了ES2015最受欢迎的三个功能:

  • Block scoping with let and const;

    letconst范围界定;
  • Lexical scoping of this with arrow functions;

    的词法作用域this带箭头的功能;
  • Shorthand object properties and methods, plus a review of getter and setter functions.

    速记对象属性和方法,以及对getter和setter函数的回顾。

For detailed thoughts on let, const, and the notion of block scoping, read Kyle Simpson's take on block scoping. If all you need is a quick practical reference, check the MDN pages for let and const.

有关letconst以及块作用域概念的详细想法,请阅读Kyle Simpson的块作用域概念 。 如果您需要的只是快速的实用参考,请查看MDN页面的letconst

Dr Rauschmayer has a wonderful article on arrow functions and lexical this. It's great reading if you want a bit more detail than I had room to cover here.

Rauschmayer博士撰写了一篇关于箭头函数精彩文章,并用词法this词汇化 。 如果您想要的细节比我在这里可以讨论的更多,这是一本很棒的书。

Finally, for an exhaustive take on all of what we've talked about here -- and a great deal more -- Dr Rauschmayer's book, Exploring ES6, is the best all-in-one reference the web has to offer.

最后,对于我们在这里讨论的所有内容(包括更多内容)进行详尽的研究,Rauschmayer博士的书《 Exploring ES6 》是网络必须提供的最好的多合一参考。

What ES2015 feature are you most excited about? Is there anything you'd like to see covered in a future article? Let me know in the comments below, or hit me on Twitter (@PelekeS) -- I'll do my best to get back to everyone individually.

您最兴奋的ES2015功能是什么? 您想在以后的文章中看到什么吗? 在下面的评论中让我知道,或者在Twitter( @PelekeS )上打我-我会尽力与每个人联系。

Note: This is part 1 of the Better JavaScript series. You can see parts 2 and 3 here:

注意:这是Better JavaScript系列的第1部分。 您可以在此处看到第2部分和第3部分:

翻译自: https://scotch.io/tutorials/better-node-with-es6-pt-i

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值