質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.06%

ヌル合体代入演算子(??=)のMDNの記述について

解決済

回答 5

投稿

  • 評価
  • クリップ 4
  • VIEW 1,178

Lhankor_Mhy

score 16504

Logical nullish assignment (??=) - JavaScript | MDN

↑こちらについて、

ヌル合体代入演算子の短絡評価は、つまり x ??= y が次の式と等価であることを意味します:

x ?? (x = y);

そして、次の式とは必ずしも等価になるとは限りません:

x = x ?? y;

という記述があります。(当方による勝手訳)

この後段について等価とならないケースがイメージできないので、教えてください。
セッタが評価されるかどうか、ということなのかな? とは思ったのですが、正しいでしょうか?
その他のケースも考えられますか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

checkベストアンサー

+4

xが単なる変数なら違いはなさそうですが、セッターのあるプロパティにa.b ??= 5;のようにした場合、セッターの実行回数が違ってくる、ように思われます(ちょっと自信はないです)。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/10 17:27

    やはりですか。
    セッタの実行回数が大きな影響を与えるようなケースを思いつかなかったので、あまり気にすることはないですかね……?

    キャンセル

  • 2020/07/10 17:29

    文法的には実装できますけど、「セッターが副作用を持つ」ようなオブジェクトは(デバッグ出力を行う程度なら別ですが)むしろ嫌がらせに近い存在でしょうしね…

    キャンセル

  • 2020/07/10 19:28

    いろいろググってたらこんなのが。
    https://v8.dev/features/logical-assignment

    なるほど、innerHTMLね、と思いました。
    というか、DOMのプロパティは副作用が多そうなので、他にも何かあるかもしれません。

    キャンセル

  • 2020/07/10 19:41

    > DOMのプロパティは副作用が多そうなので

    参照だけでリフローがかかる寸法系プロパティとか、けっこうな魔境ですね。

    キャンセル

+3

それでいいのでは?

セッタが評価されるかどうか、ということなのかな?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/10 16:39

    わたしも仕様を追っているわけではないのでそうだろうなーとしか言えません。
    最近はろくにログインすらしてないので、たまたま回答依頼に気づいて答えています。
    think49 さんが的確な回答をしてくれるのではないかと期待しています。

    キャンセル

  • 2020/07/10 17:27

    ありがとうございます、お呼びだてして失礼しました。
    (一応、ログイン時間は確認したつもりでした)
    他の方の回答をもう少しお待ちしたいと思います。

    キャンセル

+2

リンク先にある "And not equivalent to the following which would always perform an assignment:" は、「常に代入を行う次のもの (x = x ?? y;という文) とは異なります」という意味と思われるので、

x = x ?? y;

は、xがnullかundefinedでなければxにxが代入されますが、
??= を使用する場合はxにxを代入するなんてことはしない、という意味ではないでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/10 19:23

    ありがとうございます。おっしゃるとおりかと思います。

    キャンセル

+1

CoffeeScriptRubyにはかなり昔からあったので、私は何の疑問も思わないのですが、代入の評価という物を考えると違いがはっきりします。

現在の所、対応ブラウザのバージョンは開発版しかないようなので、CoffeeScriptでやってみます。

obj =
  get: 0
  set: 0
  _x: undefined
  reset: ->
    this.get = 0
    this.set = 0
    this._x = undefined
Object.defineProperty obj, 'x',
  get: ->
    this.get++
    this._x
  set: (val) ->
    this.set++
    this._x = val

obj.reset()
obj._x = undefined
obj.x ?= 1
console.log obj.get, obj.set

obj.reset()
obj._x = 0
obj.x ?= 1
console.log obj.get, obj.set

obj.reset()
obj._x = undefined
obj.x = obj.x ? 1
console.log obj.get, obj.set

obj.reset()
obj._x = 1
obj.x = obj.x ? 1
console.log obj.get, obj.set

2番目だけsetがカウントされません。

CoffeeScriptなんてマイナーなAltJSは知らない!という人のためにECMAScriptで書き直しました。

const obj = {
  get: 0,
  set: 0,
  _x: void 0,
  reset: function() {
    this.get = 0;
    this.set = 0;
    this._x = void 0;
  },
  get x() {
    this.get++;
    return this._x;
  },
  set x(val) {
    this.set++;
    return this._x = val;
  }
};

obj.reset();
obj._x = void 0;
obj.x ??= 1;
console.log(obj.get, obj.set);

obj.reset();
obj._x = 0;
obj.x ??= 1;
console.log(obj.get, obj.set);

obj.reset();
obj._x = void 0;
obj.x = obj.x ?? 1;
console.log(obj.get, obj.set);

obj.reset();
obj._x = 1;
obj.x = obj.x ?? 1;
console.log(obj.get, obj.set);

Babelで変換して実行すれば、同じ結果が得られるのが分かると思います。

なお、tc39にあるこの機能の提案に書いてあるとおり、Ruby(2003年の1.8.0には既にあった、というより1995年の初期リリースからかも?)、CoffeeScript(2010年の0.2.6から)、C#(2019年の8.0から)の機能を参考にしています。プロパティに対するゲッターとセッターがメソッドではなく参照と代入として書くスタイルの言語では特に新しくもなんともない機能です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/10 19:25

    ありがとうございます。Babelでは同様のことを試していましたが、そういうことなのか確信が持てず。
    やはりそういうことなんですね。

    キャンセル

-1

x ?? (x = y);

これは単にx ?? yをしつつ、xにyを代入しているだけです

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/10 16:30 編集

    ??演算子はショートサーキットなので、xがnullishでない場合は代入は行われません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.06%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る