3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScript に於ける 参照渡し / 参照変数 の実現可能性について

Last updated at Posted at 2024-08-29

JavaScript には 現状、参照渡し / 参照変数 にあたる構文 は ありません

レギュレーション

ここで言う 参照渡し は 実引数で渡した変数 に対して 仮引数 で渡された変数への代入が 反映される状態のことを指します。
参照変数 も 上記の様な 変数への代入が反映される状態のことを指しています。

プロパティへの代入が 反映されるのはここでは参照渡し や参照変数とは言いません

ここで、構文が無いと言っているのは 変数に限らなければ Proxy を噛ませて プロパティ経由で 代入を外の変数に反映することはできるからです。

つまり、 変数への 代入に Proxy が挟める様になる もしくは Proxy が挟まっていることを隠蔽できる構文ができれば参照渡しと言えます。

vue の場合

例えば vue だと Proxy を経由して次の様な構文で仮引数 の プロパティへの代入を反映することはできます。

import {ref} from "vue";
const v = ref(0);

function f(v2) {
 v2.value = 2;
}
f(v);
console.log(v.value);
// -> 2

そうです。このままではレギュレーション違反です。参照渡しには なりません

ただ、 vue はマクロを噛ませるタイプの言語な為、 template上では .value を書かなくてもいいです。

<script setup>
import { ref } from "vue";
const v = ref(0);
</script>
<template>
  <button @click="v = 2">click me</button>
  <div>{{ v }}</div>
</template>

playground

そう隠蔽できればいいのです。

Reactivity Transform

vue 3 では 一時期 .value を隠蔽する 構文を提供する マクロが実験的に提供されていた頃があります。

Reactivity Transform は実験的な機能でしたが、最新の 3.4 のリリースで削除されました。

次の様な構文です。

<script setup>
let count = $ref(0);

console.log(count);
// -> 0

function increment() {
  count++;
}
</script>

<template>
  <button @click="increment">{{ count }}</button>
</template>

これの script タグ内の部分ははマクロによって次の様に展開されます。

import { ref } from 'vue'

let count = ref(0)

console.log(count.value)

function increment() {
  count.value++
}

そう .value が隠蔽されて 参照渡しの実装としてはそれっぽいです。

ただ、これが 実験的な機能からそのまま廃止されたのは .value をつけないと それが ref() であるかどうかがわからず、 また、 このマクロを適用していない箇所との運用が やりにくく それらにより強い反対を受けて廃止となりました。

まぁ、わかりにくくなる要因になるので順当だとは思います。

proposal-refs

ECMAScript の未提出の野良プロポーザル に proposal-refs というのがあります。

Reference (ref) declarations and expressions for ECMAScript

とあり、これは次の様な構文を提供するプロポーザルです。

function max(ref first, ref second, ref third) {
  const ref max = first > second ? ref first : ref second;
  return max > third ? ref max : ref third;
}

let x = 1, y = 2, z = 3;
let ref w = max(ref x, ref y, ref z);
w = 4;
console.log(x); // 1
console.log(y); // 2
console.log(z); // 4

これはつまり 下記の様に解釈されます

const __ref = (get, set) => Object.freeze(Object.create(null, { value: { get, set } }));

function max(ref_first, ref_second, ref_third) {
  const ref_max = ref_first.value > ref_second.value ? ref_first : ref_second;
  return ref_max.value > ref_third.value ? ref_max : ref_third;
}

let x = 1, y = 2, z = 3;
const ref_x = __ref(() => x, _ => x = _);
const ref_y = __ref(() => y, _ => y = _);
const ref_z = __ref(() => z, _ => z = _);
const ref_w = max(ref_x, ref_y, ref_z);
ref_w.value = 4;
console.log(x); // 1
console.log(y); // 2
console.log(z); // 4

まさに 参照渡し ぽくて良いですね。

ただ、 これは ECMAScript の proposal として 提案には上がっていない様なので今後どうなるか……… vue の件もあるので多分上がらないとは思います。

ふまえて

javascript で 参照渡し / 参照変数 を使える構文を用意すると わかりにくいのはわかっていただけましたでしょうか? 既にあるコードとの兼ね合いもあり、組み込むのは難しい気がします。

ただ、class に於ける private 変数が #hoge の様な プレフィックス構文だったこともありましたので 参照変数 の prefix 指定すればわかりやすかったのでは?みたいなところはあります。

例えば……

const ref ^value = ref v;

の様な……。

※ 冗談です。既に使える prefix に空きは無かった筈です。

以上。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?