---
type: how-to
language: javascript
slug: string/truncate
title: "文字列を決まった文字数で切り詰める"
title_tag: "JavaScript 文字列を切り詰める — 絵文字が壊れない書き方"
summary: >
  長い文字列を指定した文字数で切って「…」を付ける方法をまとめます。slice() で絵文字が
  壊れる理由、スプレッド構文で足りない場面、Intl.Segmenter を使う書き方まで確かめます。
description: >
  slice() は符号単位で切るため絵文字の途中で割れます。スプレッド構文で足りる場面と、組み合わせた絵文字まで守るために Intl.Segmenter が要る場面を分けて示します。
status: published
difficulty: 2
minutes: 8

versions:
  verified: "Node 22.22.3"
  since: "ES2015"
  deprecated: null
  removed: null

sources:
  - title: "String.prototype.slice — ECMAScript® 2026 Language Specification"
    url: "https://tc39.es/ecma262/#sec-string.prototype.slice"
  - title: "Intl.Segmenter — ECMAScript® 2026 Internationalization API Specification"
    url: "https://tc39.es/ecma402/#segmenter-objects"
  - title: "Unicode Standard Annex #29: Unicode Text Segmentation"
    url: "https://unicode.org/reports/tr29/"

terms: [切り詰め, 書記素, サロゲートペア]

links:
  related:
    - javascript/learn/string/unicode
    - javascript/learn/string/slice

content_updated_at: 2026-09-08
published_at: 2026-09-08
---

## 答え

**日本語と英数字だけなら、`slice()` で足ります。**

```js run
function truncate(text, max) {
  return text.length <= max ? text : text.slice(0, max) + '…';
}

console.log(truncate('こんにちは世界', 5));
console.log(truncate('短い', 5));
```
```output
こんにちは…
短い
```

**絵文字が混ざる可能性があるなら、`Intl.Segmenter` を使ってください。**

```js run
function truncate(text, max) {
  const segmenter = new Intl.Segmenter('ja', { granularity: 'grapheme' });
  const characters = [...segmenter.segment(text)].map((s) => s.segment);

  return characters.length <= max ? text : characters.slice(0, max).join('') + '…';
}

console.log(truncate('👨‍👩‍👧と旅行に行きました', 5));
console.log(truncate('あ🙂いうえお', 3));
console.log(truncate('短い', 5));
```
```output
👨‍👩‍👧と旅行に…
あ🙂い…
短い
```

## 長さを先に見る

**切る前に長さを比べてください。** これを忘れると、短い文字列にも `…` が付きます。

```js bad
console.log('短い'.slice(0, 5) + '…');
```
```output
短い…
```

`slice()` は範囲を超えても[bad:エラーになりません]。そのまま返します。
**つまり「切られなかった」ことに気づけません。**

```js run
function truncate(text, max) {
  return text.length <= max ? text : text.slice(0, max) + '…';
}

console.log(truncate('ちょうど五文字', 7));
```
```output
ちょうど五文字
```

`…` を足す前に、[key:切る必要があったかどうか]を判定します。

## `slice()` では足りない場面

`slice()` が数えているのは[type:符号単位]で、**見た目の文字数ではありません。**

```js run
const text = 'あ🙂いうえお';

console.log(text.length);
console.log([...text].length);
```
```output
7
6
```

見た目は[num:6]文字なのに、`length` は[num:7]です。絵文字が[num:2]つ分だからです。

**絵文字の途中で切ると壊れます。**

```js bad
const text = 'あ🙂いうえお';
const cut = text.slice(0, 2);

console.log(cut.length);
console.log(cut === 'あ🙂');
```
```output
2
false
```

`あ` と絵文字の[key:前半だけ]が取れています。
画面には読めない記号が出ます。**文字数の指定は守れても、中身が壊れます。**

→ [文字を正しく数える](/ja/javascript/learn/string/unicode/)

## スプレッド構文で足りるか

`[...text]` は[type:符号位置]ごとに分けるので、絵文字は割れません。

```js run
const text = 'あ🙂いうえお';

console.log([...text].slice(0, 3).join(''));
```
```output
あ🙂い
```

**多くの場面ではこれで十分です。**

ただし、**複数の絵文字が組み合わさったものは分解されます。**

```js run
const text = '👨‍👩‍👧と旅行';

console.log([...text].slice(0, 2).join('') === '👨‍👩‍👧と');
```
```output
false
```

家族の絵文字は[num:5]つの符号位置でできています。
`slice(0, 2)` では、その[key:2つ目まで]しか取れません。

`Intl.Segmenter` なら、見た目の1文字を[em:1つとして数えます]。

```js run
const text = '👨‍👩‍👧と旅行';
const segmenter = new Intl.Segmenter('ja', { granularity: 'grapheme' });

const cut = [...segmenter.segment(text)].slice(0, 2).map((s) => s.segment).join('');

console.log(cut);
console.log(cut === '👨‍👩‍👧と');
```
```output
👨‍👩‍👧と
true
```

## どれを使うか

| 中身 | 使うもの |
|---|---|
| 日本語・英数字だけと分かっている | `slice()` |
| 利用者が入力する文章 | `Intl.Segmenter` |
| 絵文字が1つずつしか出ない | `[...text]` でも足りる |

**利用者が入れた文字列を切るなら `Intl.Segmenter` を選んでください。**
入力欄に何が来るかは、こちらでは決められません。

[dim:`Intl.Segmenter` は毎回作らず、外に出して使い回すほうが速くなります。上の例は読みやすさのために中で作っています。]

## 単語の途中で切りたくない

英語のように単語の区切りがある文章では、[fn:単語で区切ってから]数える方法もあります。

```js run
const segmenter = new Intl.Segmenter('en', { granularity: 'word' });
const text = 'The quick brown fox';

console.log([...segmenter.segment(text)].filter((s) => s.isWordLike).map((s) => s.segment));
```
```output
[ 'The', 'quick', 'brown', 'fox' ]
```

`isWordLike` が[key:単語らしいもの]だけを残します。空白と記号は落ちます。

[dim:日本語には単語の区切りに空白がありませんが、`Intl.Segmenter` は `'ja'` を指定すれば分けられます。]

## まとめ

- **切る前に長さを比べる。** `slice()` は超えても落ちないので気づけない
- `slice()` が数えるのは[type:符号単位]。**絵文字の途中で割れる**
- `[...text]` は符号位置ごと。多くの場面ではこれで足りる
- 組み合わせた絵文字まで守るなら `Intl.Segmenter`
- **利用者の入力を切るなら `Intl.Segmenter`。** 何が来るかは決められない

切り出しそのものの規則（負の数、範囲外、`substring()` との違い）はこちらにまとめてあります。
→ [文字列を切り出す・整える](/ja/javascript/learn/string/slice/)
