インタビュアー: JavaScript はどのように継承を実装しますか?

目次

1. 継承とは何ですか?

2 つの継承方法

1. プロトタイプチェーンの継承

2. コンストラクターの継承

3. 構成の継承

4. プロトタイプの継承

5. 寄生継承

6. 寄生組成の継承

 3. まとめ


1. 継承とは何ですか?

継承はオブジェクト指向ソフトウェア技術の概念です。

クラス B が別のクラス A から「継承」する場合、この B は「A のサブクラス」と呼ばれ、A は「B の親クラス」または「A は B のスーパークラス」と呼ばれます。

継承の利点:

1. 継承により、同じコードを再度記述することなく、サブクラスが親クラスのさまざまなプロパティやメソッドを持つことができます。

2. サブカテゴリは親カテゴリを継承しますが、一部の属性を再定義し、一部のメソッドを書き換えることができます。つまり、親カテゴリの元の属性とメソッドを上書きして、親カテゴリから異なる機能を取得できます。

継承に関して、最初に鮮やかな例を挙げることができます。

car クラスは親クラスであり、トラックと車は car クラスのプロパティとメソッドを継承するサブクラスです。また、親クラスのプロパティとメソッドを書き換えたり、独自のプロパティとメソッドを追加したりすることもできます。自分の。

            // 汽车父类
            class Car {
                constructor(color, type) {
                    this.color = color;
                    this.type = type;
                }
                getColor() {
                    return this.color;
                }
            }
            // 货车子类
            class Truck extends Car {
                constructor(color, speed, container) {
                    super(color, name);
                    this.container = container;
                }
                getColor() {
                    return `货车的颜色为` + super.getColor();
                }
            }
            // Suv子类
            class Suv extends Car {
                constructor(color, speed, quick) {
                    super(color, name);
                    this.quick = quick;
                }
                getColor() {
                    return `Suv的颜色为` + super.getColor();
                }
            }
            let truck1 = new Truck('red', 200, 300);
            console.log(truck1.getColor()); // 货车的颜色为red
            let suv1 = new Suv('green', 200, 300);
            console.log(suv1.getColor()); // Suv的颜色为red

この例から、乗用車、トラック、SUV 間の継承関係を詳細に説明できます。

2 つの継承方法

1. プロトタイプチェーンの継承

     プロトタイプ チェーンの継承は、より一般的な継承方法の 1 つです。関連するコンストラクター、プロトタイプ、インスタンスの間には特定の関係があります。つまり、各コンストラクターにはプロトタイプ オブジェクトがあり、プロトタイプ オブジェクトにはコンストラクターへのポインターが含まれています。関数。インスタンスにはプロトタイプ オブジェクトへのポインタが含まれています。

            function Parent() {
                this.name = 'parent';
                this.friends = ['zs', 'lisi', 'wangwu '];
            }
            function Child() {
                this.type = 'child';
            }
            Child.prototype = new Parent();
            let child1 = new Child();
            let child2 = new Child();
            child1.friends.push('sunqi');
            console.log(child1.friends, child2.friends);
            console.log(child1.__proto__);

以下の図は上記の例のプロトタイプ関係図です. Child.prototypeが親インスタンスオブジェクトparent1を実行していることがわかります. child1オブジェクトのfriendsの値を変更する場合, child1インスタンスオブジェクト自体はこの属性を持っていません,したがって、上方向に検索してparent1インスタンスオブジェクトを見つけますが、child1とchild2は同じparent1インスタンスオブジェクトを指し、メモリ空間は共有されます。

プッシュ値がparent1.friendsに追加されると、パブリック プロトタイプ オブジェクトparent1の値が直接変更されます。

 プリントアウトは次のとおりです

2. コンストラクターの継承

呼び出しを借用して親関数を呼び出すと、親のコンストラクターを呼び出してオブジェクトを構築するだけで、実際のプロトタイプの継承は認識されず、親インスタンスのプロパティとメソッドのみを構築でき、プロトタイプのプロパティとメソッドにアクセスできません。

親クラスの参照プロパティは共有されないため、最初の継承メソッドの欠点が最適化されますが、継承できるのは親クラスのインスタンス プロパティとメソッドのみで、プロトタイプのプロパティやメソッドは継承できません。

        <script>
            function Parent() {
                this.name = 'parent';
            }
            Parent.prototype.getName = function () {
                return this.name;
            };
            function Child() {
                Parent.call(this);
                this.type = 'child';
            }
            let child = new Child();
            console.log(child);
            console.log(child.name);
            console.log(child.getName());
        </script>

3. 構成の継承

構成の継承は、前の 2 つの方法を組み合わせたものです。

コードは以下のように表示されます。

        <script>
            function Parent() {
                this.name = 'parent';
                this.friends = ['zs', 'lisi', 'wangwu '];
            }

            // 为Parent原型对象上添加方法
            Parent.prototype.getName = function () {
                return this.name;
            };

            function Child() {
                Parent.call(this);
                this.type = 'child';
            }

            Child.prototype = new Parent();
            // 手动挂载构造器
            Child.prototype.constructor = Child;
            let child1 = new Child();
            let child2 = new Child();
            child1.friends.push('sunqi');
            console.log(child1.friends, child2.friends); // 不互相影响
            console.log(child1.getName());
            console.log(child2.getName());
        </script>

結合継承は、前の 2 つの継承メソッドの利点を組み合わせたものです。Child をインスタンス化するときに、親クラスのコンストラクターが呼び出されます。child1 と child2 は互いに独立しており、独自の値を持つため、相互に影響しません。メソッドが呼び出されます。 当時、child1 メソッドと child2 メソッドにはそのようなメソッドはなく、上方向に検索して Parent のプロトタイプ オブジェクトの getName メソッドを見つけました。

 印刷結果は次のようになります。

4. プロトタイプの継承

Object.create() メソッドを使用して通常のオブジェクトの継承を実現する

Object.create(proto, [propertiesObject])
このメソッドは、新しいオブジェクトを作成し、そのオブジェクトのプロトタイプ オブジェクトを指定します -------  proto

        <script>
            let parent = {
                name: 'parent',
                friends: ['zs', 'lisi', 'wangwu'],
                getName() {
                    return this.name;
                },
            };
            // 相当于 child.__proto__ == parent
            let child1 = Object.create(parent);
            console.log(child1.__proto__ == parent); // true
            // 为 child1 添加属性name
            child1.name = 'child1';
            child1.friends.push('sunqi');

            let child2 = Object.create(parent);
            child2.friends.push('child2');

            console.log(child1);
            console.log(child2);
            console.log(child1.name == child1.getName()); //true


            console.log(child1.friends); // ['zs', 'lisi', 'wangwu', 'sunqi', 'child2'] 
            console.log(child2.friends); // ['zs', 'lisi', 'wangwu', 'sunqi', 'child2']
        </script>

出力された印刷結果は次のとおりです。

child1 と child2 はどちらも独自の friends 属性を持たないため、同じメモリを指す親オブジェクトの friends メソッドを検索して見つける必要があります。これは、このメソッドが浅いコピーと複数のインスタンスの参照型属性を実装しいるObject.createためです。同じメモリを指しているので改ざんの可能性あり

5.寄生継承

寄生継承は上記の継承に基づいて最適化されており、この浅いコピーの機能を使用していくつかのメソッドが強化および追加されます。

        <script>
            let parent = {
                name: 'parent',
                friends: ['zs', 'lisi', 'wangwu'],
                getName() {
                    return this.name;
                },
            };

            // 定义继承的方法,
            function clone(proto) {
                let clone = Object.create(proto);
                // 添加自己的方法
                clone.getFriends = function () {
                    return this.friends;
                };
                return clone;
            }

            let child = clone(parent);
            console.log(child.getName());  // parent
            console.log(child.getFriends()); // ['zs', 'lisi', 'wangwu']
        </script>

6. 寄生組成の継承

組み合わせと第5の寄生方式と第3の組み合わせ方式

 <script>
            function clone(parent, child) {
                // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
                child.prototype = Object.create(parent.prototype);
                child.prototype.constructor = child;
            }

            function Parent() {
                this.name = 'parent';
                this.play = [1, 2, 3];
            }
            Parent.prototype.getName = function () {
                return this.name;
            };
            function Child() {
                Parent.call(this);
                this.friends = 'child';
            }

            clone(Parent, Child);

            Child.prototype.getFriends = function () {
                return this.friends;
            };

            let child = new Child();
            console.log(child); //{friends:"child",name:"parent",play:[1,2,3],__proto__:Parent}
            console.log(child.getName()); // parent
            console.log(child.getFriends()); // child
        </script>

プロトタイプチェーン継承の模式図

 

この方法により、Child でインスタンス化されたオブジェクトの Child は独自のプロパティとメソッドをインスタンス化でき、プロトタイプ オブジェクトのプロパティとメソッドも継承できることがわかりました。

 3. まとめ

絵で要約できる

 開発者は、寄生構成継承が最も理想的な継承方法であると信じています。

コメント欄で議論し、一緒に学びましょう。

おすすめ

転載: blog.csdn.net/qq_63299825/article/details/131063255