是的,这是另一篇关于学习 es2015(称为 es6)的文章。这是我的学习之旅,也是我的个人记事本,记录我一路上发现的新事物。随时建议新的或遗漏的部分或 为本文做出贡献 。它是开源的,在 github 上。
浏览器兼容性
es6 或 es2015 现在的官方称呼已于 2015 年 6 月发布 。因此浏览器开始实现其功能。有些已经处于良好的水平,您可以轻松在线。 kangax 是显示当前浏览器支持的示例之一 。
无论浏览器支持如何,从现在开始,人们将不得不依赖转译器来确保您的代码顺利运行,即使底层浏览器不完全支持所有最新的语言功能。为什么?好吧,对于名称 es2015 , tc39 委员会 明确建议更频繁地每年发布一次。事实上,es7 或更好的 es2016 已经在路上了。
最受欢迎的转译器肯定是
- babeljs - 以前的 es6-to-5 并在本教程中使用
- 示踪剂
addy osmani 在 github 上有一个更完整的 es6-tools 列表 。
工装
现在尝试 es6 的最简单方法可能是在 jsbin.com 上,而无需设置本地工作站。
在 jsbin.com 上设置 es6/babel
只需创建一个新的“bin”,然后选择“es6/babel”作为你的语言。
好的,现在我们已经准备就绪,可以开始了。
用
let
阻止作用域
es5 定义的变量范围与目前流行的许多其他流行语言完全不同。这会造成一些麻烦,尤其是对于新手程序员,但不仅如此。 es5 具有所谓的 函数作用域 ,而不是您期望的 块作用域 。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
这是完全合法的 javascript 代码并将以下内容打印到控制台:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
如您所见,
x
在 if 块之外也是可见的。虽然上面的示例非常清楚,但这可能会导致非常奇怪且难以阅读的代码。代码的输出是什么?
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
它打印:
10
。
通过使用
let
,
es6 允许您像预期的那样阻止变量的作用域
。让我们通过将
var
更改为
let
来调整上面的示例:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
这次,我们得到一个错误。显然(java 程序员会说)。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
虽然您
仍然可以使用
var
,但建议的新方法是切换为
let
。所以……开始习惯了。
亲自尝试一下: 点击这里
在你的代码中重新定义变量?不是 es6,坏孩子!
在 es5 中,这是完全合法的:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
显然,“重用”变量不是您想要做的事情,而是您很可能无意中引入了它。我想你可以想象由此产生的讨厌的错误。幸运的是
,当你在同一个块中重新定义同一个变量时,es6 会抛出一个错误
。但是,您必须使用
let
。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
结果:
重新定义 let 变量时出错
相反, 在子范围内重新定义变量是有效的 ,并导致覆盖先前定义的变量:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
输出是:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
亲自尝试一下: 点击这里
带有
const
真常量
es5 中的常量只不过是简单的命名约定,比如在变量前加上
const
前缀:
var const_pi = 3.141
。但是,没有什么会妨碍您在运行时更改值。
在 es6 中,终于有一个
const
关键字可以满足您的期望。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
如果您尝试写入变量,它会在运行时抛出异常。
尝试设置
const
字段时出错
但是请注意,您还可以将对象分配给 const 变量。不过,分配的是对象引用。因此,通过将另一个对象分配给变量来更改对象引用是行不通的,但您绝对可以更改对象的属性。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
亲自尝试一下: 点击这里
const 变量的范围是 block scope ,这完全有道理。
...
运营商:传播和休息传播
你还记得
function.prototype.apply
和
function.prototype.call
吗?不确定我在谷歌上搜索了多少次“应用与调用”文章,因为我不记得两者中哪一个接受数组,哪个接受参数系列。
apply
通常用于将未知数量的函数参数转发给另一个函数。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
人们这样做的原因有很多,但我现在不想详细说明。这里重要的是上面的代码是不可读的,对吧?人们立即想到:“apply 是做什么的?”和“‘争论’从何而来?”。总的来说,没有 JavaScript 经验的开发人员很难理解。
扩展运算符允许我们以更优雅的方式编写示例:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
现在很明显,我们正在调用函数
anotherfunction
并且我们传递了由其他人传入的可变数量的参数。
展开运算符不仅对函数调用有用,而且在 操作数组 时也很有用。这是一个简单的场景:你知道如何在 javascript 中连接两个数组吗?比如,拥有一个数组并将另一个数组的元素推入第一个数组?
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
es6代码:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
更具可读性,不是吗?你甚至可以这样做:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
亲自尝试一下: 点击这里
休息
不,它与休息(代表性状态转移)无关。其余参数是捕获“其余参数”的一系列函数参数中的最后一个。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
我什至不打算详细说明如何在 es5 中执行此操作(让我们忘记过去)。它与从
arguments
值“切片”到传递给当前函数的参数数量等有关......
解构赋值
解构赋值是将可迭代对象的值赋给变量的过程。 ponyfoo 最近还发布了您可能感兴趣的关于解构的深入指南 。
数组解构
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
执行此代码,
juri
和
thomas
将从分配的
myarray
中获取相应的值。
亲自尝试一下: 点击这里
通过 将解构与 ... 运算符相结合, 您可以获得更有趣的用例(也通常从函数式编程语言中得知)。提取列表的头部或尾部变得非常容易。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
亲自尝试一下: 点击这里
有了这个,再加上一点递归,求和函数可以定义为……
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
亲自尝试一下: 点击这里
还有更多:您可以 在应用解构运算符时忽略值 。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
对象解构
与数组类似,解构也适用于对象。甚至语法也很相似:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
唯一可能看起来很奇怪的是您必须用大括号括起表达式。
亲自尝试一下: 点击这里
现在,在上面的示例中 ,变量名称必须与 object 中的名称相匹配 。情况可能并非总是如此。但是 es6 有一个解决方案。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
你可以让它更短(虽然不确定那是你想要的):
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
默认值
如果在解构期间不存在给定值怎么办?应用默认值!
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
我对函数参数的默认值更加兴奋:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
目前你必须这样写
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
另一个有趣的用例是将 默认值与对象解构 结合起来。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
这种检查很麻烦,并且在嵌套对象时变得更加奇怪。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
亲自尝试一下: 点击这里
链接
箭头函数
如果您是经验丰富的 JavaScript 开发人员,这应该看起来很熟悉:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
这是一个 jquery 单击处理程序的示例。注意到
var that = this
行了吗?这是您必须处理的解决方法 - 主要是在声明回调时 - 以调整
this
的值。回调中
this
的值指向调用回调的函数,而不是回调已实现的外部范围。通常,你想要后者,这就是为什么使用像
var that = this
或
var self = this
的“hacks”。
亲自尝试一下: 点击这里
jquery 甚至有一个 jquery.proxy 来帮助你解决这个问题,但我很少看到它被使用。
es2015 引入了在其运算符之后表示的箭头函数:
=>
。他们帮助应对这些情况。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
因此,上面的内容可以改写为:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
亲自尝试一下: 点击这里
类
与许多 javascript 评论家(主要是那些不太了解 javascript 的人)相反,该语言是完全面向对象的。在 es5 中创建对象的新实例非常简单
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
通过这种方式,您可以直接创建并实例化一个新类,您可以立即使用它:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
如您所见,您可以在对象本身上设置属性和调用方法/函数。
在 es5 中还有另一种定义对象的方法,使用所谓的构造函数,然后通过对象的原型扩展该函数。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
为了能够使用这样的类,它
必须
使用
new
关键字实例化。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
大多数服务器端开发人员对这种方法感觉更舒服。我的意思是,实例化人的第一行是完全有效的 C# 代码!
关于这是否是一件好事,网络上有很长一段时间的争论(只需谷歌搜索原型继承与经典继承)。我现在不打算在这里概述它。过去的东西。
类定义
es2015 对类有原生支持……围绕它们的讨论仍在继续。但让我们看看技术细节:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
很简单,不是吗?没有告诉你我在
getfullname()
函数中使用了另一个很酷的特性:
模板字符串
。但稍后会详细介绍。
另一个重要的事情是 类没有提升 ,这意味着 - 与函数不同 - 你不能在定义之前调用/实例化一个类。
吸气剂和吸气剂
类可以为属性设置 getter 和 setter,这就像在方法之前放置
get
或
set
一样简单。
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
亲自尝试一下: 点击这里
静态方法
静态方法可以定义如下:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
遗产
你试过在 es5 中继承吗?? 您必须使用原型链并将它们之间的对象连接起来。请参阅下面的代码链接。
亲自尝试一下: 点击这里
了解原型链的工作原理并不难,但阅读该代码却一团糟。
我尝试在 es2015 中重写上面的例子。
亲自尝试一下: 点击这里
不确定你,但对我来说,这更具可读性。它主要是语法糖,但更简洁。请注意,用法完全相同!
动态方法名
方法名称也可以在运行时确定。就在 es5 中,您可以使用数组符号
aperson['firstname']
调用对象的属性/函数,您还可以在 es2015 类中定义此类方法:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
显然也适用于静态方法。此类动态或计算属性的一个有趣用例是当您使用 es2015 符号数据类型作为方法名称时。想一想。
亲自尝试一下: 点击这里
模块
就个人而言,模块是我最喜欢 es2015 的地方。到目前为止,在浏览器应用程序中有不同的模块方法。
最简单的形式,一个 iife(立即调用的函数表达式):
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
主要目标:创建私有范围,避免命名冲突并有选择地向外界发布功能。但是一旦必须异步加载“dependency1”和“dependency2”,事情就会变得一团糟。
这就是 amd 和 commonjs 发挥作用的地方,旨在标准化模块的创建和加载方式。 addy osmani 写了整篇关于如何编写模块化 javascript 的 在线文章 。
所以基本上你到目前为止所拥有的是
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
受害最大的是:可读性。嵌套级别很高,即使可以通过良好的缩进以某种方式减轻它。
es2015 中的相同模块定义 :
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
很好,不是吗?
非常简单,一个模块支持两种导出:
- 默认导出(只能是一个)
- 命名导出
不应该混合。例如:
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
通过这种方式,模块可以在外部使用
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
同样,如果我们将上面的示例转换为默认导出,它看起来像
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
..然后将被消费为
function somefunc(){
console.log('entering in somefunc');
if(true){
var x = 'hi there';
}
console.log(x);
}
(更多即将推出...)
更多即将到来。本文是逐步编写的。当发布新更新时,您将通过 @juristr 或 我的 rss feed 收到通知。