By Xu Lun, F(X)Team, Ali Tao Department

In order to be compatible with older browsers, especially IE series, front-end code using ES6 or above specifications is often transcoded into ES5 code using transcoding tools such as Babel.

It’s been six years since the release of ES6 in 2015. How compatible are browsers with ES6?

Let’s take a look at CanIUse’s data:

As you can see, 98.14% of browsers support ES6. The reason why it doesn’t exceed 99% is that Opera Mini, released in 2015, still has 1.08% usage. For mobile, Safari on iOS and Chrome released after 2016 all support ES6. Safari on iOS 7-9.3 currently accounts for 0.15% of users. Android WebView has fully supported ES6 since version 5.

The fact that more than 99 percent of the device’s capacity is not being used because of a small number of older devices does not seem statistically convincing. In addition, many applications have special processing for low-end machines, and high-end machines must be the recent old equipment. At least for mid – and high-end machines, the compatibility of transcoding is essentially negligible.

However, ES6 and above are made up of multiple features and cannot be abstracted as if 6 is better than 5. We will be the main function transcoding and not transcoding to do a comparison.

No transcoding is better

const

Const is checked with a constant. Here’s an example:

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

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();
Copy the code

This need not look at the bytecode, look at the source code to know which is better.

Copy an array

After ES6, we do array copy using the extension operator “…” .

  const a1 = [1.2.3.4.5.6.7.8.9.10];
  let a2 = [...a1];
Copy the code

Babel does transcoding without ambiguity, a concat function does it:

  var a1 = [1.2.3.4.5.6.7.8.9.10];
  var a2 = [].concat(a1);
Copy the code

From a bytecode perspective, however, things are different. Because V8 provides the CreateArrayFromIterable directive. So, before transcoding, the 9-byte instruction is done:

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]>>
Copy the code

After the transcoding comes the function call, and generating an empty array takes 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>
Copy the code

String.raw

For String.raw, transcoding also generates extra functions. For example, before transcoding, it is like this:

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

f1();
Copy the code

After transcoding, Babel helps us generate a _taggedTemplateLiteral function:

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();Copy the code

Symbol

Symbol is the new data type in ES6. In ES6, to determine its type, we simply use the Typeof operator.

let f2 = () = > {
  let s1 = Symbol(a);return typeof s1;
};
Copy the code

Babel has to introduce a library to solve this problem:

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(a);return _typeof(s1);
};
Copy the code

Rest parameters

To support REST parameters, V8 provides the CreateRestParameter directive. However, the original arguments are supported by the CreateMappedArguments directive. It’s a draw.

However, from a source code point of view, non-transcoding is shorter:

let f1 = (. values) = > {
    let sum = 0;
    for (let v of values) {
        sum += v;
    }
    return sum;
};
f1(1.4.9);
Copy the code

Transcoding is as follows:

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;
};
Copy the code

Optional catch parameter

This is a feature of ES2019 to omit error types in catches. Safari will be available in the first half of 2018.

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

After transcoding, Babel will give us an unused error variable _unused:

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

V8 generates a CatchContext for us via 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>
Copy the code

CatchContext is not generated when the catch argument is optional:

         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>
Copy the code

Generator

Using iterators in this way is the most efficient part of transcoding in the next section. However, explicit use of iterators for generators is a different story.

Let’s look at the simplest Generator where we generate just a few numbers:

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

We can see that the transcoding result not only defines several functions, but also requires the support of the regeneratorRuntime runtime:

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 and 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);
};
Copy the code

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

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

Since the runtime is not imported, we need to add a library.

npm install --save @babel/polyfill
Copy the code

Then import the library:

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

I’ll leave it to the reader to figure out how much code this adds.

class

Although class is essentially a syntactically equivalent to Function, Babel transcodes generate more code than most of you might think. Let’s look at a simple example:

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

After transcoding, Babel generates _createClass,_classCallCheck, and _defineProperties for us:

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(! (instanceinstanceof 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");
Copy the code

Built-in objects that rely on Polyfill and Runtime

As we know, ES6 has a number of new objects and new properties for existing objects.

For example, if you use a newly added Set, Map, WeakSet, WeakMap object, or a new method like Number. IsNaN, Babel does not help us transcode into ES5 statements. So how are these statements supported in older browsers? The answer, as you might have guessed, is the @babel/ Polyfill library we used for Generator.

The Babel Polyfill library is actually based on two open source libraries:

  • One is Facebook’s ReGenerator library, which currently runs as a 700-plus line file: ReGenerator Runtime.
  • The other is the core-JS library, which provides most of the built-in object support.

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));
Copy the code

Babel/ Runtime actually does something 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));
Copy the code

Transcoding is better

There are special cases for everything, and for some functions, transcoding may be better.

Deconstruction assignment

We quote the example of teacher Ruan Yifeng’s variable exchange:

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

f1();
Copy the code

After transcoding, it looks like this:

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

f1();
Copy the code

Let’s first 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
Copy the code

How many bytes does the more concise deconstruction above require? The answer is 189 bytes because iterators are involved:

         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>
Copy the code

Compatibility will have to wait

Nullish operator.

The Nullish operator is “??” Operator, “??” if null and undefined. The following values:

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

The bytecode translation is 9 bytes:

         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 
Copy the code

Transcoding results:

function greet(input) {
  returninput ! = =null&& input ! = =void 0 ? input : "Hello world";
}
Copy the code

15 bytes after translation to 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
Copy the code

“??” The operator was translated by V8 into the JumpIfUndefinedOrNull directive, which is no longer the case, becoming JumpIfNull and JumpIfUndefined.

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

Power operator

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

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

f1(10);
Copy the code

Because of the Exp instruction, it takes 6 bytes:

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

After transcoding, it becomes:

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

f1(10);
Copy the code

Since there are function calls, 16 bytes of instruction are required:

         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>
Copy the code

JSX

There’s one situation where you can’t use native code and have to transcode, and that’s how React JSX works.

Although transcoding is required, the amount of code generated by different targets can vary significantly.

For example, this code is typical of React Hooks:

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>
  );
}
Copy the code

Let’s transcode by 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);
Copy the code

The transcoding result is as follows:

.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 and 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"));
}
Copy the code

For iOS 15 transcoding, there is no need to generate as much JS code since destructuring 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"));
}
Copy the code

summary

As we can see from the previous examples, aside from the complexity of structures such as deconstruction introducing iteration, in most cases v8 is better off without transcoding, both from source code and bytecode perspectives. In particular, it is not simply transcoding and relies on the capabilities of the Polyfill runtime, which is not cost-effective in terms of both the size and speed of the code base. At least for mid-range and high-end models in recent years, it’s worth using new weapons to get better results.