[javascript] 문자열을 템플릿 문자열로 변환

템플릿 문자열을 일반적인 문자열로 만들 수 있습니까?

let a="b:${b}";

그런 다음 템플릿 문자열로 변환

let b=10;
console.log(a.template());//b:10

않고 eval, new Function동적 코드 생성의 다른 수단?



답변

템플릿 문자열은 b런타임에 변수를 동적으로 참조해야 하므로 대답은 NO입니다. 동적 코드 생성 없이는 불가능합니다.

그러나 eval그것은 매우 간단합니다.

let tpl = eval('`'+a+'`');


답변

내 프로젝트에서 ES6을 사용하여 이와 같은 것을 만들었습니다.

String.prototype.interpolate = function(params) {
  const names = Object.keys(params);
  const vals = Object.values(params);
  return new Function(...names, `return \`${this}\`;`)(...vals);
}

const template = 'Example text: ${text}';
const result = template.interpolate({
  text: 'Foo Boo'
});
console.log(result);

업데이트
lodash 의존성을 제거했습니다. ES6에는 키와 값을 얻는 동등한 방법이 있습니다.


답변

당신이 요구하는 것 :

//non working code quoted from the question
let b=10;
console.log(a.template());//b:10

에 (전력, 어, 안전의 측면에서) 정확히 동일하다 eval: 코드를 포함하는 문자열을 가지고 그 코드를 실행하는 능력; 또한 실행 된 코드가 호출자의 환경에서 로컬 변수를 볼 수있는 기능.

JS에서 함수가 아닌 한 호출자의 로컬 변수를 볼 수있는 방법은 없습니다 eval(). 심지어 Function()는 할 수 없습니다.


JavaScript에 “template strings”라는 소리가 들리면 Mustache와 같은 내장 템플릿 라이브러리라고 가정하는 것이 당연합니다. 그렇지 않습니다. 주로 JS의 문자열 보간 및 여러 줄 문자열입니다. 그래도 이것이 한동안 일반적인 오해가 될 것이라고 생각합니다. 🙁


답변

아니요, 동적 코드 생성 없이는이를 수행 할 수있는 방법이 없습니다.

그러나 템플릿 문자열을 내부적으로 사용하여 일반 문자열을 값 맵을 제공 할 수있는 함수로 변환하는 함수를 만들었습니다.

템플릿 문자열 요지 생성

/**
 * Produces a function which uses template strings to do simple interpolation from objects.
 *
 * Usage:
 *    var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
 *
 *    console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
 *    // Logs 'Bryan is now the king of Scotland!'
 */
var generateTemplateString = (function(){
    var cache = {};

    function generateTemplate(template){
        var fn = cache[template];

        if (!fn){
            // Replace ${expressions} (etc) with ${map.expressions}.

            var sanitized = template
                .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
                    return `\$\{map.${match.trim()}\}`;
                    })
                // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
                .replace(/(\$\{(?!map\.)[^}]+\})/g, '');

            fn = Function('map', `return \`${sanitized}\``);
        }

        return fn;
    }

    return generateTemplate;
})();

용법:

var kingMaker = generateTemplateString('${name} is king!');

console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.

이것이 누군가를 돕기를 바랍니다. 코드에 문제가 있으면 Gist를 업데이트하십시오.


답변

TLDR :
https://jsfiddle.net/w3jx07vt/

모든 사람들이 변수에 접근하는 것을 걱정하는 것 같습니다. 왜 전달하지 않습니까? 호출자에서 변수 컨텍스트를 가져 와서 전달하는 것이 너무 어렵지 않을 것이라고 확신합니다. 이 https://stackoverflow.com/a/6394168/6563504 를 사용 하여 obj에서 소품을 가져옵니다. 나는 지금 당신을 테스트 할 수 없지만 이것은 효과가 있습니다.

function renderString(str,obj){
    return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}

테스트했습니다. 다음은 전체 코드입니다.

function index(obj,is,value) {
    if (typeof is == 'string')
        is=is.split('.');
    if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}

function renderString(str,obj){
    return str.replace(/\$\{.+?\}/g,(match)=>{return index(obj,match)})
}

renderString('abc${a}asdas',{a:23,b:44}) //abc23asdas
renderString('abc${a.c}asdas',{a:{c:22,d:55},b:44}) //abc22asdas


답변

여기서 문제는 호출자의 변수에 액세스 할 수있는 함수를 갖는 것입니다. 이것이 eval템플릿 처리에 직접 사용되는 이유 입니다. 가능한 해결책은 사전 속성에 의해 명명 된 형식 매개 변수를 가져와 같은 순서로 해당 값으로 호출하는 함수를 생성하는 것입니다. 다른 방법은 다음과 같이 간단한 것입니다.

var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());

그리고 Babel 컴파일러를 사용하는 사람이라면 누구나 생성 된 환경을 기억하는 클로저를 만들어야합니다.

console.log(new Function('name', 'return `' + message + '`;')(name));


답변

여기에 게시 된 좋은 솔루션이 많이 있지만 ES6 String.raw 메소드 를 사용하는 솔루션은 아직 없습니다 . 여기에 나의 설득이 있습니다. 전달 된 객체의 속성 만 허용한다는 점에서 중요한 제한 사항이 있습니다. 즉 템플릿에서 코드 실행이 작동하지 않습니다.

function parseStringTemplate(str, obj) {
    let parts = str.split(/\$\{(?!\d)[\wæøåÆØÅ]*\}/);
    let args = str.match(/[^{\}]+(?=})/g) || [];
    let parameters = args.map(argument => obj[argument] || (obj[argument] === undefined ? "" : obj[argument]));
    return String.raw({ raw: parts }, ...parameters);
}
let template = "Hello, ${name}! Are you ${age} years old?";
let values = { name: "John Doe", age: 18 };

parseStringTemplate(template, values);
// output: Hello, John Doe! Are you 18 years old?
  1. 문자열을 인수가 아닌 텍스트 부분으로 나눕니다. 정규식을 참조하십시오 .
    parts: ["Hello, ", "! Are you ", " years old?"]
  2. 문자열을 속성 이름으로 분할하십시오. 일치하지 않으면 배열을 비 웁니다.
    args: ["name", "age"]
  3. obj특성 이름별로 매개 변수를 맵핑하십시오 . 얕은 한 수준 매핑으로 솔루션이 제한됩니다. 정의되지 않은 값은 빈 문자열로 대체되지만 다른 잘못된 값은 허용됩니다.
    parameters: ["John Doe", 18]
  4. 활용 String.raw(...)하고 결과를 반환합니다.