storybookで@babel7を使う

参考:

github.com

環境:

"@babel/core": "^7.1.2",
"@storybook/react": "^3.4.11",
"babel-loader": "^8.0.4",

storybookで@babel7を使うために、storybookのwebpack設定を少し上書きする。

.babelrc.js (@babel7に対応したBabel設定ファイル)

module.exports = {
  presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-flow"],
  plugins: ["@babel/plugin-proposal-class-properties"]
}

.storybook/webpack.config.js

const babelConfig = require("../.babelrc")

module.exports = (baseConfig, env, defaultConfig) => {
  defaultConfig.module.rules[0].loader = require.resolve('babel-loader');

  defaultConfig.module.rules[0].query.presets = babelConfig.presets;
  defaultConfig.module.rules[0].query.plugins = babelConfig.plugins;

  return defaultConfig;
};

はてなブログのシンタックスハイライトを jsx に対応させる

やたら重いので消した (2018/10/18)

はてなブログシンタックスハイライトを jsx に対応させる

はてなブログシンタックスハイライトは、2018/08 現在jsxには対応していない。 http://help.hatenablog.com/entry/markup/syntaxhighlight

なので、PrismJSを使って対応させてみる。

PrismJS を読み込む

<head>内に以下を追加。好みのテーマとライブラリを読み込む。CDN で読み込む場合は、ハイライトしたい言語の依存関係にあるものをすべて含める必要がある。依存関係はこちらで確認できる。jsxをハイライトしたいならば、javascriptmarkup、そしてjsxが必要なので、そのとおりに読み込む。

->|html|
    <script type="text/javascript">
        document.addEventListener('DOMContentLoaded', () => {
            const cssSources = [
                "npm/prismjs@1.15.0/themes/prism-dark.min.css"
            ];
            const link = document.createElement('link');
            link.rel = "stylesheet";
            link.href = `https://cdn.jsdelivr.net/combine/${cssSources.join(',')}`;
            document.head.appendChild(link);

            const jsSources = [
                "npm/prismjs@1.15.0",
                "npm/prismjs@1.15.0/components/prism-javascript.min.js",
                "npm/prismjs@1.15.0/components/prism-jsx.min.js",
                "npm/prismjs@1.15.0/components/prism-markup.min.js"
            ];
        });
    </script>

PrismJS では、パースされたトークン(構文の最小構成単位)にkeywordクラスが与えられることがあるが、はてなブログのデフォルトの CSS!importantでそれに対して適用されてしまうので、打ち消す必要がある:

->|html|
    <style>
        .token.atrule,
        .token.attr-value,
        .token.keyword {
            color: #d1939e !important; /* テーマによって値は異なる */
        }
    </style>

ハイライトさせる

PrismJS は、次のようなcodeブロックの中身をすべてパースし、クラス名に沿ってハイライトしてくれる。

->|html|
<pre><code class="language-css">p { color: red }</code></pre>

が、マークダウン記法に慣れているといちいちタグで囲むのも、クラス名を割り当てるのも少々めんどくさいので、次のスクリプトを追加する。ついでに<>エスケープする:

->|js|
        document.addEventListener('DOMContentLoaded', () => {

            /* 略 */

            var pres = document.querySelectorAll('pre');
            pres.forEach((pre) => {
                const langRegExp = /^-&gt;\|(.*)\|\n/
                const lang = pre.innerHTML.match(langRegExp);
                if (!lang) return;
                const content = pre.innerHTML
                    .replace(langRegExp, '')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;')
                pre.innerHTML = `<code class="language-${lang[1]}">${content}</code>`;
                pre.setAttribute('class', `language-${lang[1]}`);
            });
        });

これによって、次のようにブロックのはじめに->|jsx|を書くと:

->|none|
->|jsx| 
class Todo component Component {
  render () {
    return <div />
  }
}

PrismJS がパース可能な HTML が作り出されるようになる:

->|none|
<pre class="language-jsx">
  <code class="language-jsx">
    class Todo component Component {
      render () {
         return <div />
      }
    }
  </code>
</pre>

プラグイン

PrismJS には、プラグインがいくつか用意されており、拡張が可能。ただ、CSSはてなのデフォルト CSS とバッティングする可能性があるので慎重に。

Line Numbers

行番号を追加するプラグインを入れてみる。 まずは、プラグイン用のスクリプトと、CSS を追加する:

->|js|
        document.addEventListener('DOMContentLoaded', () => {
            const cssSources = [
                                 /* 略 */
                "npm/prismjs@1.15.0/plugins/line-numbers/prism-line-numbers.min.css"
            ];
            const link = document.createElement('link');
            link.rel = "stylesheet";
            link.href = `https://cdn.jsdelivr.net/combine/${cssSources.join(',')}`;
            document.head.appendChild(link);

            const jsSources = [
                                 /* 略 */
                "npm/prismjs@1.15.0/plugins/line-numbers/prism-line-numbers.min.js",
            ];
            const script = document.createElement('script');
            script.src = `https://cdn.jsdelivr.net/combine/${jsSources.join(',')}`
            document.body.appendChild(script);

            /* 略 */
        });

preタグのクラスにline-numbersを加える:

->|js|
        document.addEventListener('DOMContentLoaded', () => {
            /* 略 */
            pres.forEach((pre) => {
                /* 略 */
                pre.setAttribute('class', `line-numbers language-${lang[1]}`);
            });
        });

これだけだと、プラグインが追加した行番号のフォントサイズとはてなのデフォルトのフォントサイズが違うためか、ずれてしまうことがあるので、行番号の方を修正する:

->|html|
<style>
/* 略 */
code[class*=language-], pre[class*=language-] {
  font-size: inherit;
}
</style>

Copy to Clipboard

ツールバーにコピーボタンを加える。 まずは同じように、JS と CSS ファイルを読み込む:

->|js|
        document.addEventListener('DOMContentLoaded', () => {
            const cssSources = [
                /* 略 */
                "npm/prismjs@1.15.0/plugins/toolbar/prism-toolbar.min.css"
            ];
            const link = document.createElement('link');
            link.rel = "stylesheet";
            link.href = `https://cdn.jsdelivr.net/combine/${cssSources.join(',')}`;
            document.head.appendChild(link);

            const jsSources = [
                /* 略 */
                "npm/prismjs@1.15.0/plugins/toolbar/prism-toolbar.min.js",
                "npm/prismjs@1.15.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"
            ];
            const script = document.createElement('script');
            script.src = `https://cdn.jsdelivr.net/combine/${jsSources.join(',')}`
            document.body.appendChild(script);

            /* 略 */
        });

これで、ツールバーにコピーボタンが追加される。

しかし、デザインが好みじゃないので、カスタマイズしてみる。PrismJS の読み込み後に ClipboardJS を読み込み、そのコールバック内でコピーボタンを追加:

->|js|
        document.addEventListener('DOMContentLoaded', () => {
            /* 略 */
            const jsSources = [
                /* 略 */
                "npm/prismjs@1.15.0/plugins/toolbar/prism-toolbar.min.js"
            ];
            const script = document.createElement('script');
            script.src = `https://cdn.jsdelivr.net/combine/${jsSources.join(',')}`
            script.onload = () => {
                const clipboardJSSrc = document.createElement("script");
                clipboardJSSrc.src = "https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js";
                clipboardJSSrc.onload = () => {
                    addCopyBotton()
                }
                document.querySelector("head").appendChild(clipboardJSSrc);
            }
            document.head.appendChild(script);
            /* 略 */
        })

        function addCopyBotton() {
            Prism.plugins.toolbar.registerButton('hello-world', (env) => {
                var linkCopy = document.createElement('a');
                linkCopy.innerHTML = '<i class="copy-icon material-icons">file_copy</i>'
                registerClipboard();
                return linkCopy;

                function registerClipboard() {
                    var clip = new ClipboardJS(linkCopy, {
                        'text': function () {
                            return env.code;
                        }
                    });
                }
            });
        }

linkCopyのアイコンはお好みのものをどうぞ。

正直カスタマイズがかなりめんどくさかったので、次なんかやるとしたらブログ移行してからかな…

TypeScript と Material-UI withStyles

TypeScript と Material-UI withStyles

ググって最初に出てきたのを試したけどだめだったが、material-ui のリポジトリ見てたら TypeScript のサンプルがあったので、これを見ながら使ってみる。

インストール

まずは material-ui をインストール:

->|bash|
yarn add @material-ui/core

バージョンはこんなかんじ:

->|json|
  "dependencies": {
    "@material-ui/core": "^1.5.1",
  },
  "devDependencies": {
    "typescript": "^3.0.1"
  },

TypeScript v2.7 くらいでやったときはコンパイルエラー起きたので注意。

withStyles

そのまま使うならなんにも考えなくていいんだけど、withStylesclasses をインジェクションしたい場合、ちょっとトリッキーな使い方になる。

createThemeは型コンパイルを行うだけで、ランタイム時には何もしない関数。WithStylesclasses に型を提供する。

->|tsx|
import Button from "@material-ui/core/Button";
import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles";
import { Theme } from "@material-ui/core/styles/createMuiTheme";
import * as React from "react";

interface OwnProps {
  label: string;
}

const styles = (theme: Theme) => {
  return createStyles({
    button: {
      margin: theme.spacing.unit
    }
  });
};

type MyButtonProps = OwnProps & WithStyles<typeof styles>;

const MyButton: React.SFC<MyButtonProps> = ({ label, classes }) => {
  return (
    <Button color="primary" className={classes.button}>
      {label}
    </Button>
  );
};

export default withStyles(styles)(MyButton);

React v16.3 の Context と Fragment

よく使いそうな Context と Fragment についてメモ。

Context

そもそも、 Context はあらゆる階層のコンポーネント間で、データを共有する機能を持ちます。

しかし、v16.3 以前の React における Context には以下の注意書きがありました。

If you want your application to be stable, don’t use context. It is an experimental API and it is likely to break in future releases of React.

アプリを安定させたいなら使わないでね。実験的 な API なので、将来的に使えなくなるかもよ。

この注意書きは v16.3 で Context が変更されると共に削除されています。v16.3 で刷新された Context の使い方を見ていきましょう:

<Family /> を飛ばして、 <Person /> に値が渡されていることがわかります。

import React, { Component } from "react";
import { render } from "react-dom";

const Context = React.createContext();

function Family() {
  return <Person />;
}

function Person() {
  return (
    <Context.Consumer>
      {({ name, age }) => (
        <React.Fragment>
          <p>Name: {name}</p>
          <p>Age: {age}</p>
        </React.Fragment>
      )}
    </Context.Consumer>
  );
}

class App extends Component {
  state = {
    name: "Mark",
    age: 12
  };
  render() {
    return (
      <Context.Provider
        value={{
          name: this.state.name,
          age: this.state.age
        }}
      >
        <Family />
      </Context.Provider>
    );
  }
}

render(<App />, document.getElementById("root"));

新しい Context は次の 3 つの API から構成されます:

createContext()

const { Provider, Consumer } = React.createContext(defaultValue);

Consumer と Provider を返す関数で、任意のデフォルト値を引数に取ります。

Provider

<Provider value={/* some value */}>

高レベル階層で使われ、value プロパティを受け取ります。

Consumer

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

Provider 以下の階層で使われ、value を受け取り、JSX を返す関数を受け取ります。 Provider の value が変更された場合、このコンポーネントは再描画されます。

Fragment

上記の例で使用されている Fragment は v16 から追加されたビルトインコンポーネントで、リストを返すときにすごく便利です。

例えばテーブルを作りたい場合:

class App extends Component {
  render() {
    return (
      <table>
        <tbody>
          <tr>
            <Columns />
          </tr>
        </tbody>
      </table>
    );
  }
}

この Columns コンポーネントが複数の td 要素を返すとき、次の div のようなラッパーを用意する必要がありました:

function Columns(props) {
  return (
    <div>
      <td>Hello</td>
      <td>World</td>
    </div>
  );
}

これでは警告が出てしまいます。実際のアウトプットは次のようになるためです:

class App extends Component {
  render() {
    return (
      <table>
        <tbody>
          <tr>
            <div>
              <td>Hello</td>
              <td>World</td>
            </div>
          </tr>
        </tbody>
      </table>
    );
  }
}
// Warning: validateDOMNesting(...): <div> cannot appear as a child of <tr>.

個人的には次のように配列を返すようにしてたりしたんですが、正しい作法なのかどうかわからないし、読みにくい:

function Columns() {
  return [<td>Hello</td>, <td>World</td>];
}

v16 以後は次のように書くことが出来ます:

function Columns(props) {
  return (
    <React.Fragment>
      <td>Hello</td>
      <td>World</td>
    </React.Fragment>
  );
}

便利ですね。

React Component で作る window.confirm 代替品

window.confirmESLint でエラーが出るし、UI が良くない。なので似たような API で扱うことの出来る window.confirm の代用品を作ってみた。

参考: http://reactkungfu.com/2015/08/beautiful-confirm-window-with-react/

環境: React (16.1.1), React-Bootstrap (0.32.1)

Confirmation Modal

まずはスタイリッシュな Modal Window を作成。Modal が作れるなら別に React-Bootstrap じゃなくても良い。

class ConfirmationModal extends Component {
  constructor() {
    super();
    this.state = {
      show: true,
    };
  }

  abort = () => {
    this.setState({ show: false });
  };

  confirm = () => {
    this.setState({ show: false });
  };

  render() {
    const { show } = this.state;
    const { body, title } = this.props;

    return (
      <div className="static-modal">
        <Modal show={show} onHide={this.abort} backdrop>
          <Modal.Header>
            <Modal.Title>{title}</Modal.Title>
          </Modal.Header>
          <Modal.Body>{body}</Modal.Body>
          <Modal.Footer>
            <Button onClick={this.abort}>Cancel</Button>
            <Button
              className="button-l"
              bsStyle="primary"
              onClick={this.confirm}
            >
              Confirm
            </Button>
          </Modal.Footer>
        </Modal>
      </div>
    );
  }
}

confirm 関数で div を作成してそこに ConfirmationModal コンポーネントを描画。resolvecleanup を渡す。 Promiseresolve / reject 時に cleanup 関数で DOM を掃除しないとモーダル作成時に無限に div が増えていくことになる。

const confirm = ({ body, title }) => {
  const wrapper = document.body.appendChild(document.createElement('div'));
  const cleanup = () => {
    ReactDOM.unmountComponentAtNode(wrapper);
    return setTimeout(() => wrapper.remove());
  };
  const promise = new Promise((resolve, reject) => {
    try {
      ReactDOM.render(
        <ConfirmationModal
          cleanup={cleanup}
          resolve={resolve}
          title={title}
          body={body}
        />,
        wrapper
      );
    } catch (e) {
      cleanup();
      reject(e);
      throw e;
    }
  });
  return promise;
};

ConfirmationModal コンポーネントabort / confirm メソッドで真偽値を解決する。

class ConfirmationModal extends Component {
  constructor() {
    super();
    this.state = {
      show: true,
    };
  }

  abort = () => {
    const { resolve, cleanup } = this.props;
    this.setState({ show: false }, () => {
      resolve(false);
      cleanup();
    });
  };

  confirm = () => {
    const { resolve, cleanup } = this.props;
    this.setState({ show: false }, () => {
      resolve(true);
      cleanup();
    });
  };

  ...
}

使い方

ConfirmationModal の応答結果 (真偽値) を resolve しているので、async / await で次のように使うことが出来る:

async function() {
    const isConfirmed = await confirm({
      title: 'CAUTION',
      body: 'Are you sure?',
    });
    console.log(isConfirmed); // boolean
}

confirm

HOC

で、これを HOC 化したのがこちら

github.com

// confim.js
import { createConfirm } from 'react-confirm-decorator';

import ConfirmationModal from './ConfirmationModal';

const confirm = props => createConfirm(ConfirmationModal, props);

export default confirm;

好きな Modal のライブラリを使うことが出来る:

// ConfirmationModal.js
import React from 'react';
import Modal from 'react-bootstrap/lib/Modal';
import Button from 'react-bootstrap/lib/Button';

import { setConfirm } from 'react-confirm-decorator';

const ConfirmationModal = ({ show, confirm, abort, title, body }) => (
  <div className="static-modal">
    <Modal show={show} onHide={abort} backdrop>
      <Modal.Header>
        <Modal.Title>{title}</Modal.Title>
      </Modal.Header>
      <Modal.Body>{body}</Modal.Body>
      <Modal.Footer>
        <Button onClick={abort}>Cancel</Button>
        <Button className="button-l" bsStyle="primary" onClick={confirm}>
          Confirm
        </Button>
      </Modal.Footer>
    </Modal>
  </div>
);

export default setConfirm(ConfirmationModal);
import confirm from './confirm';

async function() {
    const isConfirmed = await confirm({
      title: 'CAUTION',
      body: 'Are you sure?',
    });
    console.log(isConfirmed); // boolean
}

react-confirm という似たふるまいのライブラリを途中見つけたけど、やりたかったこと (window.confirmの代用) とはちょっと違った。

僕らは JavaScript を知らない - レキシカルスコープとクロージャとガベージコレクション Lexical Scope, Closure and Garbage Collection

参考:

レキシカルスコープ

スコープ

そもそもスコープとは、変数や関数が参照できる範囲のことを言います。スコープの中で宣言した変数は同じスコープ内でのみ参照でき、スコープの外側から参照されることはありません。

例えば次のコードでは関数 foo 内で宣言された変数 b は関数 foo 内では参照できますが、関数の外側で参照しようとすると ReferenceError となります:

function foo(a) {

    var b = a * 2;

    console.log( b ); // 6
}

foo( 3 );

console.log( b ) // ReferenceError: b is not defined

この関数によるスコープのことを関数スコープと呼びます。

スコープチェーン

次のコードでは変数 a は関数内に宣言されてませんが、 ReferenceError になることはありません。なぜなら、 foo 関数のスコープの外側のスコープに存在する変数 a を使用するためです。現在のスコープに変数が存在しない場合、そのスコープをネストするより外側のスコープまで参照の範囲を広げていくこの仕組みを、スコープチェーンと呼びます。

function foo() {

    var b = a * 2;

    console.log( b );
}

var a = 2;

foo();

レキシカルスコープ

次のコードでは、 bar の実行はグローバルスコープにて行われましたが、返ってくる値はグローバルスコープに存在する x ではなく、 foo による関数スコープ内の x です。 このようにスコープが関数を実行した時ではなく、宣言したときに決定される特徴を持つスコープをレキシカルスコープと呼びます。

var x = 'global';

function foo(){
  var x = 'local';
  return function(){ return x }
};


/*
実体は以下の通りで、グローバルスコープにおいて実行されるが、 返り値は `foo` 関数スコープの `x`
function() { return x }
*/
var bar = foo();

x; // global
bar(); // local

JavaScript はコードを実行する前に、そのコードを意味のある最小単位(トークン, Token) へと分解するレキシング (Lexising) を行います。 コード実行前のレキシング時に決定されるため、レキシカル (Lexical) スコープと呼びます。

上記のコードのように、スコープの階層が異なれば同じ名前を持つ変数を宣言することができます。これをシャドーイング (shadowing) と呼びます。 barfoo スコープ内に変数 x を見つけたため、これを使用し、グローバルスコープに存在する x は無視されることになりました。グローバル変数 x がローカル変数 x の影に隠れた (シャドー, shadow) イメージです。

クロージャ

クロージャとは「関数が内包するスコープを保持する性質」のことを言い、クロージャ関数とはその性質をもつ関数のことを言います。

先程のコードも実はこのクロージャを利用していますが、いまいちわかりにくいので、もう少し分かりやすいコードを見てみましょう:

var counter = function(initialValue) {
    var count = initialValue;
    return function() {
            count++;
            return count;
    }
}


/*
    var count = 7;
    return function() {
            count++;
            return count;
    }
*/
var myCounter7 = counter(7);


/*
    var count = 15;
    return function() {
            count++;
            return count;
    }
*/
var myCounter15 = counter(15);

myCounter7();   // 8
myCounter7();   // 9

myCounter15();  // 16

上記の例では myCounter7 は、作成された時点での内部スコープを保持するクロージャ関数が代入されており、この中では count の値 7 が取り込まれています。myCounter15() では、count の値 15 が保持されています。また、それぞれ関数実行後も count の値が保持され続けていることも確認できます。

ではなぜ、レキシカルスコープを、そしてその内部の変数の値を関数実行後も保持することができるのでしょうか?それを知るためには、メモリの管理について理解する必要があります。

ガベージコレクション

変数や関数を宣言するとデータはメモリ領域を確保しますが、メモリ解放をしないとメモリリークと呼ばれる、ソフトウェアが使用できるメモリ領域が減っていく現象が発生します。

したがってメモリリークを防ぐためには適切なメモリ管理を行う必要があります。その方法は言語により異なりますが、 JavaScript ではガベージコレクションが採用されています。

ガベージコレクションでは、それを備えていない言語とは異なり、割り当てられたメモリが使用されてすでに必要なくなったときに自動的に開放されます。

どのようなときに、ガベージコレクションは割り当てられたメモリを「不要」と判断するのでしょうか。主にはその値が「参照」されているかどうかです。どこからも参照されなくなったら、それはガベージコレクションの対象となります。

具体例を見ましょう:

var obj = { x: 'foo' }
obj = null;

オブジェクト { x: 'foo'} は宣言時、変数 obj から参照されています。したがって明らかにガベージコレクションの対象にはなりませんが、その後、 変数に null を代入されたために、 { x: 'foo'} を参照しているものはなくなりました。どこからも参照されなくなった時点で、このデータは不要と判断され、ガベージコレクションの対象となり、メモリ上から開放されます。

では、ここで先程のクロージャ関数の例を見てみましょう:

var counter = function(initialValue) {
    var count = initialValue;
    return function() {
            count++;
            return count;
    }
}

/*
function(7) {
    var count = 7; 
    return function() {
            count++;
            return count;
    }
}
*/
var myCounter7 = counter(7);

/*
実行している関数は以下の通りだが、`count` はグローバルスコープではなく、
関数の宣言時の外部スコープの変数 `count` を参照している
var count = 7;
return function() {
        count++;
        return count;
}
*/
myCounter7(); // 8

/*
もう一度実行したときも、 `count` の値は `8` のまま参照され続けているので返り値が `9` になる
*/
myCounter7(); // 9

このように、countガベージコレクションの対象とならずにデータを保持することができる理由は、関数が宣言されたときの変数 count を参照するというレキシカルスコープの性質と、参照されている count のデータはメモリ上から開放されず、データが保持され続けるというガベージコレクションの性質、これらふたつの性質を持っているから、ということができます。

僕らは JavaScript を知らない - データ型と参照 Data Type and Reference

参考

データ型

JavaScript は動的型付け言語と呼ばれ、変数の型は存在しません。変数はどのデータ型でも持つことが可能です。

let value = '1'; // String
value = 1; // Number
value = true; // Boolean

データ型には、次の7つの型があります:

  • Boolean
  • Null
  • Undefined
  • Number
  • String
  • Symbol
  • Object

これらのうち、 object 以外はプリミティブ型と呼ばれます。また、オブジェクトはプリミティブ型やオブジェクトの集合です。 typeof 演算子を使うと、データ型の文字列を取得することが出来ます:

typeof undefined     === "undefined"; // true
typeof true          === "boolean";   // true
typeof 1             === "number";    // true
typeof "1"           === "string";    // true
typeof { a: 1 }      === "object";    // true
typeof Symbol()      === "symbol";    // true

ただし、 null だけは "null" ではなく、 "object" を返します。これは JavaScript のバグですが、この挙動に依ったコードがあまりにも多く、これを修正することでより多くのバグを生むため、このままにされているという経緯があります。

Undefined Type

JavaScript において、変数の型は存在せず、データ型のみ存在します。例えば次のようなコードを実行したとします:

var a;

a; // undefined
b; // ReferenceError: b is not defined

これは、変数 a の宣言時の初期値が undefined 型であるためです。

しかし、 typeof 演算子は宣言していない変数 b に対して次のように動作します:

var a;

typeof a; // undefined
typeof b; // undefined

これは安全装置として働きます。例えば、グローバルネームスペースに myFunction が存在するかどうかを確かめ、存在しない場合新たに myFunction を定義するためのコードは次のようになります:

// Uncaught ReferenceError: myFunction is not defined
if (!myFunction) {
    myFunction = function() { console.log('This is my function!') }
}

// typeof 演算子によってエラーは起きない
if (typeof myFunction === undefined) {
    myFunction = function() { console.log('This is my function!') }
}

参照

C++ において次のコードでは、変数 a は変数 myNumber の参照となります。ここでいう参照とは「別名」と考えてよく、 myNumber の別名である a の変更は myNumber にも影響します。メモリ上に変数 myNumber を作成して、そのアドレスを変数 a に渡しているため、変数から変数への参照と呼ばれます。

void myFunction( int& a )
{
    a *= 2;
}
int main()
{
    int myNumber = 2;
    myFunction( myNumber );
}

対して、JavaScript では変数に対する参照はありませんが、値に対する参照を行います。 JavaScript においてオブジェクトは参照型です。2つの異なるオブジェクトは、たとえ同じプロパティを持っていたとしても等値とはみなされません。具体例を確認しましょう:

// メモリ上に `[1, 2, 3]` を作成し、そのアドレスを `a` に代入
// また、新たにメモリ上の別の場所に `[1, 2, 3]` を作成し、そのアドレスを `b` に代入
var a = [ 1, 2, 3 ];
var b = [ 1, 2, 3 ];

// アドレスが異なるので等値ではない
a === b // false

//  `b` の値のアドレスを `a` に代入
a = b

// アドレスが同じなので等値
a === b // true

C++ や他の言語とは異なり、 JavaScript のオブジェクト変数は値のアドレスを保持しているだけで、他の変数のアドレスを保持しません。変数 c d は値を共有しているので、値に対する変更はどちらの変数からでも確認することができます。アドレスの代入、参照の値渡しと呼ばれます。

var c = [ 1, 2, 3 ];

// アドレスの代入
var d = c;

// 参照する値に対する変更
d.push( 4 );

c; // [ 1, 2, 3, 4 ]
d; // [ 1, 2, 3, 4 ]

以下のコードにおいて、変数 b に対する変更は変数 a に影響を与えることはありません。なぜならアドレスが異なるからです。

var a = [ 1, 2, 3 ];
// 参照の代入
var b = a;

// 空の配列リテラルから配列を新たに作成し、そのアドレスを代入
b = [];
a; // [ 1, 2, 3 ]
b; // []

元のオブジェクトを空にしたい場合は、既存の配列に対する変更を行います:

var a = [ 1, 2, 3 ];
var b = a;

b.length = 0;

a; // []
b; // []

オブジェクトの値だけを渡したい場合は Array.prototype.slice メソッドなどを使用します。この場合、b のアドレスと c のアドレスは異なるため、c に対する変更は影響を受けません:

var a = { hoge: 'hoge' }
var b = [ a, 1 ]
var c = b.slice();
c.push( 2 );

a // { hoge: 'hoge' }
b // [ { hoge: 'hoge' }, 1 ]
c // [ { hoge: 'hoge' }, 1, 2 ]

シャローコピー (1段階の深さのコピー) なので、 オブジェクト a の値ではなくアドレスがコピーされます:

var a = { hoge: 'hoge' }
var b = [ a, 1 ]
var c = b.slice();

a.fuga = 'fuga';
c.push( 2 );

a // { hoge: 'hoge', fuga: 'fuga' }
b // [ { hoge: 'hoge', fuga: 'fuga' }, 1 ]
c // [ { hoge: 'hoge', fuga: 'fuga' }, 1, 2 ]

本来プリミティブ型は値を保持しておくことが出来ないのですが、オブジェクトが参照型であることを逆手に取ってオブジェクトでラップしてやれば、プリミティブ型も「参照渡し」することが可能で、関数内の変数 y.b に加えた変更は変数 obj.y にも反映されます:

var a = 100;

(function(x) {
    x = 50;
})(a);

a // 100;

var obj = { b: 100 };

(function(y) {
    y.b = 50;
})(obj);

obj.b // 50;