静下心来的个人专栏
上一篇

BabelJS - 将 ES6 功能转换为 ES5

广告
选中文字可对指定文章内容进行评论啦,→和←可快速切换按钮,绿色背景文字可以点击查看评论额。
大纲

BabelJS - 将 ES6 功能转换为 ES5

在本文以及接下来的文章中,我们将看到 ES6 中添加的特性。我们还将学习如何使用 BabelJS 将功能编译到 ES5。

以下是我们将在本章中讨论的各种 ES6 特性

  • Let + Const
  • Arrow Functions
  • Classes
  • Promises
  • Generators
  • Destructuring
  • Iterators
  • Template Literalst
  • Enhanced Object
  • Default, Rest & Spread Properties

Let + Const

Let 在 JavaScript 中声明一个块作用域的局部变量。请考虑以下示例以了解 let 的用法。

let a = 1;
if (a == 1) {
   let a = 2;
   console.log(a);
}
console.log(a);

输出

2
1

第一个控制台打印 2 的原因是因为 a 再次使用 let 声明并且仅在 if 块中可用。使用 let 声明的任何变量都只能在声明的块中使用。我们已经使用 let 声明了变量 a 两次,但它并没有覆盖 a 的值。

这就是 var 和 let 关键字的区别。当您使用 var 声明变量时,该变量将在函数范围内可用,或者如果声明将充当全局变量。

如果使用 let 声明变量,则该变量在块范围内可用。如果在 if 语句中声明,它将仅在 if 块中可用。这同样适用于 switch、for 循环等。

我们现在将看到在 ES5 中使用 babeljs 进行代码转换。 让我们运行以下命令来转换代码

npx babel let.js --out-file let_es5.js

let 关键字从 es6 到 es5 的输出如下

"use strict";

var a = 1;
if (a == 1) {
   var _a = 2;
   console.log(_a);
}
console.log(a);

如果您看到 ES5 代码,let 关键字将替换为 var 关键字。 if 块中的变量也被重命名为 _a 以具有与使用 let 关键字声明时相同的效果。

Const

在本节中,我们将了解 const 关键字在 ES6 和 ES5 中的工作原理。 const关键字在范围内也可用;如果在外面,它会抛出一个错误。const 声明变量的值一旦分配就不能更改。让我们考虑以下示例以了解如何使用 const 关键字。

let a =1;
if (a == 1) {
   const age = 10;
}
console.log(age);

输出

Uncaught ReferenceError: age is not defined at :5:13

上面的输出会抛出一个错误,因为 const age 是在 if 块内定义的,并且在 if 块内可用。 我们将了解如何使用 BabelJS 转换为 ES5。

npx babel const.js --out-file const_es5.js

输出如下:

"use strict";

var a = 1;
if (a == 1) {
   var _age = 10;
}
console.log(age);

在 ES5 中,const 关键字被替换为 var 关键字,如上所示。

Arrow Functions

与变量表达式相比,箭头函数的语法更短。它也被称为胖箭头函数或 lambda 函数。该函数没有自己的 this 属性。在这个函数中,关键字function被省略了。

var add = (x,y) => {
   return x+y;
}

var k = add(3,6);
console.log(k);

输出:

9

使用 BabelJS,我们将上面的代码转换为 ES5。

npx babel arrowfunction.js --out-file arrowfunction_es5.js

使用 Babel 将箭头函数转换为变量表达式函数,如下所示。

"use strict";

var add = function add(x, y) {
   return x + y;
};

var k = add(3, 6);
console.log(k);

Classes

ES6 带有新的类特性。类类似于 ES5 中可用的基于原型的继承。class 关键字用于定义类。类就像特殊的函数,也有类似函数表达式的相似之处。它有一个构造函数,在类内部调用。

class Person {
   constructor(fname, lname, age, address) {
      this.fname = fname;
      this.lname = lname;
      this.age = age;
      this.address = address;
   }

   get fullname() {
      return this.fname +"-"+this.lname;
   }
}
var a = new Person("Siya", "Kapoor", "15", "Mumbai");
var persondet = a.fullname;

输出

Siya-Kapoor
npx babel class.js --out-file class_es5.js

使用 babeljs 添加了额外的代码,以使功能与 ES5 中的类相同。BabelJs 确保功能与 ES6 中的功能相同。

"use strict";

var _createClass = function () {
   function defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
         var descriptor = props[i];
         descriptor.enumerable = descriptor.enumerable || false;
         descriptor.configurable = true;
         if ("value" in descriptor) descriptor.writable = true;
         Object.defineProperty(target, descriptor.key, descriptor);
      }
   }
   return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps);
      if (staticProps) defineProperties(Constructor, staticProps);
      return Constructor;
   };
}();

function _classCallCheck(instance, Constructor) {
   if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
   }
}

var Person = function () {
   function Person(fname, lname, age, address) {
      _classCallCheck(this, Person);

      this.fname = fname;
      this.lname = lname;
      this.age = age;
      this.address = address;
   }

   _createClass(Person, [{
      key: "fullname",
      get: function get() {
         return this.fname + "-" + this.lname;
      }
   }]);

   return Person;
}();

var a = new Person("Siya", "Kapoor", "15", "Mumbai");
var persondet = a.fullname;

Promises

JavaScript Promise 用于管理代码中的异步请求。

当您管理来自具有依赖性的异步请求的多个回调时,它使生活更轻松并保持代码干净。Promise 提供了一种更好的使用回调函数的方法。 Promise 是 ES6 的一部分。默认情况下,当您创建一个 Promise 时,该 Promise 的状态是挂起的。

Promise来自三种状态:

  • pending (initial state)
  • resolved (completed successfully)
  • rejected(failed)

new Promise() 用于构造一个 Promise。 Promise 构造函数有一个参数,它是一个回调函数。回调函数有两个参数——resolve和reject;

这两个都是内部函数。您编写的异步代码,即 Ajax 调用、图像加载、计时函数将进入回调函数。

如果回调函数中执行的任务成功,则调用resolve函数;否则,将使用错误详细信息调用拒绝函数。

以下代码行显示了一个 Promise 结构调用

var _promise = new Promise (function(resolve, reject) {
   var success = true;
   if (success) {
      resolve("success");
   } else {
      reject("failure");
   }
});
_promise.then(function(value) {
   //once function resolve gets called it comes over here with the value passed in resolve
   console.log(value); //success
}).catch(function(value) {
   //once function reject gets called it comes over here with the value passed in reject
   console.log(value); // failure.
});

ES6 Promise 示例

let timingpromise = new Promise((resolve, reject) => {
   setTimeout(function() {
      resolve("Promise is resolved!");
   }, 1000);
});

timingpromise.then((msg) => {
   console.log(msg);
});

输出

Promise is resolved!
npx babel promise.js --out-file promise_es5.js
"use strict";

var timingpromise = new Promise(function (resolve, reject) {
   setTimeout(function () {
      resolve("Promise is resolved!");
   }, 1000);
});

timingpromise.then(function (msg) {
   console.log(msg);
});

对于 Promise,代码在转译时不会改变。我们需要使用 babel-polyfill 才能在旧版浏览器上工作。关于 babel-polyfills 的详细信息在 babel - poyfill 章节中进行了解释。

 

Generators

生成器函数就像普通函数一样。该函数具有特殊的语法 function* 和 * 到函数和要在函数内部使用的 yield 关键字。这意味着在需要时暂停或启动该功能。正常函数一旦执行开始,就不能在两者之间停止。当它遇到返回语句时,它将执行完整的函数或停止。生成器在这里执行不同的操作,您可以使用 yield 关键字暂停函数,并在需要时再次调用生成器来启动它。

function* generatorfunction(a) {
   yield a;
   yield a +1 ;
}

let g = generatorfunction(8);
console.log(g.next());
console.log(g.next());

输出:

{value: 8, done: false}
{value: 9, done: false}

执行命令:

npx babel generator.js --out-file generator_es5.js

输出结果如下:

"use strict";

var _marked = /*#__PURE__*/regeneratorRuntime.mark(generatorfunction);

function generatorfunction(a) {
   return regeneratorRuntime.wrap(function generatorfunction$(_context) {
      while (1) {
         switch (_context.prev = _context.next) {
            case 0:
               _context.next = 2;
               return a;

            case 2:
               _context.next = 4;
               return a + 1;
               
            case 4:
            case "end":
               return _context.stop();
         }
      }
   }, _marked, this);
}

var g = generatorfunction(8);
console.log(g.next());
console.log(g.next());

 

Iterators

JavaScript 中的迭代器返回一个 JavaScript 对象,该对象具有值。该对象还有一个名为 done 的标志,它具有真/假值。如果它不是迭代器的结尾,则返回 false。让我们考虑一个例子,看看迭代器在数组上的工作。

let numbers = [4, 7, 3, 10];
let a = numbers[Symbol.iterator]();
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());

在上面的示例中,我们使用了一个数字数组,并使用 Symbol.iterator 作为索引在数组上调用了一个函数。

我们在数组上使用 next() 得到的输出如下:

{value: 4, done: false}
{value: 7, done: false}
{value: 3, done: false}
{value: 10, done: false}
{value: undefined, done: true}

输出给出一个具有值的对象,并作为属性完成。每个 next() 方法调用都会给出数组中的下一个值,并以 false 完成。只有当数组中的元素完成时,done 的值才会为真。我们可以使用它来迭代数组。还有更多可用的选项,例如 for-of 循​​环,使用如下:

let numbers = [4, 7, 3, 10];
for (let n of numbers) {
   console.log(n);
}

输出:

4
7
3
10

当 for-of 循​​环使用键时,它会给出数组值的详细信息,如上所示。我们将检查这两种组合,看看 babeljs 如何将它们转换为 es5。

执行如下命令:

npx babel iterator.js --out-file iterator_es5.js

结果如下:

"use strict";

var numbers = [4, 7, 3, 10];
var a = numbers[Symbol.iterator]();
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());

var _array = [4, 7, 3, 10];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
   for (var _iterator = _array[Symbol.iterator](),
      _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
      _iteratorNormalCompletion = true) {
      var n = _step.value;

      console.log(n);
   }
} catch (err) {
   _didIteratorError = true;
   _iteratorError = err;
} finally {
   try {
      if (!_iteratorNormalCompletion && _iterator.return) {
         _iterator.return();
      }
   } finally {
      if (_didIteratorError) {
         throw _iteratorError;
      }
   }
}

在 es5 中添加了 for-of 循​​环的更改。但是 iterator.next 保持原样。我们需要使用 babel-polyfill 让它在旧浏览器中工作。 Babel-polyfill 与 babel 一起安装,并且可以从 node_modules 中使用,如下所示

<html>
   <head>
      <script type="text/javascript" src="node_modules/babel-polyfill/dist/polyfill.min.js"></script>
      <script type="text/javascript" src="iterator_es5.js"></script>
   </head>
   <body>
      <h1>Iterators</h1>
   </body>
</html>

Destructuring

解构属性的行为就像一个 JavaScript 表达式,它从数组、对象中解包值。 下面的例子将解释解构语法的工作。

let x, y, rem;
[x, y] = [10, 20];

console.log(x);
console.log(y);
[x, y, ...rem] = [10, 20, 30, 40, 50];
console.log(rem);

let z = 0;
({ x, y } = (z) ? { x: 10, y: 20 } : { x: 1, y: 2 });
console.log(x);
console.log(y);
10
20
[30, 40, 50]
1
2

上面的代码行显示了如何将值从数组的右侧分配给左侧的变量。带有 ...rem 的变量从数组中获取所有剩余的值。

我们还可以使用条件运算符从左侧的对象中分配值,如下所示

({ x, y } = (z) ? { x: 10, y: 20 } : { x: 1, y: 2 });
console.log(x); // 1
console.log(y); // 2

让我们使用 babeljs 将其转换为 ES5

npx babel destructm.js --out-file destruct_es5.js
"use strict";

var x = void 0,
   y = void 0,
   rem = void 0;
x = 10;
y = 20;

console.log(x);
console.log(y);
x = 10;
y = 20;
rem = [30, 40, 50];

console.log(rem);

var z = 0;

var _ref = z ? { x: 10, y: 20 } : { x: 1, y: 2 };

x = _ref.x;
y = _ref.y;

console.log(x);
console.log(y);

Template Literals

Template literal是一个字符串文字,它允许在其中使用表达式。它使用反引号(``)而不是单引号或双引号。当我们在字符串中说表达式时,这意味着我们可以在字符串中使用变量、调用函数等。

let a = 5;
let b = 10;
console.log(`Using Template literal : Value is ${a + b}.`);
console.log("Using normal way : Value is " + (a + b));
Using Template literal : Value is 15.
Using normal way : Value is 15
npx babel templateliteral.js --out-file templateliteral_es5.js
"use strict";

var a = 5;
var b = 10;
console.log("Using Template literal : Value is " + (a + b) + ".");

console.log("Using normal way : Value is " + (a + b));

Enhanced Object Literals

在 es6 中,添加到对象字面量的新特性非常好用。我们将通过几个 ES5 和 ES6 中的对象字面量示例 

ES5
var red = 1, green = 2, blue = 3;
var rgbes5 = {
   red: red,
   green: green,
   blue: blue
};
console.log(rgbes5); // {red: 1, green: 2, blue: 3}

ES6
let rgbes6 = {
   red,
   green,
   blue
};
console.log(rgbes6); // {red: 1, green: 2, blue: 3}

如果你看到上面的代码,ES5 和 ES6 中的对象是不同的。在 ES6 中,如果变量名称与键相同,我们不必指定键值。

让我们看看使用 babel 编译到 ES5。

const red = 1, green = 2, blue = 3;
let rgbes5 = {
   red: red,
   green: green,
   blue: blue
};
console.log(rgbes5);

let rgbes6 = {
   red,
   green,
   blue
};
console.log(rgbes6);

let brand = "carbrand";
const cars = {
   [brand]: "BMW"
}
console.log(cars.carbrand);  //"BMW"
npx babel enhancedobjliteral.js --out-file enhancedobjliteral_es5.js
"use strict";

function _defineProperty(obj, key, value) {
   if (key in obj) {
      Object.defineProperty(obj, key, {
         value: value, enumerable: true, configurable: true, writable: true
      });
   } else { obj[key] = value; } return obj;
}

var red = 1,
   green = 2,
   blue = 3;
var rgbes5 = {
   red: red,
   green: green,
   blue: blue
};
console.log(rgbes5);

var rgbes6 = {
   red: red,
   green: green,
   blue: blue
};
console.log(rgbes6);

var brand = "carbrand";
var cars = _defineProperty({}, brand, "BMW");

console.log(cars.carbrand); //"BMW"

Default, Rest & Spread Properties

在本节中,我们将讨论 default、rest 和 spread 属性。

使用 ES6,我们可以对函数 params 使用默认参数,如下所示

let add = (a, b = 3) => {
   return a + b;
}

console.log(add(10, 20));  // 30
console.log(add(10));      // 13

让我们使用 babel 将上述代码转换为 ES5。

npx babel default.js --out-file default_es5.js
"use strict";

var add = function add(a) {
   var b = arguments.length > 1 >> arguments[1] !== undefined ? arguments[1] : 3;
   return a + b;
};

console.log(add(10, 20));
console.log(add(10));

Rest 参数以三个点 (...) 开头,如下例所示

let add = (...args) => {
   let sum = 0;
   args.forEach(function (n) {
      sum += n;
   });
   return sum;
};

console.log(add(1, 2));    // 3
console.log(add(1, 2, 5, 6, 6, 7));   //27

在上面的函数中,我们将 n 个参数传递给函数 add。如果在 ES5 中添加所有这些参数,我们必须依赖 arguments 对象来获取参数的详细信息。在 ES6 中,rest 有助于用三个点定义参数,如上所示,我们可以遍历它并获得数字的总和。

注意 - 使用三个点时,我们不能使用额外的参数,即rest。

let add = (...args, value) => {    //syntax error
   let sum = 0;
   args.forEach(function (n) {
      sum += n;
   });
   return sum;
};

上面的代码会给出语法错误。 es5的编译如下

npx babel rest.js --out-file rest_es5.js
"use strict";

var add = function add() {
   for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
   }

   var sum = 0;
   args.forEach(function (n) {
      sum += n;
   });
   return sum;
};

console.log(add(1, 2));
console.log(add(1, 2, 5, 6, 6, 7));

Spread 属性也有像 rest 一样的三个点。以下是一个工作示例,展示了如何使用 spread 属性。

let add = (a, b, c) => {
   return a + b + c;
}
let arr = [11, 23, 3];
console.log(add(...arr));   //37

现在让我们看看上面的代码是如何使用 babel 转译的

npx babel spread.js --out-file spread_es5.js
"use strict";

var add = function add(a, b, c) {
   return a + b + c;
};
var arr = [11, 23, 3];
console.log(add.apply(undefined, arr));

Proxies

代理是一个对象,您可以在其中为属性查找、赋值、枚举、函数、调用等操作定义自定义行为。

var a = new Proxy(target, handler);

目标和处理程序都是对象。target 是一个对象,也可以是另一个代理元素。 handler 将是一个对象,其属性作为函数,在调用时将给出行为。

让我们试着通过一个例子来理解这些特性

let handler = {
   get: function (target, name) {
      return name in target ? target[name] : "invalid key";
   }
};

let o = {
   name: 'Siya Kapoor',
   addr: 'Mumbai'
}

let a = new Proxy(o, handler);
console.log(a.name);
console.log(a.addr);
console.log(a.age);

我们在上面的示例中定义了目标和处理程序,并将其与代理一起使用。代理返回带有键值的对象。

Siya Kapoor
Mumbai
invalid key

现在让我们看看如何使用 babel 将上述代码转换为 ES5

npx babel proxy.js --out-file proxy_es5.js
'use strict';

var handler = {
   get: function get(target, name) {
      return name in target ? target[name] : "invalid key";
   }
};

var o = {
   name: 'Siya Kapoor',
   addr: 'Mumbai'
};

var a = new Proxy(o, handler);
console.log(a.name);
console.log(a.addr);
console.log(a.age);

版权声明:著作权归作者所有。

X

欢迎加群学习交流

联系我们