2016-04-05 7 views
4

ブラウザは、動的JavaScript評価をevalまたはnew Functionでサポートしています。これは、文字列としてJavaScript関数に提供される小さなデータバインディング式をコンパイルする場合に非常に便利です。矢印関数evalプリプロセッサ

など。

var add2 = new Function('x', 'return x + 2'); 
var y = add2(5); //7 

私はバベルやJavaScriptの百以上の数行を持つ他のライブラリを使用せずにES6矢印関数の構文をサポートするために、これらの式を前処理するしたいと思います。

var selectId = new Function('x', 'return x.map(a=>a.id)'); 

残念ながら、これは最新のIEバージョンでも機能しません。

この関数は文字列を取り、別の文字列を返す必要があります。例えば。

resolveArrows('return x.map(a=>a.id)') 

ようなものを実装する方法の

'return x.map(function(a) { return a.id })'

任意のアイデアを返す必要がありますか?

+0

あなたはパーサを書きたいですか? – SparK

+0

@AndyはJavaScriptの太字のラムダ表記法(引数は 'a'、戻り値は' a.id'です)です。したがって 'a => a.id'は' x.map() 'の関数引数です。 – TheHansinator

+0

これは、矢印機能をサポートしていないブラウザでは魔法のように動作するとは思えません。あなたは、バーベルやトレーサのようなパーサーが必要です。 – Oriol

答えて

3

他の人は、このようなユーティリティは非常に壊れやすく、非常に複雑なコードでは信頼できないとすでに説明しています。

しかし、単純なケースでは、これを実装することは可能です。 次は、機能拡張のFat Arrowへのリンクです。

https://github.com/ConsciousObserver/stackoverflow/blob/master/Es6FatArrowExpansion/fatArrowUtil.js

インポートfatArrowUtil.jsし、あなたのコードにexpandFatArrow(code)を呼び出します。続き

は、サンプルの使用

expandFatArrow("()=>'test me';"); 

れており、以下

//actual 
var selectId = new Function('x', 'return x.map(a=>a.id)'); 
//after expansion 
var selectId = new Function('x', 'return x.map((function (a){return a.id}).bind(this))'); 

は注意あなたの提案、テストケースのための出力結果を以下

(function(){return 'test me';}).bind(this) 

されている:このユーティリティは、バインドを使用しています()関数の 'this'コンテキストを保持します。 コードをコンパイルしようとしません。元のコードのエラーは、展開されたコードに存在します。

以下は、テストと結果の作業サンプルです。

//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>

+1

クール。できます。それはまさに私が求めていたものです。私は潜在的な問題が1つしかないことを見ており、それは中かっこが文字列の中にある場合です。私は括弧の数を文字列の中で無効にする必要があると思うが、それは私自身でハックできるものだ。 – Marko

+0

あなたはそうだと思いませんでした。私はそれを変更しようとします。 – 11thdimension

+0

@Marko引用符付き文字列の中括弧を処理するコードを更新しました。 – 11thdimension

関連する問題