Contrairement aux langages de programmation basés sur des classes tels que C et Java, l'héritage en JavaScript est basé sur des prototypes. Dans le même temps, JavaScript étant un langage très flexible, il existe de nombreuses façons d’implémenter l’héritage.
Le premier concept de base concerne les constructeurs et les chaînes de prototypes. Le constructeur de l'objet parent s'appelle Parent, le constructeur de l'objet enfant s'appelle Child et les objets parent et enfant correspondants sont respectivement parent et enfant.
Il y a un attribut caché [[prototype]] (attention pas prototype) dans l'objet. Dans Chrome, il s'agit de __proto__, mais dans certains environnements, il est inaccessible. Lors de l'accès aux propriétés ou aux méthodes d'un objet, toutes les propriétés de l'objet seront recherchées en premier. Si elles ne sont pas trouvées, les propriétés de l'objet prototype seront recherchées étape par étape le long de la chaîne de prototypes selon [[prototype]] jusqu'à ce qu'elles soient trouvées. Sinon, retournez undéfini.
1. Héritage de la chaîne prototype :
La chaîne de prototypes est le moyen par défaut d'implémenter l'héritage en JavaScript. Si vous souhaitez qu'un objet enfant hérite de l'objet parent, le moyen le plus simple est de pointer l'attribut prototype du constructeur de l'objet enfant vers une instance de l'objet parent :
2. Héritage prototypique (chaîne non prototype) :
Afin d'éviter le problème de la création répétée d'instances d'objet prototype dans la méthode précédente, vous pouvez directement pointer le prototype du constructeur d'objet enfant vers le prototype du constructeur d'objet parent de cette manière, toutes les propriétés et méthodes de Parent. .prototype peut également être réutilisé. En même temps, il n'est pas nécessaire de créer à plusieurs reprises des instances d'objet prototype :
.
3. Héritage temporaire du constructeur :
Afin de résoudre le problème ci-dessus, vous pouvez utiliser un constructeur temporaire pour agir comme couche intermédiaire. Toutes les opérations sur le prototype de l'objet enfant sont effectuées sur l'instance du constructeur temporaire et n'affecteront pas le prototype de l'objet parent :
Nous pouvons encapsuler le travail ci-dessus dans une fonction, et appeler cette fonction à l'avenir peut facilement implémenter cette méthode d'héritage :
Cette méthode d'héritage ne modifie fondamentalement pas la relation de la chaîne de prototypes, mais copie directement tous les attributs de l'objet prototype parent vers le prototype de l'objet enfant. Bien entendu, la copie ici ne s'applique qu'aux types de données de base et aux types d'objet. ne prend en charge que le passage par référence.
5. Héritage entre objets :
En plus de la méthode d'héritage entre constructeurs, vous pouvez également hériter directement entre objets sans constructeurs. Autrement dit, copiez directement les attributs de l'objet, y compris la copie superficielle et la copie approfondie.
Copie superficielle :
Acceptez l'objet à hériter, créez en même temps un nouvel objet vide, copiez les propriétés de l'objet à hériter dans le nouvel objet et renvoyez le nouvel objet :
Copie approfondie :
Le problème de la copie superficielle est également évident : elle ne peut pas copier les attributs du type d'objet mais ne peut transmettre que la référence. Pour résoudre ce problème, une copie profonde doit être utilisée. L'objectif de la copie profonde réside dans l'appel récursif de copie. Lorsque les propriétés du type d'objet sont détectées, l'objet ou le tableau correspondant est créé et les valeurs du type de base sont copiées une par une.
6. Héritage prototype :
A l'aide de l'objet parent, créez un nouvel objet prototypé par l'objet parent via le constructeur :
7.原型繼承與屬性拷貝混合:
原型繼承方法中以傳入的父對象為原型建構子對象,同時還可以在父對象提供的屬性之外額外傳入需要拷貝屬性的對象:
for (var i in stuff) {
n[i] = stuff[i]
}
return n
}
這種方式不涉及原型鏈的操作,傳入多個需要拷貝屬性的對象,依序進行屬性的全拷貝:
9.構造器借用:
JavaScript中的call()和apply()方法非常好用,其改變方法執行上下文的功能在繼承的實作中也能發揮作用。所謂建構子借用是指在子物件建構器中藉用父物件的建構子對this進行操作:
function Child() {
Parent.apply(this, arguments)
}
var child = new Child()
console.log(child.name)
而這種方法的缺點在於,在子物件的建置過程中沒有使用過new操作符,因此子物件不會繼承父級原型物件上的任何屬性,在上面的程式碼中,child的name屬性將會是undefined。
要解決這個問題,可以再次手動將子物件建構器原型設為父物件的實例:
要解決這個問題,就要去掉一次父物件建構器的調用,建構器借用不能省略,那麼只能去掉後一次調用,實現繼承原型的另一方法就是迭代複製: