JavaScript>引き継ぎとコピー


JavaScript継承とコピー
Date:7 th of Aug,2015
Author:HaoyCn
本論文では、JavaScriptでの継承関係の実現と、オブジェクトのコピー方法について議論します.以下、それぞれ4つの継承方法を検討します.相続方法を話してから、対象のコピーについて話します.
先に説明する必要があるのは、後文で継承したいコンストラクタの関数は:
var Animal = function(name){
    this.name = name;
};
Animal.prototype.jump = function(){
    console.log('jumped');
};
プロトタイプ相続法
本方法の特性は、再利用可能な部分をプロトタイプチェーンに移行することができ、再利用できない場合は対象の自身の属性として設定されます.
var Human = function(name){
    this.name = name;
};
//       
// Human.prototype.constructor = Animal;
//            
Human.prototype = new Animal;
//      
//   `Human`                 `Animal`   `Human`
Human.prototype.constructor = Human;

var man = new Human('HaoyCn');
man.jump();
次に、オブジェクトmanは、Animal.prototype.jump方法を使用することができ、検索プロセスは:
  • man自体にはjump方法がない
  • ルックアップman.constructor.prototype、すなわちHuman.prototypeは、Human.prototype自体もjump方法がなく、Animalによって構成されたオブジェクトであるため、
  • Animal.prototypeを検索し、jumpの方法が見つかった.実行する
  • .
    原型のみから法を継承する
    「プロトタイプチェーン継承法」に比べ、本方法の利点は、運転時の効率が向上し、新規オブジェクトが作成されていないことです.
    var Human = function(name){
        this.name = name;
    };
    Human.prototype = Animal.prototype;
    var man = new Human('HaoyCn');
    man.jump();
    
    この時の検索過程は以下の通りです.
  • man自体にはjump方法がない
  • ルックアップman.constructor.prototype、すなわちHuman.prototypeHuman.prototypeAnimal.prototypeの参照であり、jumpの方法が発見され、実行される
  • 一歩減った.しかしながら、価格は、Human.prototypeの修正は、前者が後者への参照であるため、Animal.prototypeに影響を与える.
    致命的な欠点の一つは、サブ構造のオブジェクトを修正できないconstructorです.
    テストします
    man.constructor === Animal;//true
    
    newのプロセスを振り返ってみましょう.
    var newProcess = function(){
        var ret;
        //        
        var obj = {};
        //     
        var Constructor = Array.prototype.shift.call(arguments);
        //     
        obj.__proto__ = Constructor.prototype;
        //               
        ret = Constructor.apply(obj,arguments);
        //         
        return 'object' === typeof ret ? ret : obj;
    };
    
    これにより、「原型継承法のみから」manはどのように作成されたかを振り返ってみます.
    // var man = newProcess(Human,'HaoyCn');
    //     
    var ret;
    var man = {};
    // var Constructor = Array.prototype.shift.call(arguments);
    //   
    //var Constructor = Human;
    man.__proto__ = Human.prototype;
    // ret = Human.apply(obj,arguments);
    // `Human`       
    man.name = 'HaoyCn';
    // `Human`        undefined,  ret = undefined;
    //     `newProcess`  `man`
    
    したがって、理解は難しくないです.
    man.constructor === 
        man.__proto__.constructor === 
        Human.prototype.constructor ===
        Animal.prototype.constructor ===
    Animal
    
    仮コンストラクタ継承法
    「原型継承法のみ」の問題が発覚しました.Animal.prototypeHuman.prototypeに対する修正によって変更されます.変更されるとAnimalによって構成されたオブジェクトも変更されます.例を挙げます.
    var monkey = new Animal('monkey');
    var woman = new Human('woman');
    monkey.jump();// jumped
    woman.jump();// jumped
    //         `Animal.prototype`
    Human.prototype.jump = function(){
        console.log('I refuse');
    };
    //              
    monkey.jump();// I refuse
    woman.jump();// I refuse
    
    じゃ、私たちはどうやってこの問題を回避しますか?
    「一時的コンストラクタ継承法」は、次のような仲介関数を使用します.
    var F = function(){};
    F.prototype = Animal.prototype;
    var Human = function(name){
        this.name = name;
    };
    Human.prototype = new F;
    Human.prototype.constructor = Human;
    Human.prototype.sing = function(){
        console.log('Mayday');
    };
    var man = new Human('HaoyCn');
    man.jump();
    man.sing();
    
    Human.prototypeに対する任意の変更は、仲介コンストラクタによって作成されたオブジェクトの属性に対する修正となる.jump検索プロセスは、
  • man自体にはjump方法がない
  • ルックアップman.constructor.prototype、すなわちHuman.prototypeは、Human.prototype自体もjump方法がなく、Fによって構成されたオブジェクトであるため、
  • ルックアップF.prototype、すなわちAnimal.prototypeは、jumpの方法が発見され、実行される
  • .
    この方法は最初の「原型継承法のみ」と比べて、どのような進歩がありますか?
    まず「原型継承法のみ」の操作を見てください.
    Human.prototype = new Animal;
    //     :
    // Human.prototype.name = undefined;//    `Animal`      
    
    つまり、Human.prototypeは不必要な属性が多くなり、仲介器はこの不必要な属性を回避する.
    コンストラクタ借用法
    以上の継承法に共通する欠点の一つは、Humanビルダー構造のオブジェクトはAnimal.prototypeを共有することができるが、name属性については、Humanビルダーは自分で構造name属性を書き直すしかないということです.なぜ初期化属性の方法を共用しないのですか?
    コンストラクター借用法が生まれました.name属性の作成をAnimalに渡し、Human属性を追加します.私たちは「臨時コンストラクター法」に基づいてさらに改善しています.
    var F = function(){};
    F.prototype = Animal.prototype;
    var Human = function(){
        Animal.apply(this,arguments);
        this.country = arguments[1];
    }
    Human.prototype = new F;
    Human.prototype.constructor = Human;
    var man = new Human('HaoyCn','China');
    console.log(man.country);// China
    
    これで、私たちはリラックスして怠けを完成しました.これはPHPにおける構造関数のカバー方法を思い付きました.
    // PHP
    class Human{
        public $name;
        public $country;
        function __construct($name,$country){
            parent::__construct($name);
            $this->country = $country;
        }
    }
    
    相続に関する話はこれで終わります.コピーについて話します.
    原型属性コピー法
    プロトタイプ機構を利用した.高級ブラウザでは、country方法でオブジェクトのコピーを完了します.
    Object.create = Object.create || function(obj){
        var F = function(){};
        F.prototype = obj;
        return new F;
    }
    
    これは薄いコピーです.私たちがコピーされたオブジェクトを修正すれば、新しいオブジェクトにも影響します.以下の例を示します
    var man = {
        name: 'HaoyCn',
        jump: function(){
            console.log('jumped');
        }
    };
    var monkey = Object.create(man);
    monkey.jump();// jumped
    man.jump = function(){
        console.log('I refuse');
    };
    monkey.jump();// I refuse
    
    浅いコピーと深いコピー
    問題を目の前に置いて、どうやって深くコピーしますか?
    私たちのコピー対象は「原型属性コピー法」のほかに、従来の遍及でも可能です.浅いコピーのように巡回:
    var man = {
        name: 'HaoyCn',
        jump: function(){
            console.log('jumped');
        }
    };
    var monkey = {};
    for(var i in man){
        monkey[i] = man[i]; 
    }
    monkey.jump();// jumped
    
    深くコピーするということは、属性がまだ対象であれば、コピーするということです.
    function deepCopy(origin,copy){
        copy = copy || {};
        for(var i in origin){
            if('object' === typeof origin[i]){
                //              ,    
                copy[i] = ('Array' === origin[i].constructor) ? [] : {};
                deepCopy(origin[i],copy[i]);
            }else{
                copy[i] = origin[i];
            }
        }
    }
    
    以上は深いコピーの一つの要約原理コードです.より複雑な検査プロセスは、Object.createを参照することができる.しかし、このようなコピー(jQuery.extendの深度コピーを含む)は、純粋なオブジェクトに対する深いコピーしかできません.関数、RegExp、Dateなどは、深くコピーできません.
    以上です.純粋でない対象に対しての深いコピーの方法については、例えばjQuery.extendを呼び出してから対象を構築する方式ですが、十分ではありません.この点で心得があれば、教えてください.