×
Community Blog Do I Need to Transcode the Code of ES6 or Its Later Versions to the Code of ES5?

Do I Need to Transcode the Code of ES6 or Its Later Versions to the Code of ES5?

This article discusses ES5 code, ES6 code, and various transcoding tools (such as Babel).

By Xulun from F(x) Team

In order to be compatible with old browsers, especially the IE series, frontend code that uses the specifications of ES6 or later is often transcoded into ES5 code using transcoding tools such as Babel.

It has been seven years since the ES6 was released in 2015. What is the compatibility of browsers with ES6 now?

Let's look at the CanIUse data:

1
98.14% of browsers support ES6. The data does not exceed 99% because there is still 1.08% of Opera Mini (released in 2015) to be used. Safari on iOS and Chrome released for mobile phones after 2016 all support ES6. The current proportion of users on Safari for iOS 7.0-9.3 is 0.15%. Android WebView has fully supported ES6 since Android 5.0.

It seems that nearly 99% capabilities of devices have not been applied because of the small number of old devices. However, it does not seem to be convincing. In addition, many applications have special processing for low-end machines, and mid-to-high-end machines must be recent old devices. At least for mid-to-high-end machines, the necessity of transcoding compatibility is negligible.

However, ES6 and its later versions are composed of multiple features. Therefore, it cannot be simply understood as ES6 being better than ES5. We will compare transcoded and non-transcoded main features.

Better Results without Transcoding

Const

The const is to be checked with constants. For example:

let f1 = () => {
  const a = 0;
  a = 2;
};
f1();

After transcoding, Babel generates a _readOnlyError function for us:

function _readOnlyError(name) { throw new TypeError("\"" + name + "\" is read-only"); }

var f1 = function f1() {
  var a = 0;
  2, _readOnlyError("a");
};
f1();

Instead of looking at the bytecode, we can know which is better by looking at the source code.

Array Copy

After ES6, we use the extension operator "..." to perform array copy.

  const a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  let a2 = [...a1];

Babel is competent for transcoding simply with a concat function:

  var a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  var a2 = [].concat(a1);

However, it is different from the point of view of the bytecode because v8 provides the CreateArrayFromIterable instruction.

Only a 9-byte instruction is required to have it done before transcoding:

Bytecode length: 9
Parameter count 1
Register count 2
Frame size 16
OSR nesting level: 0
Bytecode Age: 0
         0x3c140829374e @    0 : 79 00 00 25       CreateArrayLiteral [0], [0], #37
         0x3c1408293752 @    4 : c4                Star0 
         0x3c1408293753 @    5 : 7a                CreateArrayFromIterable 
         0x3c1408293754 @    6 : c3                Star1 
         0x3c1408293755 @    7 : 0e                LdaUndefined 
         0x3c1408293756 @    8 : a9                Return 
Constant pool (size = 1)
0x3c1408293721: [FixedArray] in OldSpace
 - map: 0x3c1408002205 <Map>
 - length: 1
           0: 0x3c1408293715 <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x3c14082936e5 <FixedArray[10]>>

After transcoding, the function is called, and an empty array is generated, which requires a total of 21 bytes:

         0x3c1408293696 @    0 : 79 00 00 25       CreateArrayLiteral [0], [0], #37
         0x3c140829369a @    4 : c4                Star0 
         0x3c140829369b @    5 : 7b 01             CreateEmptyArrayLiteral [1]
         0x3c140829369d @    7 : c1                Star3 
         0x3c140829369e @    8 : 2d f7 01 02       LdaNamedProperty r3, [1], [2]
         0x3c14082936a2 @   12 : c2                Star2 
         0x3c14082936a3 @   13 : 5e f8 f7 fa 04    CallProperty1 r2, r3, r0, [4]
         0x3c14082936a8 @   18 : c3                Star1 
         0x3c14082936a9 @   19 : 0e                LdaUndefined 
         0x3c14082936aa @   20 : a9                Return 
Constant pool (size = 2)
0x3c1408293665: [FixedArray] in OldSpace
 - map: 0x3c1408002205 <Map>
 - length: 2
           0: 0x3c1408293659 <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x3c1408293629 <FixedArray[10]>>
           1: 0x3c1408202e9d <String[6]: #concat>

String.raw

Transcoding also generates multiple functions for string.raw. For example, this is before transcoding:

let f1 = () => {
  String.raw`\n`;
};

f1();

After transcoding, Babel generates a _taggedTemplateLiteral function for us:

var _templateObject;

function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }

var f1 = function f1() {
    String.raw(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n"])));
};

f1();

Symbol

Symbol is a new data type added by ES6. In ES6, we need to judge its type and can use the operator named typeof.

let f2 = () => {
  let s1 = Symbol();
  return typeof s1;
};

This is difficult for Babel, and it is necessary to introduce a library to solve it:

function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }

var f1 = function f1() {
  var s1 = Symbol();
  return _typeof(s1);
};

rest Parameters

v8 offers the CreateRestParameter command to support rest parameters. However, the original arguments also support the CreateMappedArguments command.

However, the length will be shorter from the point of view of source code if transcoding is not performed:

let f1 = (...values) => {
    let sum = 0;
    for (let v of values) {
        sum += v;
    }
    return sum;
};
f1(1, 4, 9);

This is after transcoding:

var f1 = function f1() {
    var sum = 0;

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

    for (var _i = 0, _values = values; _i < _values.length; _i++) {
        var v = _values[_i];
        sum += v;
    }

    return sum;
};

Optional Catch Parameters

This is a feature of ES2019, and it can omit the error type in the catch. It was supported by Safari in the first half of 2018.

let f3 = f2 => {
  try {
    f2();
  } catch {
    console.error("Error");
  }
};

After transcoding, Babel generates an unused error variable, •_unused•, for us:

var f1 = function f1(f2) {
    try {
        f2();
    } catch (_unused) {
        console.error("Error");
    }
};

With the error variable, v8 generates a CatchContext for us through the CreateCatchContext and a CATCH_SCOPE for the catch block:

         0x1937082936b6 @    0 : 19 ff fa          Mov <context>, r0
         0x1937082936b9 @    3 : 61 03 00          CallUndefinedReceiver0 a0, [0]
         0x1937082936bc @    6 : 8a 20             Jump [32](0x1937082936dc @ 38)
         0x1937082936be @    8 : c3                Star1 
         0x1937082936bf @    9 : 82 f9 00          CreateCatchContext r1, [0]
         0x1937082936c2 @   12 : c4                Star0 
         0x1937082936c3 @   13 : 10                LdaTheHole 
         0x1937082936c4 @   14 : a6                SetPendingMessage 
         0x1937082936c5 @   15 : 0b fa             Ldar r0
         0x1937082936c7 @   17 : 1a f9             PushContext r1
         0x1937082936c9 @   19 : 21 01 02          LdaGlobal [1], [2]
         0x1937082936cc @   22 : c1                Star3 
         0x1937082936cd @   23 : 2d f7 02 04       LdaNamedProperty r3, [2], [4]
         0x1937082936d1 @   27 : c2                Star2 
         0x1937082936d2 @   28 : 13 03             LdaConstant [3]
         0x1937082936d4 @   30 : c0                Star4 
         0x1937082936d5 @   31 : 5e f8 f7 f6 06    CallProperty1 r2, r3, r4, [6]
         0x1937082936da @   36 : 1b f9             PopContext r1
         0x1937082936dc @   38 : 0e                LdaUndefined 
         0x1937082936dd @   39 : a9                Return 
Constant pool (size = 4)
0x19370829367d: [FixedArray] in OldSpace
 - map: 0x193708002205 <Map>
 - length: 4
           0: 0x193708293649 <ScopeInfo CATCH_SCOPE>
           1: 0x193708202741 <String[7]: #console>
           2: 0x193708202769 <String[5]: #error>
           3: 0x19370800455d <String[5]: #Error>

No CatchContext is directly generated because of optional catch parameters.

         0x19370829376a @    0 : 19 ff fa          Mov <context>, r0
         0x19370829376d @    3 : 61 03 00          CallUndefinedReceiver0 a0, [0]
         0x193708293770 @    6 : 8a 15             Jump [21](0x193708293785 @ 27)
         0x193708293772 @    8 : 10                LdaTheHole 
         0x193708293773 @    9 : a6                SetPendingMessage 
         0x193708293774 @   10 : 21 00 02          LdaGlobal [0], [2]
         0x193708293777 @   13 : c2                Star2 
         0x193708293778 @   14 : 2d f8 01 04       LdaNamedProperty r2, [1], [4]
         0x19370829377c @   18 : c3                Star1 
         0x19370829377d @   19 : 13 02             LdaConstant [2]
         0x19370829377f @   21 : c1                Star3 
         0x193708293780 @   22 : 5e f9 f8 f7 06    CallProperty1 r1, r2, r3, [6]
         0x193708293785 @   27 : 0e                LdaUndefined 
         0x193708293786 @   28 : a9                Return 
Constant pool (size = 3)
0x193708293735: [FixedArray] in OldSpace
 - map: 0x193708002205 <Map>
 - length: 3
           0: 0x193708202741 <String[7]: #console>
           1: 0x193708202769 <String[5]: #error>
           2: 0x19370800455d <String[5]: #Error>

Generator

Parsing and assigning values that use iterators are the highly efficient part of transcoding in the next section. However, it is another case for the generator explicitly using iterators.

Let's look at the simplest generator. We can only generate a few numbers:

let f1 = () => {
  let obj1 = {
    *[Symbol.iterator]() {
      yield 1;
      yield 2;
      yield 3;
    }
  };
  [...obj1];
};

As we can see, transcoding results define several functions and require the support of regeneratorRuntime:

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

var f1 = function f1() {
  var obj1 = {
    [Symbol.iterator]() {
      return /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
        return regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return 1;

              case 2:
                _context.next = 4;
                return 2;

              case 4:
                _context.next = 6;
                return 3;

              case 6:
              case "end":
                return _context.stop();
            }
          }
        }, _callee);
      })();
    }

  };

  _toConsumableArray(obj1);
};

If you run the code above with Node, an error will be reported:

  var obj1 = _defineProperty({}, Symbol.iterator, /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
                                                               ^
ReferenceError: regeneratorRuntime is not defined

Since a run library has not been introduced, we have to add a library and install it first:

npm install --save @babel/polyfill

Then, bring in the library:

require("@babel/polyfill");
...
var f1 = function f1() {
    var obj1 = {
        [Symbol.iterator]() {
            return /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
...

The amount of code added needs you to calculate.

Class

The class is similar to syntax sugar of the function in nature, but Babel transcoding may generate more code than most colleagues think. Let's look at a simple example:

  class Code {
    constructor(source) {
      this.source = source;
    }
  }
  code1 = new Code("test1.js");

This is the result after transcoding. Babel generates three functions: the_createClass,_classCallCheck, and _defineProperties:

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); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }

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

var Code = /*#__PURE__*/_createClass(function Code(source) {
    _classCallCheck(this, Code);

    this.source = source;
});

code1 = new Code("test1.js");

Built-In Objects Depending on Polyfill and Runtime

We know ES6 has added many new objects and new attributes of original objects.

For example, if you use newly added objects (such as Set, Map, WeakSet, and WeakMap) or new methods (such as •Number.isNaN•), Babel does not transcode those into ES5 statements. How are these statements supported in old browsers? The answer (as you may have guessed) is the •@ babel/polyfill• library we used in the generator previously.

The Babel polyfill library is based on two open-source libraries:

  • One is the regenerator library of Facebook, regenerator runtime.
  • The other is the core-js library, which supports most built-in objects.

Our code is written like this:

Array.from(new Set([1, 2, 3, 2, 1]));
[1, [2, 3], [4, [5]]].flat(2);
Promise.resolve(32).then(x => console.log(x));

Babel/runtime executes like this:

import from from 'core-js-pure/stable/array/from';
import flat from 'core-js-pure/stable/array/flat';
import Set from 'core-js-pure/stable/set';
import Promise from 'core-js-pure/stable/promise';

from(new Set([1, 2, 3, 2, 1]));
flat([1, [2, 3], [4, [5]]], 2);
Promise.resolve(32).then(x => console.log(x));

Better Transcoding Effect

There are special cases in everything. In terms of some features, transcoding may lead to better results.

Deconstruction and Assignment

We quote the example of variable exchange by Ruan Yifeng:

let f1 = () => {
  let x = 1;
  let y = 2;
  [x, y] = [y, x];
};

f1();

After transcoding, it becomes like this:

var f1 = function f1() {
  var x = 1;
  var y = 2;
  var _ref = [y, x];
  x = _ref[0];
  y = _ref[1];
};

f1();

First, let's look at the bytecode after transcoding, which has 44 bytes:

         0xf1208293682 @    0 : 0d 01             LdaSmi [1]
         0xf1208293684 @    2 : c4                Star0 
         0xf1208293685 @    3 : 0d 02             LdaSmi [2]
         0xf1208293687 @    5 : c3                Star1 
         0xf1208293688 @    6 : 79 00 00 25       CreateArrayLiteral [0], [0], #37
         0xf120829368c @   10 : c0                Star4 
         0xf120829368d @   11 : 0c                LdaZero 
         0xf120829368e @   12 : c1                Star3 
         0xf120829368f @   13 : 0b f9             Ldar r1
         0xf1208293691 @   15 : 36 f6 f7 01       StaInArrayLiteral r4, r3, [1]
         0xf1208293695 @   19 : 0d 01             LdaSmi [1]
         0xf1208293697 @   21 : c1                Star3 
         0xf1208293698 @   22 : 0b fa             Ldar r0
         0xf120829369a @   24 : 36 f6 f7 01       StaInArrayLiteral r4, r3, [1]
         0xf120829369e @   28 : 19 f6 f8          Mov r4, r2
         0xf12082936a1 @   31 : 0c                LdaZero 
         0xf12082936a2 @   32 : 2f f8 03          LdaKeyedProperty r2, [3]
         0xf12082936a5 @   35 : c4                Star0 
         0xf12082936a6 @   36 : 0d 01             LdaSmi [1]
         0xf12082936a8 @   38 : 2f f8 05          LdaKeyedProperty r2, [5]
         0xf12082936ab @   41 : c3                Star1 
         0xf12082936ac @   42 : 0e                LdaUndefined 
         0xf12082936ad @   43 : a9                Return

How many bytes are required for a more concise deconstruction above? The answer is 189 bytes because it involves iterators:

         0xf120829376e @    0 : 0d 01             LdaSmi [1]
         0xf1208293770 @    2 : c4                Star0 
         0xf1208293771 @    3 : 0d 02             LdaSmi [2]
         0xf1208293773 @    5 : c3                Star1 
         0xf1208293774 @    6 : 79 00 00 25       CreateArrayLiteral [0], [0], #37
         0xf1208293778 @   10 : c1                Star3 
         0xf1208293779 @   11 : 0c                LdaZero 
         0xf120829377a @   12 : c2                Star2 
         0xf120829377b @   13 : 0b f9             Ldar r1
         0xf120829377d @   15 : 36 f7 f8 01       StaInArrayLiteral r3, r2, [1]
         0xf1208293781 @   19 : 0d 01             LdaSmi [1]
         0xf1208293783 @   21 : c2                Star2 
         0xf1208293784 @   22 : 0b fa             Ldar r0
         0xf1208293786 @   24 : 36 f7 f8 01       StaInArrayLiteral r3, r2, [1]
         0xf120829378a @   28 : b1 f7 03 05       GetIterator r3, [3], [5]
         0xf120829378e @   32 : 19 f7 f8          Mov r3, r2
         0xf1208293791 @   35 : 9f 07             JumpIfJSReceiver [7](0xf1208293798 @ 42)
         0xf1208293793 @   37 : 65 bf 00 fa 00    CallRuntime [ThrowSymbolIteratorInvalid], r0-r0
         0xf1208293798 @   42 : c0                Star4 
         0xf1208293799 @   43 : 2d f6 01 07       LdaNamedProperty r4, [1], [7]
         0xf120829379d @   47 : c1                Star3 
         0xf120829379e @   48 : 12                LdaFalse 
         0xf120829379f @   49 : bf                Star5 
         0xf12082937a0 @   50 : 19 ff f2          Mov <context>, r8
         0xf12082937a3 @   53 : 0b f5             Ldar r5
         0xf12082937a5 @   55 : 96 21             JumpIfToBooleanTrue [33](0xf12082937c6 @ 88)
         0xf12082937a7 @   57 : 11                LdaTrue 
         0xf12082937a8 @   58 : bf                Star5 
         0xf12082937a9 @   59 : 5d f7 f6 0d       CallProperty0 r3, r4, [13]
         0xf12082937ad @   63 : bb                Star9 
         0xf12082937ae @   64 : 9f 07             JumpIfJSReceiver [7](0xf12082937b5 @ 71)
         0xf12082937b0 @   66 : 65 b7 00 f1 01    CallRuntime [ThrowIteratorResultNotAnObject], r9-r9
         0xf12082937b5 @   71 : 2d f1 02 0b       LdaNamedProperty r9, [2], [11]
         0xf12082937b9 @   75 : 96 0d             JumpIfToBooleanTrue [13](0xf12082937c6 @ 88)
         0xf12082937bb @   77 : 2d f1 03 09       LdaNamedProperty r9, [3], [9]
         0xf12082937bf @   81 : bb                Star9 
         0xf12082937c0 @   82 : 12                LdaFalse 
         0xf12082937c1 @   83 : bf                Star5 
         0xf12082937c2 @   84 : 0b f1             Ldar r9
         0xf12082937c4 @   86 : 8a 03             Jump [3](0xf12082937c7 @ 89)
         0xf12082937c6 @   88 : 0e                LdaUndefined 
         0xf12082937c7 @   89 : c4                Star0 
         0xf12082937c8 @   90 : 0b f5             Ldar r5
         0xf12082937ca @   92 : 96 21             JumpIfToBooleanTrue [33](0xf12082937eb @ 125)
         0xf12082937cc @   94 : 11                LdaTrue 
         0xf12082937cd @   95 : bf                Star5 
         0xf12082937ce @   96 : 5d f7 f6 0f       CallProperty0 r3, r4, [15]
         0xf12082937d2 @  100 : bb                Star9 
         0xf12082937d3 @  101 : 9f 07             JumpIfJSReceiver [7](0xf12082937da @ 108)
         0xf12082937d5 @  103 : 65 b7 00 f1 01    CallRuntime [ThrowIteratorResultNotAnObject], r9-r9
         0xf12082937da @  108 : 2d f1 02 0b       LdaNamedProperty r9, [2], [11]
         0xf12082937de @  112 : 96 0d             JumpIfToBooleanTrue [13](0xf12082937eb @ 125)
         0xf12082937e0 @  114 : 2d f1 03 09       LdaNamedProperty r9, [3], [9]
         0xf12082937e4 @  118 : bb                Star9 
         0xf12082937e5 @  119 : 12                LdaFalse 
         0xf12082937e6 @  120 : bf                Star5 
         0xf12082937e7 @  121 : 0b f1             Ldar r9
         0xf12082937e9 @  123 : 8a 03             Jump [3](0xf12082937ec @ 126)
         0xf12082937eb @  125 : 0e                LdaUndefined 
         0xf12082937ec @  126 : c3                Star1 
         0xf12082937ed @  127 : 0d ff             LdaSmi [-1]
         0xf12082937ef @  129 : bd                Star7 
         0xf12082937f0 @  130 : be                Star6 
         0xf12082937f1 @  131 : 8a 05             Jump [5](0xf12082937f6 @ 136)
         0xf12082937f3 @  133 : bd                Star7 
         0xf12082937f4 @  134 : 0c                LdaZero 
         0xf12082937f5 @  135 : be                Star6 
         0xf12082937f6 @  136 : 10                LdaTheHole 
         0xf12082937f7 @  137 : a6                SetPendingMessage 
         0xf12082937f8 @  138 : bc                Star8 
         0xf12082937f9 @  139 : 0b f5             Ldar r5
         0xf12082937fb @  141 : 96 23             JumpIfToBooleanTrue [35](0xf120829381e @ 176)
         0xf12082937fd @  143 : 19 ff f0          Mov <context>, r10
         0xf1208293800 @  146 : 2d f6 04 11       LdaNamedProperty r4, [4], [17]
         0xf1208293804 @  150 : 9e 1a             JumpIfUndefinedOrNull [26](0xf120829381e @ 176)
         0xf1208293806 @  152 : b9                Star11 
         0xf1208293807 @  153 : 5d ef f6 13       CallProperty0 r11, r4, [19]
         0xf120829380b @  157 : 9f 13             JumpIfJSReceiver [19](0xf120829381e @ 176)
         0xf120829380d @  159 : b8                Star12 
         0xf120829380e @  160 : 65 b7 00 ee 01    CallRuntime [ThrowIteratorResultNotAnObject], r12-r12
         0xf1208293813 @  165 : 8a 0b             Jump [11](0xf120829381e @ 176)
         0xf1208293815 @  167 : ba                Star10 
         0xf1208293816 @  168 : 0c                LdaZero 
         0xf1208293817 @  169 : 1c f4             TestReferenceEqual r6
         0xf1208293819 @  171 : 98 05             JumpIfTrue [5](0xf120829381e @ 176)
         0xf120829381b @  173 : 0b f0             Ldar r10
         0xf120829381d @  175 : a8                ReThrow 
         0xf120829381e @  176 : 0b f2             Ldar r8
         0xf1208293820 @  178 : a6                SetPendingMessage 
         0xf1208293821 @  179 : 0c                LdaZero 
         0xf1208293822 @  180 : 1c f4             TestReferenceEqual r6
         0xf1208293824 @  182 : 99 05             JumpIfFalse [5](0xf1208293829 @ 187)
         0xf1208293826 @  184 : 0b f3             Ldar r7
         0xf1208293828 @  186 : a8                ReThrow 
         0xf1208293829 @  187 : 0e                LdaUndefined 
         0xf120829382a @  188 : a9                Return 
Constant pool (size = 5)
0xf1208293731: [FixedArray] in OldSpace
 - map: 0x0f1208002205 <Map>
 - length: 5
           0: 0x0f12082936fd <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x0f12082936ed <FixedArray[2]>>
           1: 0x0f1208004e65 <String[4]: #next>
           2: 0x0f1208004441 <String[4]: #done>
           3: 0x0f1208005619 <String[5]: #value>
           4: 0x0f12080051dd <String[6]: #return>

Compatibility Needs to Wait a Little Longer

Nullish Operator

The Nullish operator is the "??" operator. If the results are null and undefined, the value after "??" can be taken:

function greet(input) {
  return input ?? "Hello world";
}

It is 9 bytes after being translated into the bytecode:

         0x94c082935da @    0 : 0b 03             Ldar a0
         0x94c082935dc @    2 : 9e 04             JumpIfUndefinedOrNull [4](0x94c082935e0 @ 6)
         0x94c082935de @    4 : 8a 04             Jump [4](0x94c082935e2 @ 8)
         0x94c082935e0 @    6 : 13 00             LdaConstant [0]
         0x94c082935e2 @    8 : a9                Return 

Results after transcoding:

function greet(input) {
  return input !== null && input !== void 0 ? input : "Hello world";
}

It is 15 bytes after being translated into the bytecode:

         0x2a8a082935da @    0 : 0b 03             Ldar a0
         0x2a8a082935dc @    2 : 9a 0a             JumpIfNull [10](0x2a8a082935e6 @ 12)
         0x2a8a082935de @    4 : 0b 03             Ldar a0
         0x2a8a082935e0 @    6 : 9c 06             JumpIfUndefined [6](0x2a8a082935e6 @ 12)
         0x2a8a082935e2 @    8 : 0b 03             Ldar a0
         0x2a8a082935e4 @   10 : 8a 04             Jump [4](0x2a8a082935e8 @ 14)
         0x2a8a082935e6 @   12 : 13 00             LdaConstant [0]
         0x2a8a082935e8 @   14 : a9                Return

The "??" operator is translated into a JumpIfUndefinedOrNull instruction by v8. After transcoding, there is no such treatment. It becomes two instructions, JumpIfNull and JumpIfUndefined.

Therefore, as long as the browser supports it, the Nullish operator is still worth not transcoding.

Involution Operator

Like Nullish, the involution operator is supported by instructions. This saves the overhead of function calls.

let f1 = x => {
  return x ** x;
};

f1(10);

Six bytes are enough because of the Exp instruction:

         0xb75082936be @    0 : 0b 03             Ldar a0
         0xb75082936c0 @    2 : 3e 03 00          Exp a0, [0]
         0xb75082936c3 @    5 : a9                Return

After transcoding, it becomes:

var f1 = function f1(x) {
  return Math.pow(x, x);
};

f1(10);

A 16-byte instruction is required because there is a function call:

        0xb7508293652 @    0 : 21 00 00          LdaGlobal [0], [0]
         0xb7508293655 @    3 : c3                Star1 
         0xb7508293656 @    4 : 2d f9 01 02       LdaNamedProperty r1, [1], [2]
         0xb750829365a @    8 : c4                Star0 
         0xb750829365b @    9 : 5f fa f9 03 03 04 CallProperty2 r0, r1, a0, a0, [4]
         0xb7508293661 @   15 : a9                Return 
Constant pool (size = 2)
0xb7508293621: [FixedArray] in OldSpace
 - map: 0x0b7508002205 <Map>
 - length: 2
           0: 0x0b75082028e5 <String[4]: #Math>
           1: 0x0b7508202aa1 <String[3]: #pow>

JSX

In one case, it is impossible to use native code and necessary to be transcoded. This is the situation of React JSX.

Transcoding is required, but the amount of code brought by different goals is also significantly different.

For example, take a look at the following code, which is a typical React Hooks usage:

import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

We transcode according to iOS 9:

const babel = require("@babel/core");
const generate = require("@babel/generator");

    let result3 = babel.transformSync(code, {
        targets: "iOS 9",
        sourceMaps: true,
        presets: ["@babel/preset-env", "@babel/preset-react"]
    });
    let str1 = result3.code;
    console.log(str1);

The transcoding result is listed below:

...
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function Example() {
  var _useState = (0, _react.useState)(0),
      _useState2 = _slicedToArray(_useState, 2),
      count = _useState2[0],
      setCount = _useState2[1];

  return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("p", null, "You clicked ", count, " times"), /*#__PURE__*/_react.default.createElement("button", {
    onClick: function onClick() {
      return setCount(count + 1);
    }
  }, "Click me"));
}

However, transcoding aimed at iOS 15 does not need to generate much js code because deconstruction is supported:

function Example() {
    const [count, setCount] = (0, _react.useState)(0);
    return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("p", null, "You clicked ", count, " times"), /*#__PURE__*/_react.default.createElement("button", {
        onClick: () => setCount(count + 1)
    }, "Click me"));
}

Summary

In the preceding cases (except for deconstruction that introduces iteration), it will become complex. In most cases, it is more conducive to v8 to improve performance without transcoding from the two aspects of source code and bytecode. In particular, simple transcoding is not enough. It depends on the feature of polyfill runtime, which is not cost-effective in terms of code library size and running speed. It is worth using new tools to achieve better results, specifically the mid-to-high-end machines in recent years.

0 0 0
Share on

Alibaba F(x) Team

66 posts | 3 followers

You may also like

Comments