ecmascript 6 features(pdf 版)
TRANSCRIPT
ECMAScript 6 Features2015/06/06
2015/06/30 改訂
taskie
1 / 85
このスライドについて
ECMAScript 6 の新機能を紹介するスライドです仕様書:ECMAScript 2015 Language Specification ‒ ECMA-262 6thEdition
処理系の対応状況は ECMAScript 6 compatibility table を参考に章立ておよび紹介の順序はこの表に従う
全機能を網羅しているわけではない基本的に ES5 は ES3 の、ES6 は ES3, ES5 の上位互換
ES3, ES5 の機能は引き続き ES6 でも使えると思ってよい"use strict"; で動かなくなる構文( with 文など)は避けるべき
2 / 85
TL;DR(抜粋)
オブジェクトリテラルの略記 ( {[x]: a} , {a, b} , { f(x) { ... } } )列挙 ( for (var x of xs) )テンプレートリテラル ( `${x} + ${y} = ${x + y}` )分割代入 ( [x, y] = xs , {a, b} = obj )ブロックスコープ変数 ( let ) , 定数 ( const )アロー関数 ( (x, y) => { ... } )クラス ( class , extends , super )ジェネレータ ( function * , yield )Promise
3 / 85
目次
最適化 (Optimization)文法 (Syntax)束縛 (Bindings)関数 (Functions)ビルトイン (Built-ins)ビルトイン拡張 (Built-in extensions)サブクラス化 (Subclassing)モジュール (Modules)
4 / 85
最適化 (Optimization)
末尾呼び出し最適化
5 / 85
末尾呼び出し最適化
ES6
function gcd(m, n) { if (n === 0) { return m; } else { return gcd(n, m % n); }}
ES6 処理系では関数末尾での再帰呼び出しが最適化される(はず)参考:BabelとTraceurでES6末尾再帰最適化を試す - teppeis blog
6 / 85
文法 (Syntax)
デフォルト引数 ( function (x, y = 42) )可変長引数 ( function (x, ...ys) )配列の展開 ( f(...xs) , [x, ...ys, z] )オブジェクトリテラルの略記 ( {[x]: a} , {a, b} , { f(x) { ... } } )2進数 / 8進数リテラル ( 0b1010 , 0o755 )列挙 ( for (var x of xs) )テンプレートリテラル ( `${x} + ${y} = ${x + y}` )新しい正規表現のフラグ ( /.../y , /🍣🍺 *🍖 /u )分割代入 ( [x, y] = xs , {a, b} = obj )
7 / 85
デフォルト引数
ES3 / ES5
function myPow(x, y) { if (typeof y === "undefined") y = 2; return Math.pow(x, y);}console.log(myPow(3)); // 9console.log(myPow(2, 10)); // 1024
8 / 85
デフォルト引数
ES6
function myPow(x, y = 2) {
return Math.pow(x, y);}console.log(myPow(3)); // 9console.log(myPow(2, 10)); // 1024
「デフォルト値を持つ引数」の後に「デフォルト引数を持たない引数」を置いてはならない
function (x = 2, y) はダメ
9 / 85
可変長引数
ES3 / ES5
function f(x) { console.log(x, Array.prototype.slice.call(arguments, 1));}f(2, 3, 5); // 2 [ 3, 5 ]
arguments は Array ではないarguments を Array に変換するややこしいイディオムが存在する
10 / 85
可変長引数
ES6
function f(x, ...ys) { console.log(x, ys);}f(2, 3, 5); // 2 [ 3, 5 ]
ちなみに Array.from(arguments) で配列に変換することもできる
11 / 85
配列の展開(引数編)
ES3 / ES5
function f(x, y, z) { console.log(x + y * z);}f.apply(null, [2, 3, 5]); // 17
12 / 85
配列の展開(引数編)
ES6
function f(x, y, z) { console.log(x + y * z);}f(...[2, 3, 5]); // 17
13 / 85
配列の展開(配列リテラル編)
ES3 / ES5
var xs = [5, 7];var ys = [2, 3].concat(xs, [11, 13])console.log(ys); // [ 2, 3, 5, 7, 11, 13 ]
14 / 85
配列の展開(配列リテラル編)
ES6
var xs = [5, 7];var ys = [2, 3, ...xs, 11, 13];console.log(ys); // [ 2, 3, 5, 7, 11, 13 ]
15 / 85
式をプロパティ名に使う
ES3 / ES5
var key = "foo";var obj = {};obj[key] = "bar";console.log(obj); // { "foo" : "bar" }
16 / 85
式をプロパティ名に使う
ES6
var key = "foo";var obj = {[key]: "bar"};
console.log(obj); // { "foo" : "bar" }
17 / 85
プロパティ名の略記
ES3 / ES5
var x = 2, y = 3, z = 5;var obj = {x: x, y: y, z: z};
18 / 85
プロパティ名の略記
ES6
var x = 2, y = 3, z = 5;var obj = {x, y, z};
変数名とプロパティ名が同じ場合に略記できる
19 / 85
メソッドの略記
ES3 / ES5
var obj = { f: function (x) { console.log(x * this.y); }, y: 42};
20 / 85
メソッドの略記
ES6
var obj = { f(x) { console.log(x * this.y); }, y: 42 };
21 / 85
列挙 ( for ‒ of )
ES3 (BAD)
var xs = [2, 3, 5];for (var i in xs) { console.log(xs[i]);}
javascript - Why is using "for...in" with array iteration such a bad idea?- Stack Overflow
var xs = []; xs[5] = 42; のようなパターンでアウトArray.prototype を弄るわるいライブラリがいるとアウト
22 / 85
列挙 ( for ‒ of )
ES3 (GOOD)
var xs = [2, 3, 5];for (var i = 0; i < xs.length; ++i) { console.log(xs[i]);}
23 / 85
列挙 ( for ‒ of )
ES5
var xs = [2, 3, 5];xs.forEach(function (x, i) { console.log(x);});
Array.prototype.forEach を使うコールバック関数の第 2 引数には添字が入っている
24 / 85
列挙 ( for ‒ of )
ES6
var xs = [2, 3, 5];for (var x of xs) { console.log(x);}
配列だけでなくジェネレータ(後述)なども列挙できるSymbol.iterator (後述)を利用すれば独自のオブジェクトも列挙できる
25 / 85
2進数 / 8進数リテラル
ES6
console.log(0b1010); // 10console.log(0o755); // 493
b / o は小文字でも大文字でも可
26 / 85
テンプレートリテラル
ES3 / ES5
var a = 7, b = 8;console.log(a + " + " + b + " = " + (a + b));
27 / 85
テンプレートリテラル
ES6
var a = 7, b = 8;console.log(`${a} + ${b} = ${a + b}`);
28 / 85
新しい正規表現のフラグ ( y , u )
ES6
var re = /(\d+)\.?/y;var ip = "127.0.0.1";while (re.exec(ip)) console.log(re.lastIndex); // 4 6 8 9
sticky flag ( lastIndex から順次検索)
console.log(/🍣🍺 *🍖 /.test("🍣🍺🍺🍺🍖 ")); // false
console.log(/🍣🍺 *🍖 /u.test("🍣🍺🍺🍺🍖 ")); // true
参考:Unicode-aware regular expressions in ECMAScript 6 · MathiasBynens
29 / 85
分割代入
ES3 / ES5
var xs = [2, 3, 5];var x = xs[0], y = xs[1], z = xs[2];
var obj = {x: 2, y: 3, nested: {z: 5}};var x = obj.x, y = obj.y, z = obj.nested.z;
var obj = {x: 2, y: 3, nested: {z: 5}};var a = obj.x, b = obj.y, c = obj.nested.z;
30 / 85
分割代入
ES6
var xs = [2, 3, 5];var [x, y, z] = xs;
var obj = {x: 2, y: 3, nested: {z: 5}};var {x, y, nested: {z}} = obj;
var obj = {x: 2, y: 3, nested: {z: 5}};var {x: a, y: b, nested: {z: c}} = obj;
xs , obj が 1 回しか登場しないのが利点関数の戻り値を扱う場合一時変数に代入する必要がない変数名が長い場合にもアドバンテージ
31 / 85
束縛 (Bindings)
ブロックスコープ変数 ( let )定数 ( const )
32 / 85
ブロックスコープ変数 ( let )
ES3 / ES5 (BAD)
var s = "foo";{ var s = "bar"; console.log(s); // bar}console.log(s); // bar
ES5 以前の JavaScript には関数スコープしか存在しない
33 / 85
ブロックスコープ変数 ( let )
ES3 / ES5 (GOOD)
var s = "foo";(function () { var s = "bar"; console.log(s); // bar})();console.log(s); // foo
関数の即時実行でスコープを実現する
34 / 85
ブロックスコープ変数 ( let )
ES6
var s = "foo";{ let s = "bar"; console.log(s); // bar}console.log(s); // foo
let で変数を宣言するとブロックスコープを持つ変数になる
35 / 85
let と for
ES3 / ES5 (BAD)
var fs = [];for (var i = 0; i < 3; ++i) { var f = function () { console.log(i); }; fs.push(f);}for (var i = 0; i < 3; ++i) { fs[i](); // 3, 3, 3}
クロージャが同じ i を参照している
36 / 85
let と for
ES3 / ES5 (GOOD)
var fs = [];for (var i = 0; i < 3; ++i) { var f = (function (i) { return function () { console.log(i); }; })(i); fs.push(f);}for (var i = 0; i < 3; ++i) { fs[i](); // 0, 1, 2}
関数の即時実行でスコープを切る
37 / 85
let と for
ES6
var fs = [];for (let i = 0; i < 3; ++i) { var f = function () { console.log(i); }; fs.push(f);}for (var i = 0; i < 3; ++i) { fs[i](); // 0, 1, 2}
for の初期化節で let を使う
38 / 85
定数 ( const )
ES6
const answer = 42;answer = 0; // compile error
代入禁止
const s = "foo";{ const s = "bar"; console.log(s); // bar}console.log(s); // foo
const もブロックスコープを持つ
39 / 85
関数 (Functions)
アロー関数 ( (x, y) => { ... } )クラス ( class , extends , super )ジェネレータ ( function * , yield )
40 / 85
アロー関数
ES3 / ES5
var myRandom = function (x, y) { var range = y - x; return x + (Math.random() * range);};var pow2 = function (x) { return x * x };
41 / 85
アロー関数
ES6
var myRandom = (x, y) => { var range = y - x; return x + (Math.random() * range);};var pow2 = x => x * x;
引数が 1 個の場合 (x) => { ... } を x => { ... } と略記可(foo) => { return bar; } を (foo) => bar と略記可アロー記法と function 記法では this の扱いについて違いがある
42 / 85
アロー関数( this の扱い)
ES3 / ES5 (BAD)
var obj = { f: function () {
setTimeout(function () { console.log(this.x); }, 1000); }, x: 42};obj.f(); // undefined
this !== obj となってしまう
43 / 85
アロー関数( this の扱い)
ES3 (GOOD)
var obj = { f: function () { var that = this; setTimeout(function () { console.log(that.x); }, 1000); }, x: 42};obj.f(); // 42
this に別名を付ける
44 / 85
アロー関数( this の扱い)
ES5 (GOOD)
var obj = { f: function () {
setTimeout((function () { console.log(this.x); }).bind(this), 1000); }, x: 42};obj.f(); // 42
Function.prototype.bind を使う
45 / 85
アロー関数( this の扱い)
ES6
var obj = { f: function () {
setTimeout(() => { console.log(this.x); }, 1000); }, x: 42};obj.f(); // 42
アロー記法の場合関数外の this を関数内でも使える ( this === obj )クラスを使う際に特に威力を発揮する
46 / 85
クラス ( class )
ES3 / ES5
JavaScript はプロトタイプベースのオブジェクト指向言語であり、ES5 以前にクラスは存在しない
class という単語自体は古くから予約語だったりするES5 以前でもクラスや継承を実現するパターンは存在する
CoffeescriptとTypescriptから学ぶjsでのクラス・継承パターン | WEBEGG
47 / 85
クラス ( class )
ES6
class Application { constructor(name) { this.name = name; } start() { document.addEventListener("DOMContentLoaded", (event) => { this.domDidLoad(event); }); } domDidLoad(event) { console.log(event); console.log(this); console.log(`Application ${this.name} start...`); }}
48 / 85
クラスの継承 ( extends , super )
ES6
class MyApplication extends Application { constructor(name, canvasId, fps = 60) { super(name); this.canvasId = canvasId; this.fps = fps; } domDidLoad(event) { super.domDidLoad(event); this.canvas = document.getElementById(this.canvasId); this.context = this.canvas.getContext("2d"); setTimeout(() => this.draw(this.context), 1000 / this.fps); } draw(ctx) { ctx.fillStyle = "#def"; ctx.fillRect(0, 0, 640, 480); }}
49 / 85
クラス( this の扱い)
ES6 (GOOD)
document.addEventListener("DOMContentLoaded", (event) => { this.domDidLoad(event);});
setTimeout(() => this.draw(this.context), 1000 / this.fps);
クラスのコードより抜粋アロー関数大活躍
50 / 85
クラス( this の扱い)
ES6 (BAD)
document.addEventListener("DOMContentLoaded", function (event) { this.domDidLoad(event);});
setTimeout(function () { this.draw(this.context); }, 1000 / this.fps);
function でこう書くことはできない
51 / 85
クラス( this の扱い)
ES6 (BAD)
document.addEventListener("DOMContentLoaded", this.domDidLoad);
こう書くこともできない
52 / 85
クラス( this の扱い)
ES6 (GOOD) (2)
document.addEventListener("DOMContentLoaded", this.domDidLoad.bind(this));
setTimeout(this.draw.bind(this, this.context), 1000 / this.fps);
Function.prototype.bind を使う場合
53 / 85
ジェネレータ
ES6
function * range(n) { for (var i = 0; i < n; ++i) yield i;}
const gen = range(3);console.log(gen.next()); // { value: 0, done: false }console.log(gen.next()); // { value: 1, done: false }console.log(gen.next()); // { value: 2, done: false }console.log(gen.next()); // { value: undefined, done: true }
function * と yield で中断できる関数を作ることができるいわゆる coroutine
54 / 85
ジェネレータの列挙
ES6
function * range(n) { for (var i = 0; i < n; ++i) yield i;}
for (var i of range(3)) { console.log(i); // 0, 1, 2}
for ‒ of でジェネレータの値を列挙できる
55 / 85
ジェネレータの展開
ES6
function * range(n) { for (var i = 0; i < n; ++i) yield i;}
console.log([...range(2), ...range(3)]); // [ 0, 1, 0, 1, 2 ]
... でジェネレータを展開できる
56 / 85
ビルトイン (Built-ins)
型付き配列 ( Uint8Array , Float32Array , ..., DataView )Map , WeakMap , Set , WeakSet
Proxy , Reflect
Promise
Symbol
57 / 85
型付き配列
ES6
var xs = new Float32Array(3);console.log(xs); // [ 0, 0, 0 ]var ys = new Uint8Array([-1, 0, 255, 256]);console.log(ys); // [ 255, 0, 255, 0 ]var zs = new Uint8ClampedArray([-1, 0, 255, 256]);console.log(zs); // [ 0, 0, 255, 255 ]
XHR で取得したバイナリを扱う際に有用WebGL を使う際にも利用するnew の際に配列長か Array か ArrayBuffer を与える
58 / 85
ArrayBuffer と型付き配列
ES6
var buf = new ArrayBuffer(8);var f64 = new Float64Array(buf);var i32 = new Int32Array(buf);var ui8 = new Uint8Array(buf);f64[0] = 0.1;console.log(f64); // [0.1]console.log(i32); // [-1717986918, 1069128089]console.log(ui8); // [154, 153, 153, 153, 153, 153, 185, 63]
型付き配列は view(窓)に過ぎず、実体は ArrayBuffer であるArrayBuffer を共有すると内容も共有される
59 / 85
DataView
ES6
var buf = new ArrayBuffer(4);var view = new DataView(buf);view.setUint8(0, 0xA0);console.log(view.getInt32(0)); // -1610612736console.log(view.getInt32(0, true)); // 160
C の構造体のような複合データを扱う際には DataView を用いるとよいget 系関数の第 2 引数、set 系関数の第 3 引数にはエンディアンを指定する
デフォルトはビッグエンディアンリトルエンディアンなら true を指定
60 / 85
Map
ES6
var map = new Map();var key1 = {};var key2 = {};var key3 = key1;map.set(key1, "foo");map.set(key2, "bar");console.log(map.get(key2)); // barconsole.log(map.get(key3)); // foofor (var key of map.keys()) console.log(key); // {} {}
いわゆる辞書型キーにオブジェクトを使うことができる
61 / 85
WeakMap
ES6
var map = new WeakMap();var key1 = {};var key2 = {};var key3 = key1;map.set(key1, "foo");map.set(key2, "bar");console.log(map.get(key2)); // barconsole.log(map.get(key3)); // fooconsole.log(typeof map.keys === "undefined"); // true
キーを弱参照で持つ Map
キーを列挙することはできない
62 / 85
Set
ES6
var set = new Set([1, 1, 2, 3, 5, 8]);set.add(2).add(3).add(5).add(7).add(11);console.log(set); // [ 1, 2, 3, 5, 8, 7, 11 ]console.log(set.has(4)); // falseconsole.log(set.has(5)); // true
いわゆる集合型列挙は挿入順
63 / 85
WeakSet
ES6
var set = new WeakSet();var key1 = {};var key2 = {};var key3 = key1;set.add(key1);console.log(set.has(key2)); // falseconsole.log(set.has(key3)); // true
キー(値)を弱参照で持つ Set
WeakMap と同じく列挙不可
64 / 85
Proxy
ES6
var obj = {};var handler = { get(target, name) { return target[name] / 2; }, set(target, name, val) { target[name] = val * 2; }};var proxy1 = new Proxy(obj, handler);var proxy2 = new Proxy(proxy1, handler);var proxy3 = new Proxy(proxy2, handler);proxy3.foo = 42;console.log(proxy3.foo); // 42console.log(proxy2.foo); // 84console.log(proxy1.foo); // 168console.log(obj); // { "foo" : 336 }
メタプログラミングに使えるらしい参考:Meta programming with ECMAScript 6 proxies
65 / 85
Reflect
ES6
var obj = {};var handler = { get(target, name) { return Reflect.get(target, name) / 2; }, set(target, name, val) { Reflect.set(target, name, val * 2); }};var proxy1 = new Proxy(obj, handler);var proxy2 = new Proxy(proxy1, handler);var proxy3 = new Proxy(proxy2, handler);proxy3.foo = 42;console.log(proxy3.foo); // 42console.log(proxy2.foo); // 42console.log(proxy1.foo); // 42console.log(obj); // { "foo" : 84 }
参考:ecmascript 6 - What does the Reflect object do in JavaScript? -Stack Overflow
66 / 85
Promise
ES6
非同期処理をうまく扱うための仕組み参考:JavaScript Promiseの本then のメソッドチェーンで処理順を制御し、 catch でエラー処理をする非同期処理を行うライブラリを作る際には Promise オブジェクトを返す APIを用意しておくとよいpetkaantonov/bluebird などの polyfill が存在
67 / 85
Promise
ES6
new Promise((resolve, reject) => { setTimeout(() => resolve("A"), 1000);}).then((str) => { console.log(str); // A return new Promise((resolve, reject) => { setTimeout(() => resolve("B"), 1000); }).then((str) => { console.log(str); // B return Promise.reject(new Error("C")); });}).catch((err) => { console.log(err.message); // C return "D";}).then((str) => { console.log(str); // D});console.log("Start!");
Start! → A → B → C → D の順に表示される
68 / 85
Symbol
ES6
var sym1 = Symbol("foo");var sym2 = Symbol("bar");var sym3 = Symbol("bar");
console.log(sym1 == sym2); // falseconsole.log(sym2 == sym3); // falseconsole.log(sym3 == sym1); // false
一意なキーの生成などに利用参考:Symbolについて - JS.next
69 / 85
Symbol.iterator
ES6
var obj = { };obj[Symbol.iterator] = function () { return { next: function () { return (this.i < 3) ? {done: false, value: this.i++} : {done: true}; }, i: 0 }};for (var x of obj) console.log(x); // 0 1 2
自前のオブジェクトを for ‒ of で列挙できる参考:ES6のイテレータについて調べた - ひよこ3分07秒のTechブログ
70 / 85
ビルトイン拡張 (Built-inextensions)
Oject.assign
Object.setPrototypeOf
関数名の取得String
Array
Number
Math
71 / 85
Object.assign
ES6
var obj = {foo: "bar", x: 42};Object.assign(obj, {foo: "baz"}, {hoge: "piyo"});console.log(obj); // { "foo" : "baz", "x" : 42 , "hoge" : "piyo" }
オブジェクトのマージ
72 / 85
Object.setPrototypeOf
ES6
var proto = {hoge: "piyo"};var obj = {foo: "bar"};Object.setPrototypeOf(obj, proto);console.log(Object.getPrototypeOf(obj) === proto); // trueconsole.log(obj.foo); // barconsole.log(obj.hoge); // piyoobj.hoge = "fuga";console.log(obj.hoge); // fuga
オブジェクトのプロトタイプを設定するいわゆる __proto__ の代替として利用可
73 / 85
関数名の取得
ES6
function foo() { }console.log(foo.name); // foovar bar = function () { };console.log(bar.name); // barconsole.log((function () { }).name === "") // true
name プロパティで関数名を取得できる
74 / 85
String
ES6
console.log("🍣 ".codePointAt(0)); // 127843
console.log(String.fromCodePoint(12354, 32, 127843)); // あ 🍣console.log("A".repeat(3)); // AAAconsole.log("heart".includes("ear")); // trueconsole.log("heart".startsWith("hear")); // trueconsole.log("heart".startsWith("ear", 1)); // trueconsole.log("heart".endsWith("art")); // true
75 / 85
Array
ES6
var obj = {0: "foo", 1: "bar", length: 2};console.log(Array.from(obj)); // ["foo", "bar"]function * gen() { yield 2; yield 3; yield 5; }console.log(Array.from(gen())); // [2, 3, 5]console.log([2, 3, 5, 7].find(x => x > 3)); // 5console.log([2, 3, 5, 7].findIndex(x => x > 3)); // 2console.log(new Array(3).fill(42)); // [42, 42, 42]
from : 「配列のような」オブジェクトを配列に変換find : 条件に一致する最初の要素を得るfindIndex : 条件に一致する最初の要素の添字を得る
76 / 85
Number
ES6
console.log(Number.isFinite(42)); // trueconsole.log(Number.isFinite(-Infinity)); // falseconsole.log(Number.isFinite(NaN)); // falseconsole.log(Number.isInteger(42.00000)); // trueconsole.log(Number.isInteger(42.00001)); // falseconsole.log(Number.isSafeInteger(1e+15)); // trueconsole.log(Number.isSafeInteger(1e+16)); // falseconsole.log(Number.EPSILON); // 2.220446049250313e-16console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991console.log((Number.MIN_SAFE_INTEGER - 1) | 0); // 0console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991console.log((Number.MAX_SAFE_INTEGER + 1) | 0); // 0
77 / 85
Math
ES6
console.log(Math.sign(-42)); // -1console.log(Math.cosh(Math.log(2))); // 1.25console.log(Math.trunc(-3.5)); // -3console.log(Math.cbrt(27)); // 3console.log(Math.hypot(3, 4)); // 5
sign : 符号(-1, 0, +1)cosh , sinh , tanh : 双曲線関数trunc : 0 方向への丸めcbrt : 立方根hypot : 二乗和の平方根
78 / 85
サブクラス化 (Subclassing)
Array , RegExp , Function , Promise , Boolean , Number , String ,Map , Set を継承したクラスを定義できる
79 / 85
モジュール (Modules)
import , export
80 / 85
import , export
CommonJS
// app.jsvar utils = require("./utils");utils.hello(); // Hello, world!console.log(utils.answer); // 42
// utils.jsfunction hello() { console.log("Hello, world!"); }var answer = 42;module.exports = { hello: hello, answer: answer };
81 / 85
import , export
ES6
// app.jsimport utils from "./utils";utils.hello(); // Hello, world!console.log(utils.answer); // 42
// utils.jsfunction hello() { console.log("Hello, world!"); }var answer = 42;export default { hello: hello, answer: answer };
クラスなども export 可能参考:Babelで理解するEcmaScript6の import / export - Qiita
82 / 85
import * as
ES6
// app.jsimport * as utils from "./utils";utils.hello(); // Hello, world!console.log(utils.answer); // 42
// utils.jsexport function hello() { console.log("Hello, world!"); }export var answer = 42;
83 / 85
import { ... }
ES6
// app.jsimport { hello, answer } from "./utils";hello(); // Hello, world!console.log(answer); // 42
// utils.jsexport function hello() { console.log("Hello, world!"); }export var answer = 42;
84 / 85