2018-05-20T04:00:17Z

JavaScriptのクラス記述方法のまとめ(2018年)

JavaScriptのクラス記述方法のまとめ。まとめてみると意外と知らないことも多くてぼちぼち収穫がありました。ご指摘等あれば、ぜひ御教示ください。

クラス記述

  • ES6のクラス
    • 制約が多い
      • static変数を記述できない
      • private変数を記述できない
  • Object.createの継承
    • 親、子の継承ならば問題は発生しない
    • 親、子、孫の継承で問題が発生する
  • inherits()の継承
    • 採用
  • Babel, TypeScript
    • 時間の余裕があれば、覚えたい

ソース

/*!
 * Sample.js
 *
 * Copyright (c) 2018 toshi
 * Released under the MIT license.
 * see https://opensource.org/licenses/MIT
 *
 * The inherits() function is:
 * Copyright (c) Isaac Z. Schlueter
 * Released under the ISC license.
 * see https://opensource.org/licenses/ISC
 */

if (typeof(inherits) === 'undefined') {
  // 継承
  // see https://github.com/isaacs/inherits/blob/master/inherits_browser.js
  function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    var TempCtor = function () {}
    TempCtor.prototype = superCtor.prototype
    ctor.prototype = new TempCtor()
    ctor.prototype.constructor = ctor
  }
}

(function(global, factory) {
  if (typeof define === 'function' && define.amd) {
    define(factory);
  } else if (typeof exports === 'object') {
    module.exports = factory();
  } else if (!global.SuperClass) {
    global.SuperClass = factory();
  }
})(this, function() {
  "use strict";
  
  var _this = void 0;
  var _super= Object;
  var _superPrivateVariable = 'super private variable';
  
  function _superPrivateMethod() {
    console.log('_superPrivateMethod() {');
    console.group();
      console.log('print '+_superPrivateVariable);
    console.groupEnd();
    console.log('}');
  }
  
  _this = function SuperClass_constructor() {
    console.log('SuperClass_constructor() {');
    console.group();
      SuperClass_initialize.apply(this, arguments);
    console.groupEnd();
    console.log('}');
  }
  inherits(_this, _super);
  
  _this.superStaticVariable = 'super static variable';
  
  _this.staticMethod = function SuperClass_staticMethod() {
    console.log('SuperClass_staticMethod() {');
    console.group();
      console.log('print '+_this.superStaticVariable);
    console.groupEnd();
    console.log('}');
  }
  
  function SuperClass_initialize(arg0, arg1) {
    console.log('SuperClass_initialize('+arg0+', '+arg1+');');
    
    this.superPublicVariable = 'super public variable';
  }
  
  _this.prototype.publicMethod = function SuperClass_publicMethod() {
    console.log('SuperClass_publicMethod() {');
    console.group();
      console.log('print '+this.superPublicVariable);
    console.groupEnd();
    console.log('}');
  }
  
  _this.prototype.superPublicMethod = function SuperClass_superPublicMethod() {
    console.log('SuperClass_superPublicMethod() {');
    console.group();
      console.log('print '+this.superPublicVariable);
    console.groupEnd();
    console.log('}');
  }
  
  _this.prototype.accessor = function SuperClass_accessor() {
    console.log('SuperClass_accessor() {');
    console.group();
      console.log('call _superPrivateMethod() {');
      console.group();
        _superPrivateMethod();
      console.groupEnd();
      console.log('}');
      console.log('print '+_superPrivateVariable);
      console.log('print '+_this.superStaticVariable);
      console.log('print '+this.superPublicVariable);
    console.groupEnd();
    console.log('}');
  }
  
  return _this;
});

// UMD (Universal Module Definition)
// see https://github.com/umdjs/umd/blob/master/templates/returnExports.js
(function BaseClass_loader(global, factory) {     // グローバル汚染防止のため、無名関数のほうが良い
  // 実行環境用の処理
  if (typeof define === 'function' && define.amd) {
    // AMD (RequireJSなど)
    console.log('amd');
    define(['SuperClass'], factory);
  } else if (typeof exports === 'object') {
    // Node.js
    console.log('node');
    module.exports = factory(require('SuperClass'));
  } else if (!global.BaseClass) {
    console.log('global');
    global.BaseClass = factory(global.SuperClass);
  }
})(this, function BaseClass_factory(SuperClass) { // グローバル汚染防止のため、無名関数のほうが良い
  "use strict";
  
  var _this = void 0;
  var _super= SuperClass;
  
  /**
   * 非公開変数
   */
  var _privateVariable = 'private variable';
  
  /**
   * 非公開関数
   */
  function _privateMethod() {
    console.log('_privateMethod() {');
    console.group();
      console.log('print '+_privateVariable);
    console.groupEnd();
    console.log('}');
  }
  
  /**
   * コンストラクタ
   * @constructor
   */
  _this = function BaseClass_constructor() {
    console.log('BaseClass_constructor() {');
    console.group();
      this.initialize.apply(this, arguments);
      //BaseClass_initialize.apply(this, arguments);
    console.groupEnd();
    console.log('}');
  }
  inherits(_this, SuperClass);
  
  /**
   * 静的変数
   */
  _this.staticVariable = 'static variable';
  
  /**
   * 定数(大文字、_区切り)
   * 変更できてしまうのが難点
   */
  _this.CONST_VARIABLE = 'CONST_VARIABLE';
  
  /**
   * 静的関数
   */
  _this.staticMethod = function BaseClass_staticMethod() {
    console.log('BaseClass_staticMethod() {');
    console.group();
      console.log('print '+_this.staticVariable);
    console.groupEnd();
    console.log('}');
  }
  
  /**
   * 初期化
   */
  _this.prototype.initialize = function BaseClass_initialize(arg0) {  // 公開
  //function BaseClass_initialize(arg0) {                             // 非公開
    console.log('BaseClass_initialize('+arg0+') {');
    console.group();
      
      console.log('call _super.initialize() {');
      console.group();
        _super.call(this, arg0, 'base arg1');   // 引数を指定して渡す
        //_super.apply(this, arguments);        // 引数をそのまま渡す
      console.groupEnd();
      console.log('}');
      
      /**
       * メンバ変数
       */
      this.publicVariable = 'public variable';
      
      /**
       * プロテクト変数(_接頭詞)
       * 変更できてしまうのが難点
       */
      this._protectedVariable = 'protected variable';
    console.groupEnd();
    console.log('}');
  }
  
  /**
   * メンバ関数
   */
  _this.prototype.publicMethod = function BaseClass_publicMethod() {
    console.log('BaseClass_publicMethod() {');
    console.group();
      console.log('print '+this.publicVariable);
      console.log('call _super.publicMethod() {');
      console.group();
        _super.prototype.publicMethod.call(this);
      console.groupEnd();
      console.log('}');
      console.log('print '+this.superPublicVariable);
    console.groupEnd();
    console.log('}');
  }
  
  _this.prototype.accessor = function BaseClass_accessor() {
    console.log('BaseClass_accessor() {');
    console.group();
      console.log('call _privateMethod() {');
      console.group();
        _privateMethod();
      console.groupEnd();
      console.log('}');
      console.log('call _super.accessor.call(this) {');
      console.group();
        _super.prototype.accessor.call(this);
      console.groupEnd();
      console.log('}');
      console.log('print '+_privateVariable);
      console.log('print '+_this.staticVariable);
      console.log('print '+this.publicVariable);
      console.log('print '+_super.superStaticVariable);
      console.log('print '+this.superPublicVariable);
    console.groupEnd();
    console.log('}');
  }
  
  // BaseClassのコンストラクタ
  return _this;
});

補足

便宜上MITライセンスを指定していますが、上記のクラスをそのまま使用することは決してないと思います。変数、関数へのアクセス関連の考え方は、「このコードは標準的な技術を持つ技術者であれば、別個に思いついた可能性がある」 と考えます。そのため、著作権表示等なく使用していただいて構いません。(Sample.jsとしては、MITライセンスの対象となります)

また、inherits関数は、筆者のコードではありません。記載の著作権表示、ライセンス表示の通りです。

参考

 コメントを書く