JS継承の実現
6412 ワード
JSは誕生当初から対象に向けた言語ではなかった.
どのようにJSで継承を実現するかについては、まとめて4つの書き方があります.
コンストラクタ継承
このようにして一つの欠点があります.親タイプの公開方法と属性にアクセスできないということです.
サブクラスでは、親タイプのプライベート属性を取得する必要がある場合には、
クラス継承には二つの欠点があります.トラップ・サブオブジェクトを参照して、親の種類の方法や変数を任意に修正し、他のサブオブジェクトのdog 1.types.push('fish')consosolie.log(dog 1.types)///[cat],[dog],[fish]consolip.log(dog 2.types)/[fish] 構成の異なる実例となる属性を初期化できない これは主にクラス継承のためです.
グループ引継ぎ
組み合わせ継承とは、以上の2つの継承方式の利点を組み合わせて、両者の欠点を捨てて、実現される組み合わせのことです.
この問題を解決するために最適化し、このような継承方式が生まれた.
コンビネーション寄生式継承
Object.createを使って、浅いコピーをして、親の原型の方法をコピーしてDocg.prototypeに与えます.このような種類では、父の種類の共有方法があります.また、父のタイプを呼び出すためのコンストラクターが少なくなります.
書き換え
ES 6では文法飴を使ってextendsを実現します.
前に勉強したことがある、あるいは対象言語の基礎に向かっているものがあると、これは分かりやすく、extensのキーワードを継承として使っています.
以上より、JSの継承は全部でコンストラクタ継承、クラス継承、グループ継承、グループ寄生継承、ES 6のextens継承の5つの継承方式に分けられ、そのうちの4つは第3の最適化実現である.
最後に、実現
MDN:new演算子は、ユーザによって定義されたオブジェクトタイプの例または構築関数を有する内蔵オブジェクトの例を作成する.
文法:new constructor[(argments)]
どのようにJSで継承を実現するかについては、まとめて4つの書き方があります.
コンストラクタ継承
function Animal(name) {
this.name = name
this.sayName = function() {
console.log(this.name)
}
}
function Dog(name, hobby) {
//
let ani = new Animal(name)
for(let p in ani) {
if (ani.hasOwnProperty(p)) {
this[p] = ani[p]
}
}
this.hobby = hobby
}
let dog1 = new Dog('xiaohei', 'bone')
let dog2 = new Dog('fofo', 'bone and fish')
console.log(dog1.sayName()) // xiaohei
console.log(dog2.sayName()) // fofo
対象と偽って相続を実現し、実際には構造関数において、親類のすべての属性を取得し、自身のオブジェクトに保存することにより、親類の属性と方法を呼び出すことができる.ここでforinの方式は親の属性を遍歴し、forinは公開された属性と方法を遍歴するので、hasOwnProperty
現在のオブジェクトに書き込む範囲を制御する.そうでないと、すべての属性はすべてプライベート属性に変わります.このようにして一つの欠点があります.親タイプの公開方法と属性にアクセスできないということです.
Animal.prototype.sayHobby = function() {
console.log(this.hobby)
}
dog1.sayHobby() // VM2748:1 Uncaught TypeError: dog1.sayHobby is not a function at :1:6
コードの最適化サブクラスでは、親タイプのプライベート属性を取得する必要がある場合には、
call
およびapply
親タイプの方法を呼び出したときに、現在のコンテキストをサブクラスのオブジェクトに変更すると、サブクラスのオブジェクトは、親タイプのすべてのプライベート属性を取得することができる.function Animal(name) {
this.name = name
this.sayName = function() {
console.log(this.name)
}
}
function Dog(name, hobby) {
//
Animal.call(this, name)
this.hobby = hobby
}
let dog1 = new Dog('xiaohei', 'bone')
let dog2 = new Dog('fofo', 'bone and fish')
console.log(dog1.sayName()) // xiaohei
console.log(dog2.sayName()) // fofo
クラス継承function Animal(name) {
this.name = name || 'animal'
this.types = ['cat', 'dog']
this.sayTypes = function() {
console.log(this.types.join('-'))
}
}
Animal.prototype.sayName = function() {
console.log(this.name)
}
function Dog(name) {
this.name = name
}
Dog.prototype = new Animal('animal')
let dog1 = new Dog('xiaohei')
dog1.sayName() // xiaohei
let dog2 = new Dog('feifei')
dog2.sayName() // feifei
この継承方式は、子類prototype.__proto__
親類prototype
を引用することにより、親類のプライベート方法と共有方法にサブクラスをアクセスさせることができる.詳細はキーワードnew
の実現を見ることができます.クラス継承には二つの欠点があります.
Dog.prototype = new Animal('animal')
によって実現されます.私たちは父の構造関数を一度だけ呼び出します.したがって、サブクラスでは、上記name
属性のみを書くことができます.サブクラスでは、書き換えが必要です.グループ引継ぎ
組み合わせ継承とは、以上の2つの継承方式の利点を組み合わせて、両者の欠点を捨てて、実現される組み合わせのことです.
function Animal(name) {
this.name = name
this.types = ['dog', 'cat']
}
Animal.prototype.sayName = function() {
console.log(this.name)
}
function Dog(name, hobby) {
// , ,
Animal.call(this, name)
this.hobby = hobby
}
// prototype
Dog.prototype = new Animal()
Dog.prototype.constructor = Dog
Dog.prototype.sayHobby = function() {
console.log(this.hobby)
}
// test instance of dog1
let dog1 = new Dog('xiaohei', 'bone')
dog1.sayName() // xiaohei
dog1.sayHobby() // bone
dog1.types.push('ant') // types: ['dog', 'cat', 'ant']
// test instance of dog2
let dog2 = new Dog('feifei', 'fish')
dog2.sayName() // feifei
dog2.sayHobby() // fish
dog2.types // ['dog', 'cat']
組み合わせモードは、構造関数の継承とクラスの継承による問題を解決し、理想的な継承方式として計算されますが、ここにはいくつかの欠点があります.この問題を解決するために最適化し、このような継承方式が生まれた.
コンビネーション寄生式継承
function Animal(name) {
this.name = name
this.types = ['dog', 'cat']
}
Animal.prototype.sayName = function() {
console.log(this.name)
}
function Dog(name, hobby) {
// , ,
Animal.call(this, name)
this.hobby = hobby
}
/** **/
Dog.prototype = Object.create(Animal.prototype)
// Animal.prototype , Dog , Dog
Dog.prototype.constructor = Dog
Dog.prototype.sayHobby = function() {
console.log(this.hobby)
}
// test instance of dog1
let dog1 = new Dog('xiaohei', 'bone')
dog1.sayName() // xiaohei
dog1.sayHobby() // bone
dog1.types.push('ant') // types: ['dog', 'cat', 'ant']
// test instance of dog2
let dog2 = new Dog('feifei', 'fish')
dog2.sayName() // feifei
dog2.sayHobby() // fish
dog2.types // ['dog', 'cat']
MDN解釈:Object.create()方法は新しいオブジェクトを作成し、既存のオブジェクトを使用して新規作成の対象を提供する.proto_Object.createを使って、浅いコピーをして、親の原型の方法をコピーしてDocg.prototypeに与えます.このような種類では、父の種類の共有方法があります.また、父のタイプを呼び出すためのコンストラクターが少なくなります.
書き換え
create
方法:function create(target) {
function F() {}
F.prototype = target
return new F()
}
また、サブクラスのconstructorは、サブクラスのprototypeを変更するので、サブクラスの構造関数を再設定する必要があります.ES 6では文法飴を使ってextendsを実現します.
前に勉強したことがある、あるいは対象言語の基礎に向かっているものがあると、これは分かりやすく、extensのキーワードを継承として使っています.
class Animal {
constructor(name) {
this.name = name
}
sayName() {
console.log(this.name)
}
}
class Dog extends Animal {
constructor(name, hobby) {
super(name)
this.hobby = hobby
}
sayHobby() {
console.log(this.hobby)
}
}
let dog1 = new Dog('xiaohei', 'bone')
dog1.sayName() // xiaohei
dog1.sayHobby() // bone
let dog2 = new Dog('feifei', 'fish')
dog2.sayName() // feifei
dog2.sayHobby() // fish
締め括りをつける以上より、JSの継承は全部でコンストラクタ継承、クラス継承、グループ継承、グループ寄生継承、ES 6のextens継承の5つの継承方式に分けられ、そのうちの4つは第3の最適化実現である.
最後に、実現
new
キーワードの実現MDN:new演算子は、ユーザによって定義されたオブジェクトタイプの例または構築関数を有する内蔵オブジェクトの例を作成する.
文法:new constructor[(argments)]
function new(constructor, arguments) {
let o = {}
if (constructor && typeof constructor === 'function') {
//
o.__proto__ = constructor.prototype
//
constructor.apply(o, arguments)
return o
}
}