Làm cách nào để thực hiện phép nội suy chuỗi trong JavaScript?


536

Xem xét mã này:

var age = 3;

console.log("I'm " + age + " years old!");

Có cách nào khác để chèn giá trị của biến vào chuỗi không, ngoài nối chuỗi?


7
Bạn có thể kiểm CoffeeScript: coffeescript.org
dennismonsewicz

1
Như những người khác đã chỉ ra, phương pháp bạn đang sử dụng cách dễ nhất để thực hiện. Tôi rất thích có thể tham khảo các biến trong chuỗi, nhưng trong javascript bạn cần sử dụng phép nối (hoặc một số cách tiếp cận tìm / thay thế khác). Những người như bạn và tôi có lẽ chỉ hơi gắn bó với PHP vì lợi ích của chúng ta.
Lev

4
Sử dụng mẫu
Jahan

2
Ghép không phải là nội suy và người dùng hỏi làm thế nào để nội suy. Tôi nghĩ rằng Lev cuối cùng đã cung cấp câu trả lời, tức là không có cách nào để làm điều này trong JS gốc. Tôi không hiểu tại sao đây không phải là một câu hỏi thực sự.
gitb

4
Nếu tôi có thể được phép trả lời, thì bây giờ đã có giải pháp bắt đầu cho các phiên dịch mới hơn (2015 FF và Chrome đã hỗ trợ): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ . Ví dụ. Show GRAPH of : ${fundCode}-${funcCode} giúp tôi Show GRAPH of : AIP001-_sma (sử dụng backticks xung quanh chuỗi 1 .... dường như không thể hiển thị ở đây)
Marcos

Câu trả lời:


537

Kể từ ES6, bạn có thể sử dụng mẫu chữ :

const age = 3
console.log(`I'm ${age} years old!`)

PS Lưu ý việc sử dụng backticks : ``.


3
Tôi đang tự hỏi tại sao họ lại đi với backticks về điều này, liệu "$ {age}" có đủ để thực hiện phép nội suy không?
Lemonade cười

5
@LaughingLemonade Tương thích ngược. Nếu nó được thực hiện theo cách đó, tất cả các mã hiện có in một chuỗi ở định dạng đó sẽ thất bại.
thefourtheye

1
@Jonathan Tôi luôn không thích backticks là một nhân vật có ý nghĩa. Nhưng tôi không chắc lý do là phổ biến hay chỉ áp dụng cho bố cục bàn phím ngôn ngữ của tôi. Backtick là một loại nhân vật đặc biệt chờ đợi được viết cho đến khi nhân vật tiếp theo, yêu cầu nó phải được nhấn hai lần để viết (và viết hai trong số chúng tại thời điểm này). Điều này là do một số nhân vật tương tác với họ, chẳng hạn như "e". Nếu tôi cố gắng viết "ello" với họ, tôi sẽ nhận được èllo``. Nó cũng có vị trí hơi khó chịu (shift + Forwardtick, một nhân vật có vẻ ngoài đặc biệt khác), vì nó yêu cầu dịch chuyển sang loại, không giống như '.
felix

@felix đó là một suy nghĩ cụ thể cho bố cục bàn phím của bạn; những gì bạn mô tả được gọi là "khóa chết". Chúng cũng có mặt trong bố trí bàn phím Na Uy (nơi tôi đến), đó là một phần lý do tôi chuyển sang bố trí bàn phím Hoa Kỳ cho tất cả các chương trình. (Có một số ký tự khác - ví dụ [và] - cũng dễ dàng hơn nhiều trên bàn phím Hoa Kỳ.)
Eivind Eklund

239

tl; dr

Sử dụng chuỗi ký tự mẫu của ECMAScript 2015, nếu có.

Giải trình

Không có cách nào trực tiếp để làm điều đó, theo thông số kỹ thuật của ECMAScript 5, nhưng ECMAScript 6 có các chuỗi mẫu , còn được gọi là gần như bằng chữ trong quá trình soạn thảo thông số kỹ thuật. Sử dụng chúng như thế này:

> var n = 42;
undefined
> `foo${n}bar`
'foo42bar'

Bạn có thể sử dụng bất kỳ biểu thức JavaScript hợp lệ nào bên trong {}. Ví dụ:

> `foo${{name: 'Google'}.name}bar`
'fooGooglebar'
> `foo${1 + 3}bar`
'foo4bar'

Điều quan trọng khác là, bạn không phải lo lắng về chuỗi nhiều dòng nữa. Bạn có thể viết chúng đơn giản như

> `foo
...     bar`
'foo\n    bar'

Lưu ý: Tôi đã sử dụng io.js v2.4.0 để đánh giá tất cả các chuỗi mẫu được hiển thị ở trên. Bạn cũng có thể sử dụng Chrome mới nhất để kiểm tra các ví dụ được hiển thị ở trên.

Lưu ý: Thông số kỹ thuật ES6 hiện đã được hoàn thiện , nhưng vẫn chưa được triển khai bởi tất cả các trình duyệt chính.
Theo các trang Mạng của Nhà phát triển Mozilla , việc này sẽ được triển khai để hỗ trợ cơ bản bắt đầu ở các phiên bản sau: Firefox 34, Chrome 41, Internet Explorer 12. Nếu bạn là người dùng Opera, Safari hoặc Internet Explorer và hiện đang tò mò về điều này , giường thử nghiệm này có thể được sử dụng để chơi xung quanh cho đến khi mọi người nhận được hỗ trợ cho việc này.


3
Bạn có thể sử dụng được nếu bạn sử dụng babeljs , vì vậy bạn có thể giới thiệu nó trong cơ sở mã của mình và sau đó bỏ bước dịch chuyển một khi các trình duyệt bạn cần hỗ trợ thực hiện.
ivarni

Có cách nào để chuyển đổi một chuỗi tiêu chuẩn thành một chuỗi mẫu bằng chữ không? Ví dụ, nếu một người đã có một tệp json chứa một bảng dịch mà giá trị cần nội suy vào chúng cho mục đích hiển thị? Tôi nghĩ rằng giải pháp đầu tiên có thể hoạt động tốt cho tình huống này, nhưng tôi làm như mới chuỗi mẫu cú pháp nói chung.
phiền ngày

Vì đây vẫn là một trong những kết quả tìm kiếm đầu tiên trên phép nội suy chuỗi js, sẽ thật tuyệt nếu bạn có thể cập nhật nó để phản ánh tính khả dụng chung bây giờ. "Không có cách nào trực tiếp để làm điều đó" có lẽ không còn đúng nữa đối với phần lớn các trình duyệt.
Felix Dombek

2
Đối với thị lực kém trong khán giả, 'nhân vật khác với'. Nếu bạn gặp khó khăn trong việc này, hãy đảm bảo rằng bạn sử dụng trích dẫn nghiêm trọng, (còn gọi là trích dẫn nghiêng, trích dẫn ngược) en.wikipedia.org/wiki/Grave_accent . Nó nằm ở trên cùng bên trái trên bàn phím của bạn :)
Quincy

@Quincy Phía dưới bên trái của bàn phím Mac - còn được gọi là back-tick :)
RB.

192

JavaScript Remedial của Douglas Crockford bao gồm một String.prototype.supplantchức năng. Nó ngắn, quen thuộc và dễ sử dụng:

String.prototype.supplant = function (o) {
    return this.replace(/{([^{}]*)}/g,
        function (a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        }
    );
};

// Usage:
alert("I'm {age} years old!".supplant({ age: 29 }));
alert("The {a} says {n}, {n}, {n}!".supplant({ a: 'cow', n: 'moo' }));

Nếu bạn không muốn thay đổi nguyên mẫu của String, bạn luôn có thể điều chỉnh nó thành độc lập hoặc đặt nó vào một số không gian tên khác hoặc bất cứ thứ gì.


103
Lưu ý: Điều này sẽ chạy chậm hơn mười lần so với chỉ nối.
cllpse

36
Và mất khoảng ba lần nhấn phím & byte.
ma11hew28

8
@roosteronacid - Bạn có thể đưa ra một số quan điểm về việc giảm tốc độ đó không? Giống như, từ 0,01 đến 0,1 giây (quan trọng) hoặc từ 0,000000001 đến 0,00000001s (không liên quan)?
chris

16
@george: Một thử nghiệm nhanh trên máy của tôi đã cho 7312 ns cho "Con bò nói moo, moo, moo!" sử dụng phương thức của Crockford so với 111 ns cho hàm được biên dịch trước để kéo các biến ra khỏi đối tượng được truyền và nối chúng với các phần không đổi của chuỗi mẫu. Đây là Chrome 21.
George

13
Hoặc sử dụng nó như sau:"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])
Robert Massa

54

Lưu ý: tránh mọi hệ thống mẫu không cho phép bạn thoát khỏi các dấu phân cách của chính nó. Ví dụ, sẽ không có cách nào để xuất ra sau đây bằng supplant()phương pháp được đề cập ở đây.

"Tôi 3 tuổi nhờ vào biến {age} của tôi."

Nội suy đơn giản có thể hoạt động cho các tập lệnh nhỏ khép kín, nhưng thường đi kèm với lỗ hổng thiết kế này sẽ hạn chế bất kỳ việc sử dụng nghiêm trọng nào. Tôi thực sự thích các mẫu DOM, chẳng hạn như:

<div> I am <span id="age"></span> years old!</div>

Và sử dụng thao tác jQuery: $('#age').text(3)

Thay vào đó, nếu bạn chỉ đơn giản là mệt mỏi với việc nối chuỗi, luôn có cú pháp thay thế:

var age = 3;
var str = ["I'm only", age, "years old"].join(" ");

4
Lưu ý bên lề: Array.join()chậm hơn so với +ghép nối trực tiếp ( kiểu), bởi vì các công cụ trình duyệt (bao gồm V8, bao gồm nút và hầu hết mọi thứ chạy JS ngày nay) đã tối ưu hóa nó một cách ồ ạt và có rất nhiều sự khác biệt trong việc kết hợp trực tiếp
pilau

1
Phương thức thay thế cho phép bạn tạo chuỗi bạn đề cập: {token} chỉ được thay thế nếu đối tượng dữ liệu có chứa một thành viên được gọi token- do đó miễn là bạn không cung cấp đối tượng dữ liệu có agethành viên, nó sẽ ổn.
Chris Fewtrell

1
Chris, tôi không thấy đó là một giải pháp. Tôi có thể dễ dàng cập nhật ví dụ để sử dụng cả biến tuổi và chuỗi {age}. Bạn có thực sự muốn lo lắng về những gì bạn có thể đặt tên cho các biến của mình dựa trên văn bản sao chép mẫu không? - Ngoài ra, vì bài đăng này tôi đã trở thành một fan hâm mộ lớn của các thư viện ràng buộc dữ liệu. Một số, như RactiveJS, sẽ cứu bạn khỏi một DOM đầy xuống với các nhịp thay đổi. Và không giống như Mustache, nó chỉ cập nhật phần đó của trang.
greg.kindel

3
Câu trả lời chính của bạn dường như cho rằng javascript này đang chạy trong môi trường trình duyệt mặc dù câu hỏi không được gắn thẻ HTML.
canon

2
"Lời cảnh báo" của bạn supplantlà không chính đáng: "I am 3 years old thanks to my {age} variable.".supplant({}));trả về chính xác chuỗi đã cho. Nếu ageđược đưa ra, người ta vẫn có thể in {}sử dụng{{age}}
le_m

23

Hãy thử thư viện sprintf (một triển khai sprintf JavaScript mã nguồn mở hoàn chỉnh). Ví dụ:

vsprintf('The first 4 letters of the english alphabet are: %s, %s, %s and %s', ['a', 'b', 'c', 'd']);

vsprintf lấy một mảng các đối số và trả về một chuỗi được định dạng.


22

Tôi sử dụng mô hình này trong nhiều ngôn ngữ khi tôi chưa biết cách thực hiện đúng và chỉ muốn nhanh chóng đưa ra ý tưởng:

// JavaScript
let stringValue = 'Hello, my name is {name}. You {action} my {relation}.'
    .replace(/{name}/g    ,'Indigo Montoya')
    .replace(/{action}/g  ,'killed')
    .replace(/{relation}/g,'father')
    ;

Trong khi không đặc biệt hiệu quả, tôi thấy nó có thể đọc được. Nó luôn hoạt động và luôn có sẵn:

' VBScript
dim template = "Hello, my name is {name}. You {action} my {relation}."
dim stringvalue = template
stringValue = replace(stringvalue, "{name}"    ,"Luke Skywalker")     
stringValue = replace(stringvalue, "{relation}","Father")     
stringValue = replace(stringvalue, "{action}"  ,"are")

LUÔN LUÔN

* COBOL
INSPECT stringvalue REPLACING FIRST '{name}'     BY 'Grendel'
INSPECT stringvalue REPLACING FIRST '{relation}' BY 'Mother'
INSPECT stringvalue REPLACING FIRST '{action}'   BY 'did unspeakable things to'



6

Hãy thử kiwi , một mô-đun JavaScript trọng lượng nhẹ để nội suy chuỗi.

Bạn có thể làm

Kiwi.compose("I'm % years old!", [age]);

hoặc là

Kiwi.compose("I'm %{age} years old!", {"age" : age});

6

Nếu bạn muốn nội suy trong console.logđầu ra, thì chỉ cần

console.log("Eruption 1: %s", eruption1);
                         ^^

Đây %slà cái được gọi là "định dạng định dạng". console.logcó loại hỗ trợ nội suy tích hợp này.


1
Đây là câu trả lời đúng duy nhất. Câu hỏi là về nội suy không phải chuỗi mẫu.
sospedra

5

Đây là một giải pháp yêu cầu bạn cung cấp một đối tượng với các giá trị. Nếu bạn không cung cấp một đối tượng làm tham số, nó sẽ mặc định sử dụng các biến toàn cục. Nhưng tốt hơn là sử dụng tham số, nó sạch hơn nhiều.

String.prototype.interpolate = function(props) {
    return this.replace(/\{(\w+)\}/g, function(match, expr) {
        return (props || window)[expr];
    });
};

// Test:

// Using the parameter (advised approach)
document.getElementById("resultA").innerText = "Eruption 1: {eruption1}".interpolate({ eruption1: 112 });

// Using the global scope
var eruption2 = 116;
document.getElementById("resultB").innerText = "Eruption 2: {eruption2}".interpolate();
<div id="resultA"></div><div id="resultB"></div>


3
Đừng dùng eval, evallà ác!
chris97ong

5
@ chris97ong Mặc dù điều đó là "đúng", ít nhất là đưa ra một lý do ("xấu xa" không giúp ích gì) hoặc giải pháp thay thế. Hầu như luôn luôn có một cách sử dụng eval, nhưng đôi khi không. Ví dụ: nếu OP muốn một cách để nội suy bằng cách sử dụng phạm vi hiện tại, mà không phải vượt qua một đối tượng tra cứu (như cách nội suy Groovy hoạt động), tôi khá chắc chắn evalsẽ được yêu cầu. Đừng chỉ dùng đến "eval is evil" cũ.
Ian

1
không bao giờ sử dụng eval cũng không bao giờ đề nghị nó được sử dụng
Hasen

2
@hasenj đây là lý do tại sao tôi nói nó "có thể không phải là ý tưởng tốt nhất" và cung cấp một phương pháp thay thế. Nhưng thật không may, evallà cách duy nhất để truy cập các biến cục bộ trong phạm vi . Vì vậy, đừng từ chối nó chỉ vì nó làm tổn thương cảm xúc của bạn. BTW, tôi cũng thích cách khác vì nó an toàn hơn, nhưng evalphương pháp là câu trả lời chính xác cho câu hỏi của OP, do đó nó nằm trong câu trả lời.
Lucas Trzesniewski

1
Vấn đề với evallà không thể truy cập các vars từ một phạm vi khác, vì vậy nếu .interpolatecuộc gọi của bạn nằm trong một chức năng khác và không phải là toàn cầu, nó sẽ không hoạt động.
georg

2

Mở rộng câu trả lời thứ hai của Greg Kindel , bạn có thể viết một hàm để loại bỏ một số bản tóm tắt:

var fmt = {
    join: function() {
        return Array.prototype.slice.call(arguments).join(' ');
    },
    log: function() {
        console.log(this.join(...arguments));
    }
}

Sử dụng:

var age = 7;
var years = 5;
var sentence = fmt.join('I am now', age, 'years old!');
fmt.log('In', years, 'years I will be', age + years, 'years old!');

1

Tôi có thể chỉ cho bạn một ví dụ:

function fullName(first, last) {
  let fullName = first + " " + last;
  return fullName;
}

function fullNameStringInterpolation(first, last) {
  let fullName = `${first} ${last}`;
  return fullName;
}

console.log('Old School: ' + fullName('Carlos', 'Gutierrez'));

console.log('New School: ' + fullNameStringInterpolation('Carlos', 'Gutierrez'));


1

Kể từ ES6, nếu bạn muốn thực hiện phép nội suy chuỗi trong các khóa đối tượng, bạn sẽ nhận được SyntaxError: expected property name, got '${'nếu bạn làm một cái gì đó như:

let age = 3
let obj = { `${age}`: 3 }

Bạn nên làm như sau thay thế:

let obj = { [`${age}`]: 3 }

1

Cung cấp thêm cho phiên bản ES6 của bài đăng của @Chris Nielsen.

String.prototype.supplant = function (o) {
  return this.replace(/\${([^\${}]*)}/g,
    (a, b) => {
      var r = o[b];
      return typeof r === 'string' || typeof r === 'number' ? r : a;
    }
  );
};

string = "How now ${color} cow? {${greeting}}, ${greeting}, moo says the ${color} cow.";

string.supplant({color: "brown", greeting: "moo"});
=> "How now brown cow? {moo}, moo, moo says the brown cow."

0

Sử dụng cú pháp mẫu không thành công trong các trình duyệt cũ hơn, quan trọng nếu bạn đang tạo HTML để sử dụng công khai. Sử dụng phép nối rất tẻ nhạt và khó đọc, đặc biệt nếu bạn có nhiều biểu thức dài hoặc dài hoặc nếu bạn phải sử dụng dấu ngoặc đơn để xử lý hỗn hợp các mục số và chuỗi (cả hai đều sử dụng toán tử +).

PHP mở rộng các chuỗi được trích dẫn có chứa các biến và thậm chí một số biểu thức bằng cách sử dụng một ký hiệu rất nhỏ gọn: $a="the color is $color";

Trong JavaScript, một chức năng hiệu quả có thể được viết để hỗ trợ điều này: var a=S('the color is ',color); , sử dụng số lượng đối số biến. Mặc dù không có lợi thế hơn so với phép nối trong ví dụ này, khi các biểu thức trở nên dài hơn, cú pháp này có thể rõ ràng hơn. Hoặc người ta có thể sử dụng ký hiệu đô la để báo hiệu bắt đầu một biểu thức bằng hàm JavaScript, như trong PHP.

Mặt khác, viết một hàm giải pháp hiệu quả để cung cấp mở rộng chuỗi giống như mẫu cho các trình duyệt cũ hơn sẽ không khó. Ai đó có lẽ đã làm điều đó rồi.

Cuối cùng, tôi tưởng tượng rằng sprintf (như trong C, C ++ và PHP) có thể được viết bằng JavaScript, mặc dù nó sẽ kém hiệu quả hơn một chút so với các giải pháp khác này.


0

Nội suy tùy chỉnh linh hoạt:

var sourceElm = document.querySelector('input')

// interpolation callback
const onInterpolate = s => `<mark>${s}</mark>`

// listen to "input" event
sourceElm.addEventListener('input', parseInput) 

// parse on window load
parseInput() 

// input element parser
function parseInput(){
  var html = interpolate(sourceElm.value, undefined, onInterpolate)
  sourceElm.nextElementSibling.innerHTML = html;
}

// the actual interpolation 
function interpolate(str, interpolator = ["{{", "}}"], cb){
  // split by "start" pattern
  return str.split(interpolator[0]).map((s1, i) => {
    // first item can be safely ignored
	  if( i == 0 ) return s1;
    // for each splited part, split again by "end" pattern 
    const s2 = s1.split(interpolator[1]);

    // is there's no "closing" match to this part, rebuild it
    if( s1 == s2[0]) return interpolator[0] + s2[0]
    // if this split's result as multiple items' array, it means the first item is between the patterns
    if( s2.length > 1 ){
        s2[0] = s2[0] 
          ? cb(s2[0]) // replace the array item with whatever
          : interpolator.join('') // nothing was between the interpolation pattern
    }

    return s2.join('') // merge splited array (part2)
  }).join('') // merge everything 
}
input{ 
  padding:5px; 
  width: 100%; 
  box-sizing: border-box;
  margin-bottom: 20px;
}

*{
  font: 14px Arial;
  padding:5px;
}
<input value="Everything between {{}} is {{processed}}" />
<div></div>


0

Mặc dù các mẫu có thể là tốt nhất cho trường hợp bạn mô tả, nếu bạn có hoặc muốn dữ liệu và / hoặc đối số của mình ở dạng lặp / mảng, bạn có thể sử dụng String.raw.

String.raw({
  raw: ["I'm ", " years old!"]
}, 3);

Với dữ liệu là một mảng, người ta có thể sử dụng toán tử trải rộng:

const args = [3, 'yesterday'];
String.raw({
  raw: ["I'm ", " years old as of ", ""]
}, ...args);

0

Không thể tìm thấy những gì tôi đang tìm kiếm, sau đó tìm thấy nó -

Nếu bạn đang sử dụng Node.js, có một utilgói tích hợp với chức năng định dạng hoạt động như thế này:

util.format("Hello my name is %s", "Brent");
> Hello my name is Brent

Thật trùng hợp, điều này giờ cũng được tích hợp vào các console.loghương vị trong Node.js -

console.log("This really bad error happened: %s", "ReferenceError");
> This really bad error happened: ReferenceError
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.