Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
181
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

引数を渡さなかった際にデフォルト値を使う場合の注意点

よくやる variable = argument || default_value; パターン は割と罠

jQuery のソースコードなり、色んな実装で見るこのパターンのことです。

/**
  * @param {number} value
  * @param {number=} opt_value
  * @param {(function():*)=} opt_callback
  */
function doSomething(value, opt_value) {
   var option = opt_value || 10; // opt_value が渡されていなかったらデフォルト
   /**
     * 以下なんらかの処理
     */
}

これは undefined を boolean キャストして false が返る仕様を利用したテクニックですが、実際は割と限定された用途で書けるものだと捉えた方が良いです。

まずいパターン

このようなコンストラクタを作った場合

function Hoge(foo, opt_bar) {
  this.foo = foo || 'foo';
  this.bar = opt_bar || 10;
}

目的を持って null を渡す場合に無効化される

nullundefined 同様 boolean として扱うと false を返します。

var hoge = new Hoge('hey', null);
hoge.bar; // 10

opt_value が 0, false が渡って、それに意味がある場合

同様に boolean として扱うと false になるものもすべて無効化されます。
ありがちなのは、明示的に 0 を渡して無効化されてるケアレスミス

var hoge = new Hoge('hey', 0);
hoge.bar; // 10

opt_value をバリデーションしないと挙動が想定してない結果になる場合

利用側でそもそも渡すべきではない、という方針もありだけど (というか、そうであれば optional arguments を設計に入れない方が良い)、こういった失敗のパターン

/**
  * @param {Array.<number>} scores
  * @param {number=} opt_index
  * @return {number}
  */
function average(scores, opt_index) {
   var l = opt_index || scores.length, // option 引数で配列を絞り込むつもり
         sum = 0;
   for (i = 0; i < l; i++) {
       sum += scores[i]; // l > scores.length であった場合 undefined が加算
   }
   return sum / l; //  l > scores.length であった場合 sum が NaN になってる
}

対策

opt_value === undefined ? default_value : opt_value; パターンにする

関数宣言内では typeof undefined_value == 'undefined' としなくても RefferenceError にはなりません。以下のように三項演算子で書くと、確実に引数が渡されていなかった、あるいは undefined を渡していた時のみデフォルト値を代入します。

function Hoge(foo, opt_bar) {
  this.foo = foo || 'foo';
  this.bar = opt_bar === undefined ? 10 : opt_bar;
}

supplement() メソッドみたいなのを作る

バリデーションも込みで効率化したい場合、(というか、↑ はたくさん書くとすごい読みづらいし糞コード化する)

supplement(default_value, opt_arg, opt_callback)

/**
 * @description 関数宣言内で引数をバリデーションしたり
 * @param {*} default_value
 * @param {(*)=} opt_arg
 * @param {(function(*,*):*)=} opt_callback
 * @return {*}
 */
function supplement(default_value, opt_arg, opt_callback) {
  if (opt_arg === undefined) {
    return default_value;
  }
  if (opt_callback === undefined) {
    return opt_arg;
  }
  return opt_callback(default_value, opt_arg);
}

こう使う

これで 先のサンプルをこう修正します。

/**
  * @param {Array.<number>} scores
  * @param {number=} opt_index
  * @return {number}
  */
function average(scores, opt_index) {
   function validate(def, val) {
     return Math.max(Math.min(def, val), 0);
   }
   var l = supplement(score.length, opt_index, validate),
         sum = 0;
   for (i = 0; i < l; i++) {
       sum += scores[i];
   }
   return sum / l;
}

以上です

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
181
Help us understand the problem. What are the problem?