17
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

babelでlet a = () => {} が let a = function a(){}と匿名関数に名前が付けられるのはなんでなんでなの?というのを調べた

Last updated at Posted at 2016-06-01

発端

let hoge = (x) => x + 1
がbabelで
var hoge = function hoge(x) {x + 1;}
的にコンパイルされるので、無名関数じゃないじゃん!なんで!と駄々こねて同僚の業務時間奪ってしまった。

という話をされて、「なるほど、まあ確かに。」と思ったので調べた。

結論

  • arrow-functionのそのものの機能 / 仕様ではない。
  • function.nameのための変換によって変換がされてるっぽい

おためし

準備としてこんなコードを用意

let a = () => {}       // arrow function
let b = function(){}   // 普通のfunction(無名)
let c = function d(){} // 名前付きfunction

babel-preset-es2015でcompile

わりといつもやるようなやり方。

% $(npm bin)/babel foo.js --presets es2015               
"use strict";

var a = function a() {};
var b = function b() {};
var c = function d() {};

確かに。無名関数に名前がつけられた。

transform-es2015-arrow-functionだけ使う

babelは各機能毎に使えるので、arrow-functionだけ利用してみると、ちょっと面白い事になった

% $(npm bin)/babel foo.js --plugins transform-es2015-arrow-functions                        
let a = function () {}; // まだ無名!
let b = function () {};
let c = function d() {};

関数に変換されるだけで名前はつけられてない状態になった。

transform-es2015-function-nameだけ使う

で、問題のfunction.nameの話。

% $(npm bin)/babel foo.js --plugins transform-es2015-function-name                          
let a = () => {};
let b = function b() {}; // 名前がついた!
let c = function d() {};

arrow functionは当然変換されないのだが、bに注目してみると、function b()と名前がついているのが確認出来る。この2つのtransformによってarrow functionに名前が付くようになっていることがわかる。

このふたつをどっちも使う

やるまでもないけど、一応

% $(npm bin)/babel foo.js --plugins transform-es2015-function-name,transform-es2015-arrow-functions
let a = function a() {};
let b = function b() {};
let c = function d() {};

function.nameとは?

function.nameプロパティは、ざっくり言えば、関数の名前を取得する機能になる。

// Function Statements
function fn () {}
console.log(fn.name) // "fn"

// variables
const a = function(){}
console.log(a.name) // "a"
const b = () => {}
console.log(b.name) // "b"

ここで、各環境でのサポート具合がわかる Compatibility table を参考にすると、それなりにこの機能をサポートしてないブラウザが多い。

サポートしてないブラウザの場合は、core-jsがpolyfillしてくれている。
そこで更にcore-jsが何をやっているのかを見ると、だいたいこんな感じで処理している。
(core-jsのソースが読みづらい場合はStack Overflowの類似記事らへんを眺めると良いかもしれない)

  1. functionをstringに変換して、関数の内容を文字列として取得する('' + that)1
  2. /^\s*function ([^ (]*)/という正規表現で、名前の部分を取得する
  3. 取得できた値を、Function.prototype.nameで返せるようにしている。

transform-es2015-function-namelet a = function(){}let a = function a(){}にしてくれることによって、core-jsがここまで処理を出来る、といった具合になる。

余談:じゃあfunction.nameって何に使えるのか?

主観だが、基本的にはデバッグ向けぐらいじゃないかなーと思っている。

本格的に使わなければならない状況に陥ったとすると、無理のある実装をしている可能性を疑うだろう。

ただし、例えばデバッグの場合だったら、こんな場合とかで使えるだろう(あんまりいいコードの例が思いつかなかった)

function hogeFunctionGenerator(someFlag, defaultCallback){
  if(someFlag){
    return function someFlagFn(){}
  }
  return defaultCallback
}

// どの関数が結局コールされるのか知りたい場合
console.log(hogeFunctionGenerator(true, function (){}).name) // "someFlagFn"
console.log(hogeFunctionGenerator(false, function (){}).name) // ""
console.log(hogeFunctionGenerator(false, function cb(){}).name) // "cb"

余談のおまけ:bableにおいてclassのconstructorではどうなるか?

function.nameを利用して、classがどのクラスなのかというのを判定することをc.consturctor.nameを呼び出すことでが出来るということがこちらにて紹介されていた。

ただし、この場合で深めの使い方をすると、babelではなかなかうまくいかなそうだ。

class Hoge {}

function createClassZ() {
  // Z classを返す
  return class Z{ 
    constructor(){
    }
  }
}
function createAnonymousClass() {
  // 匿名classを返す
  return class { 
    constructor(){
    }
  }
}
let ZX = createClassZ()
let zx = new ZX()

let X = createAnonymousClass()
let x = new X()

console.log("zx", zx.constructor.name)
console.log("x", x.constructor.name)

Chrome(V8)では

zx Z
x undefined

となってくれるところが、babelではclassをpolyfillしている都合でこんな感じになってしまう。

zx Z
x _class

このような使い方だとヤバそうなので注意したい。

類似の問題はissueとして上がっていた。
https://phabricator.babeljs.io/T7014

参考資料など

  1. 自分の記憶が確かなら、functionをstring変換すると文字列になるというのは、特に明確な仕様ではなく、「だいたいのブラウザがそう実装している」という類のものだったはず。

17
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?