//start of fat arrow utility
'use strict';
function expandFatArrow(code) {
\t var arrowHeadRegex = RegExp(/(\((?:\w+,)*\w+\)|\(\)|\w+)[\r\t ]*=>\s*/);
\t var arrowHeadMatch = arrowHeadRegex.exec(code);
\t
\t if(arrowHeadMatch) {//if no match return as it is
\t \t var params = arrowHeadMatch[1];
\t \t if(params.charAt(0) !== "(") {
\t \t \t params = "(" + params + ")";
\t \t }
\t \t var index = arrowHeadMatch.index;
\t \t var startCode = code.substring(0, index);
\t \t
\t \t var bodyAndNext = code.substring(index + arrowHeadMatch[0].length);
\t \t
\t \t var curlyCount = 0;
\t \t var curlyPresent = false;
\t \t var singleLineBodyEnd = 0;
\t \t var bodyEnd = 0;
\t \t var openingQuote = null;
\t \t
\t \t for(var i = 0; i < bodyAndNext.length; i++) {
\t \t \t var ch = bodyAndNext[i];
\t \t \t if(ch === '"' || ch === "'") {
\t \t \t \t openingQuote = ch;
\t \t \t \t i = skipQuotedString(bodyAndNext, openingQuote, i);
\t \t \t \t ch = bodyAndNext[i];
\t \t \t }
\t \t \t
\t \t \t if(ch === '{'){
\t \t \t \t curlyPresent = true;
\t \t \t \t curlyCount++;
\t \t \t } else if(ch === '}') {
\t \t \t \t \t curlyCount--;
\t \t \t } else if(!curlyPresent) {
\t \t \t \t //any character other than { or }
\t \t \t \t singleLineBodyEnd = getSingeLineBodyEnd(bodyAndNext, i);
\t \t \t \t break;
\t \t \t }
\t \t \t if(curlyPresent && curlyCount === 0) {
\t \t \t \t bodyEnd = i;
\t \t \t \t break;
\t \t \t }
\t \t }
\t \t var body = null;
\t \t if(curlyPresent) {
\t \t \t if(curlyCount !== 0) {
\t \t \t \t throw Error("Could not match curly braces for function at : " + index);
\t \t \t }
\t \t \t body = bodyAndNext.substring(0, bodyEnd+1);
\t \t \t
\t \t \t var restCode = bodyAndNext.substring(bodyEnd + 1);
\t \t \t var expandedFun = "(function " + params + body + ").bind(this)";
\t \t \t code = startCode + expandedFun + restCode;
\t \t } else {
\t \t \t if(singleLineBodyEnd <=0) {
\t \t \t \t throw Error("could not get function body at : " + index);
\t \t \t }
\t \t \t
\t \t \t body = bodyAndNext.substring(0, singleLineBodyEnd+1);
\t \t \t
\t \t \t restCode = bodyAndNext.substring(singleLineBodyEnd + 1);
\t \t \t expandedFun = "(function " + params + "{return " + body + "}).bind(this)";
\t \t \t code = startCode + expandedFun + restCode;
\t \t }
\t \t return expandFatArrow(code);//recursive call
\t }
\t return code;
}
function getSingeLineBodyEnd(bodyCode, startI) {
\t var braceCount = 0;
\t var openingQuote = null;
\t
\t for(var i = startI; i < bodyCode.length; i++) {
\t \t var ch = bodyCode[i];
\t \t var lastCh = null;
\t \t if(ch === '"' || ch === "'") {
\t \t \t openingQuote = ch;
\t \t \t i = skipQuotedString(bodyCode, openingQuote, i);
\t \t \t ch = bodyCode[i];
\t \t }
\t \t
\t \t if(i !== 0 && !bodyCode[i-1].match(/[\t\r ]/)) {
\t \t \t lastCh = bodyCode[i-1];
\t \t }
\t \t if(ch === '{' || ch === '(') {
\t \t \t braceCount++;
\t \t } else if(ch === '}' || ch === ')') {
\t \t \t braceCount--;
\t \t }
\t \t
\t \t if(braceCount < 0 || (lastCh !== '.' && ch === '\n')) {
\t \t \t return i-1;
\t \t }
\t }
\t
\t return bodyCode.length;
}
function skipQuotedString(bodyAndNext, openingQuote, i) {
\t var matchFound = false;//matching quote
\t var openingQuoteI = i;
\t i++;
\t for(; i < bodyAndNext.length; i++) {
\t \t var ch = bodyAndNext[i];
\t \t var lastCh = (i !== 0) ? bodyAndNext[i-1] : null;
\t \t
\t \t if(ch !== openingQuote || (ch === openingQuote && lastCh === '\\')) {
\t \t \t continue;//skip quoted string
\t \t } else if(ch === openingQuote) {//matched closing quote
\t \t \t matchFound = false;
\t \t \t break;
\t \t }
\t }
\t if(matchFound) {
\t \t throw new Error("Could not find closing quote for quote at : " + openingQuoteI);
\t }
\t return i;
}
//end of fat arrow utility
//validation of test cases
(function() {
\t var tests = document.querySelectorAll('.test');
\t var currentExpansionNode = null;
\t var currentLogNode = null;
\t for(var i = 0; i < tests.length; i++) {
\t \t var currentNode = tests[i];
\t \t addTitle("Test " + (i+1), currentNode);
\t \t createExpansionAndLogNode(currentNode);
\t \t
\t \t var testCode = currentNode.innerText;
\t \t var expandedCode = expandFatArrow(testCode);
\t \t logDom(expandedCode, 'expanded');
\t \t
\t \t eval(expandedCode);
\t \t
\t };
\t function createExpansionAndLogNode(node) {
\t \t var expansionNode = document.createElement('pre');
\t \t expansionNode.classList.add('expanded');
\t \t currentExpansionNode = expansionNode;
\t \t
\t \t var logNode = document.createElement('div');
\t \t logNode.classList.add('log');
\t \t currentLogNode = logNode;
\t \t
\t \t appendAfter(node,expansionNode);
\t \t addTitle("Expansion Result", expansionNode);
\t \t appendAfter(expansionNode, logNode);
\t \t addTitle("Output", logNode);
\t }
\t function appendAfter(afterNode, newNode) {
\t \t afterNode.parentNode.insertBefore(newNode, afterNode.nextSibling);
\t }
\t //logs to expansion node or log node
\t function logDom(str, cssClass) {
\t \t console.log(str);
\t \t var node = null;
\t \t if(cssClass === 'expanded') {
\t \t \t node = currentExpansionNode;
\t \t } else {
\t \t \t node = currentLogNode;
\t \t }
\t \t
\t \t var newNode = document.createElement("pre");
\t \t
\t \t newNode.innerText = str;
\t \t node.appendChild(newNode);
\t }
\t function addTitle(title, onNode) {
\t \t var titleNode = document.createElement('h3');
\t \t titleNode.innerText = title;
\t \t onNode.parentNode.insertBefore(titleNode, onNode);
\t }
})();
pre {
\t padding: 5px;
}
* {
\t margin: 2px;
}
.test-unit{
\t border: 2px solid black;
\t padding: 5px;
}
.test{
\t border: 1px solid gray;
\t background-color: #eef;
\t margin-top: 5px;
}
.expanded{
\t border: 1px solid gray;
\t background-color: #ffe;
}
.log{
\t border: 1px solid gray;
\t background-color: #ddd;
}
.error {
\t border: 1px solid gray;
\t background-color: #fff;
\t color: red;
}
<html>
\t <head>
\t \t <link rel='stylesheet' href='style.css'>
\t </head>
\t <body>
<div class='test-unit'>
<pre class='test'>
\t //skip braces in string, with curly braces
\t var fun =()=> {
\t \t return "test me {{{{{{} {{{}";
\t };
\t logDom(fun());
\t var fun1 =()=> logDom('test me again{ { {}{{ }}}}}}}}}}}}}}');
\t fun1();
</pre>
</div>
<div class='test-unit'>
<pre class='test'>
\t var selectId = new Function('x', 'return x.map(a=>a.id)');;
\t var mappedArr = selectId([{id:'test'},{id:'test1'}]);
\t console.log("test0: " + JSON.stringify(mappedArr));
\t logDom("test0: " + JSON.stringify(mappedArr), 'log');
</pre>
</div>
<div class='test-unit'>
<pre class='test'>
\t //with surrounding code
\t var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
\t var es6OddNumbers = numbers.filter(number => number % 2);
\t logDom("test1 : " + es6OddNumbers, 'log');
</pre>
</div>
<div class='test-unit'>
<pre class='test'>
\t //standalone fat arrow
\t var square = x => x * x;
\t logDom("test2: " + square(10), 'log');
</pre>
</div>
<div class='test-unit'>
<pre class='test'>
\t //with mutiple parameters, single line
\t var add = (a, b) => a + b;
\t logDom("test3: " + add(3, 4), 'log');
</pre>
</div>
<div class='test-unit'>
<pre class='test'>
\t //test with surrounding like test1
\t var developers = [{name: 'Rob'}, {name: 'Jake'}];
\t var es6Output = developers.map(developer => developer.name);
\t logDom("test4: " + es6Output, 'log');
</pre>
</div>
<div class='test-unit'>
<pre class='test'>
\t //empty braces, returns undefined
\t logDom("test5: " + (()=>{})(), 'log');
</pre>
</div>
<div class='test-unit'>
<pre class='test'>
\t //return empty object
\t logDom("test6: " + (()=>{return {}})(), 'log');
</pre>
</div>
<div class='test-unit'>
<pre class='test'>
\t //working with the 'this' scope and multiline
\t function CounterES6() {
\t this.seconds = 0;
\t var intervalCounter = 0;
\t var intervalId = null;
\t intervalId = window.setInterval(() => {
\t \t \t this.seconds++;
\t \t \t logDom("test7: interval seconds: " + this.seconds, 'log');
\t \t \t if(++intervalCounter > 9) {
\t \t \t \t clearInterval(intervalId);
\t \t \t \t logDom("Clearing interval", 'log');
\t \t \t }
\t \t }, 1000);
\t }
\t var counterB = new CounterES6();
\t window.setTimeout(() => {
\t \t var seconds = counterB.seconds;
\t \t logDom("test7: timeout seconds: " +counterB.seconds, 'log');
\t }, 1200);
</pre>
</div>
\t \t
\t </body>
</html>
あなたはパーサを書きたいですか? – SparK
@AndyはJavaScriptの太字のラムダ表記法(引数は 'a'、戻り値は' a.id'です)です。したがって 'a => a.id'は' x.map() 'の関数引数です。 – TheHansinator
これは、矢印機能をサポートしていないブラウザでは魔法のように動作するとは思えません。あなたは、バーベルやトレーサのようなパーサーが必要です。 – Oriol