Programmation orientée objet JavaScript
Introduction orientée objet
Qu'est-ce qu'un objet?
Tout est objet
Quel est l'objet, nous pouvons le comprendre à deux niveaux.
(1) Un objet est une abstraction d'une seule chose.
Un livre, une voiture ou une personne peuvent être un objet et une base de données, une page Web ou une connexion à un serveur distant peuvent également être un objet. Lorsque la chose réelle est abstraite dans un objet, la relation entre la chose réelle devient la relation entre les objets, de sorte que vous pouvez simuler la situation réelle et programmer l'objet.
(2) Un objet est un conteneur qui encapsule des propriétés et des méthodes.
Les attributs sont l'état de l'objet et les méthodes sont le comportement de l'objet (pour accomplir une certaine tâche). Par exemple, nous pouvons faire abstraction des animaux en tant qu'objets animaux, utiliser des «attributs» pour enregistrer de quel type d'animal il s'agit, et utiliser des «méthodes» pour représenter certains comportements des animaux (courir, chasser, se reposer, etc.).
Dans le développement réel, un objet est un concept abstrait, qui peut être simplement compris comme un ensemble de données ou un ensemble de fonctions.
ECMAScript-262 définit un objet comme: une collection d'attributs non ordonnés, dont les attributs peuvent contenir des valeurs de base, des objets ou des fonctions. À proprement parler, cela revient à dire qu'un objet est un ensemble de valeurs sans ordre particulier. Chaque propriété ou méthode d'un objet a un nom et chaque nom est mappé à une valeur.
Conseil: chaque objet est créé en fonction d'un type de référence. Ces types peuvent être les types natifs intégrés au système ou les types définis par le développeur.
Qu'est-ce qui est orienté objet
L'orientation objet n'est pas une nouveauté, ce n'est qu'un code procédural hautement encapsulé, le but est d'améliorer l'efficacité du développement et la maintenabilité du code.
Programmation orientée objet - La programmation orientée objet, en abrégé POO, est une idée de développement de programmation. Il résume diverses relations complexes du monde réel en objets, puis complète la simulation du monde réel par la division du travail et la coopération entre les objets.
Dans l'idée du développement de programme orienté objet, chaque objet est un centre fonctionnel, avec une division claire du travail, et peut accomplir des tâches telles que la réception d'informations, le traitement des données et l'envoi d'informations. Par conséquent, la programmation orientée objet présente les caractéristiques de flexibilité, de réutilisation du code et de modularité élevée. Elle est facile à maintenir et à développer. Par rapport à la programmation procédurale traditionnelle constituée d'une série de fonctions ou d'instructions, elle est plus adaptée à la coopération multi-personnes Projets logiciels à grande échelle.
Orienté objet et orienté processus:
Faire face au processus, c'est le faire soi-même, peu importe sa taille, tout est complet, suivre chaque étape et être méthodique
Orienté objet consiste à trouver un objet et à commander le résultat
Orienté objet transforme les exécuteurs en commandants
L'encapsulation orientée objet ne remplace pas l'encapsulation orientée processus, mais orientée processus
Fonctionnalités orientées objet:
1. Encapsulation
2. Héritage
3. [Polymorphisme] Résumé
L'incarnation de base de l'orientation objet dans le programme
En JavaScript, tous les types de données peuvent être considérés comme des objets, et bien sûr les objets peuvent également être personnalisés. Le type de données objet personnalisé est le concept de classe (classe) en orienté objet.
Nous utilisons un exemple pour illustrer la différence dans le déroulement du programme entre orienté processus et orienté objet.
Supposons que nous souhaitons traiter la feuille de score d'un élève. Afin de représenter le score d'un élève, un programme orienté processus peut être représenté par un objet:
var std1 = { name: 'Michael', score: 98 }
var std2 = { name: 'Bob', score: 81 }
而处理学生成绩可以通过函数实现,比如打印学生的成绩:
function printScore (student) {
console.log('姓名:' + student.name + ' ' + '成绩:' + student.score)
}
Si nous adoptons des idées de programmation orientée objet, notre premier choix n'est pas de penser au processus d'exécution du programme, mais le type de données Student doit être traité comme un objet, qui a deux propriétés (Property) name et score. Si vous souhaitez imprimer le score d'un élève, vous devez d'abord créer un objet correspondant à l'élève, puis envoyer un message printScore à l'objet et laisser l'objet imprimer ses propres données.
抽象数据行为模板(Class):
function Student(name, score) {
this.name = name;
this.score = score;
this.printScore = function() {
console.log('姓名:' + this.name + ' ' + '成绩:' + this.score);
}
}
根据模板创建具体实例对象(Instance):
var std1 = new Student('Michael', 98)
var std2 = new Student('Bob', 81)
实例对象具有自己的具体行为(给对象发消息):
std1.printScore() // => 姓名:Michael 成绩:98
std2.printScore() // => 姓名:Bob 成绩 81
Les idées de conception orientées objet viennent de la nature, car dans la nature, les concepts de classe et d'instance sont naturels. La classe est un concept abstrait. Par exemple, notre définition de Classe —— L'élève fait référence au concept d'élèves, tandis que les instances sont des élèves spécifiques. Par exemple, Michael et Bob sont deux élèves spécifiques.
Par conséquent, l'idée de conception orientée objet est:
Classe de sortie abstraite (constructeur)
Créer une instance en fonction de la classe (constructeur)
Instance de commande pour obtenir le résultat
Le degré d'abstraction orientée objet est supérieur à celui des fonctions, car une classe contient à la fois des données et des méthodes pour manipuler les données.
创建对象
简单方式
我们可以直接通过 new Object() 创建:
var person = new Object()
person.name = 'Jack'
person.age = 18
person.sayName = function () {
console.log(this.name)
}
每次创建通过 new Object() 比较麻烦,所以可以通过它的简写形式对象字面量来创建:
var person = {
name: 'Jack',
age: 18,
sayName: function () {
console.log(this.name)
}
}
对于上面的写法固然没有问题,但是假如我们要生成两个 person 实例对象呢?
var person1 = {
name: 'Jack',
age: 18,
sayName: function () {
console.log(this.name)
}
}
var person2 = {
name: 'Mike',
age: 16,
sayName: function () {
console.log(this.name)
}
}
通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。
简单方式的改进:工厂函数
我们可以写一个函数,解决代码重复问题:
function createPerson (name, age) {
return {
name: name,
age: age,
sayName: function () {
console.log(this.name)
}
}
}
然后生成实例对象:
var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)
这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题, 但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
Constructeur
Guide de contenu:
Syntaxe du constructeur
Constructeur d'analyse
La relation entre le constructeur et l'objet d'instance
L'attribut constructeur de l'instance
opérateur instanceof
La différence entre un appel de fonction ordinaire et un appel de constructeur
La valeur de retour du constructeur
Le problème avec le constructeur
更优雅的工厂函数:构造函数
一种更优雅的工厂函数就是下面这样,构造函数:
function Person (name, age) {
this.name = name
this.age = age
this.sayName = function () {
console.log(this.name)
}
}
var p1 = new Person('Jack', 18)
p1.sayName() // => Jack
var p2 = new Person('Mike', 23)
p2.sayName() // => Mike
解析构造函数代码的执行
在上面的示例中,Person() 函数取代了 createPerson() 函数,但是实现效果是一样的。 这是为什么呢?
我们注意到,Person() 中的代码与 createPerson() 有以下几点不同之处:
Objets créés non affichés
Attribuer des attributs et des méthodes directement à cet objet
Aucune déclaration de retour
Le nom de la fonction utilise une personne en majuscule
Pour créer une instance Person, vous devez utiliser l'opérateur new. L'appel du constructeur de cette manière passera par les 4 étapes suivantes:
Créer un nouvel objet
Attribuez la portée du constructeur au nouvel objet (cela pointe donc vers ce nouvel objet)
Exécuter le code dans le constructeur
Renvoyer un nouvel objet
Voici le pseudo-code spécifique:
function Person (name, age) {
// 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象
// var instance = {}
// 然后让内部的 this 指向 instance 对象
// this = instance
// 接下来所有针对 this 的操作实际上操作的就是 instance
this.name = name
this.age = age
this.sayName = function () {
console.log(this.name)
}
// 在函数的结尾处会将 this 返回,也就是 instance
// return this
}
构造函数和实例对象的关系
使用构造函数的好处不仅仅在于代码的简洁性,更重要的是我们可以识别对象的具体类型了。 在每一个实例对象中同时有一个 constructor 属性,该属性指向创建该实例的构造函数:
console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true
对象的 constructor 属性最初是用来标识对象类型的, 但是,如果要检测对象的类型,还是使用 instanceof 操作符更可靠一些:
console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true
Pour résumer:
Le constructeur est un modèle abstrait abstrait de choses concrètes
L'objet d'instance est un objet d'instance concret obtenu selon le modèle de constructeur abstrait
Chaque objet d'instance a un attribut constructeur, qui pointe vers le constructeur qui a créé l'instance
Remarque: l'argument selon lequel le constructeur est une propriété d'une instance n'est pas rigoureux, le prototype en parlera plus tard.
La relation entre l'instance et le constructeur peut être jugée via l'attribut constructeur de l'instance
Remarque: cette méthode n'est pas rigoureuse, il est recommandé d'utiliser l'opérateur instanceof, apprendre le prototype plus tard vous expliquera pourquoi
Le problème avec le constructeur
使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题:
function Person (name, age) {
this.name = name
this.age = age
this.type = 'human'
this.sayHello = function () {
console.log('hello ' + this.name)
}
}
var p1 = new Person('Tom', 18)
var p2 = new Person('Jack', 16)
在该示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。 那就是对于每一个实例对象,type 和 sayHello 都是一模一样的内容, 每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。
console.log(p1.sayHello === p2.sayHello) // => false
对于这种问题我们可以把需要共享的函数定义到构造函数外部:
function sayHello = function () {
console.log('hello ' + this.name)
}
function Person (name, age) {
this.name = name
this.age = age
this.type = 'human'
this.sayHello = sayHello
}
var p1 = new Person('Top', 18)
var p2 = new Person('Jack', 16)
console.log(p1.sayHello === p2.sayHello) // => true
这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局命名空间冲突的问题。
你肯定想到了可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题:
var fns = {
sayHello: function () {
console.log('hello ' + this.name)
},
sayAge: function () {
console.log(this.age)
}
}
function Person (name, age) {
this.name = name
this.age = age
this.type = 'human'
this.sayHello = fns.sayHello
this.sayAge = fns.sayAge
}
var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)
console.log(p1.sayHello === p2.sayHello) // => true
console.log(p1.sayAge === p2.sayAge) // => true
至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。 但是代码看起来还是那么的格格不入,那有没有更好的方式呢?
Syntaxe du constructeur de résumé
Constructeur d'analyse
La relation entre le constructeur et l'objet d'instance
L'attribut constructeur de l'instance
opérateur instanceof
Le problème avec le constructeur
Guide du contenu du prototype :
Utilisez un objet prototype prototype pour résoudre le problème du constructeur
Analyser la relation entre le constructeur, l'objet prototype et l'objet d'instance
Principe de recherche des membres de la propriété: chaîne de prototypes
L'objet d'instance lit et écrit les membres dans l'objet prototype
Forme abrégée de l'objet prototype
Prototype d'objet natif
Objet
Déployer
Chaîne de caractères
…
Le problème des objets prototypes
Suggestions sur l'utilisation des fonctions construites et des objets prototypes
Une meilleure solution: prototype
JavaScript stipule que chaque constructeur a une propriété prototype qui pointe vers un autre objet. Toutes les propriétés et méthodes de cet objet appartiendront au constructeur.
Cela signifie également que nous pouvons définir directement les propriétés et les méthodes que toutes les instances d'objet doivent partager sur l'objet prototype.
la fonction Personne (nom, âge) { this.name name = this.age = Age } la console.log (Person.prototype) Person.prototype.type = 'Humain' Person.prototype.sayName = function () { Console. log (this.name) } var new new P1 = the Person (...) var new new P2 = the Person (...) to true the console.log (p1.sayName === p2.sayName) // => quand toutes les instances L'attribut type et la méthode sayName () sont en fait la même adresse mémoire, pointant vers l'objet prototype, donc l'efficacité de fonctionnement est améliorée.
La relation entre constructeur, instance et prototype
Toute fonction a une propriété prototype, qui est un objet.
F. function () {}
the console.log (F.prototype) // => Object F.prototype.sayHi = function () { the console.log ('Hi!') } Le constructeur d'objet prototype a un constructeur par défaut Propriété, pointant vers la fonction où se trouve l'objet prototype.
console.log (F.prototype.constructor === F) // => true
L'objet instance obtenu via la fonction constructeur contiendra un proto pointeur vers l'objet prototype de la fonction constructeur .
nouvelle nouvelle instance F. = var ()
la console.log (instance. proto === F.prototype) // => à true est des propriétés non standard.
__proto__
Des exemples d'objets peuvent accéder directement aux membres de l'objet prototype.
instance.sayHi () // => salut!
Résumé:
Toute fonction a une propriété prototype, qui est un objet
L'objet prototype du constructeur a un attribut constructeur par défaut, qui pointe vers la fonction où se trouve l'objet prototype
L'objet d'instance obtenu via le constructeur contiendra un pointeur vers l'objet prototype du constructeur proto
Toutes les instances héritent directement ou indirectement des membres de l'objet prototype
Le principe de recherche des membres d'attribut: chaîne de prototypes Après avoir
compris la relation entre l'objet constructeur-instance-prototype, expliquons pourquoi l'objet instance peut accéder aux membres de l'objet prototype.
Chaque fois que le code lit un attribut d'un objet, il effectuera une recherche, la cible est l'attribut avec le nom donné
La recherche commence à partir de l'instance d'objet elle-même
Si un attribut avec le nom donné est trouvé dans l'instance, la valeur de l'attribut est renvoyée
S'il n'est pas trouvé, continuez à rechercher l'objet prototype pointé par le pointeur et recherchez l'attribut avec le nom donné dans l'objet prototype
Si cette propriété est trouvée dans l'objet prototype, la valeur de la propriété est renvoyée
En d'autres termes, lorsque nous appelons person1.sayName (), deux recherches seront effectuées successivement:
Tout d'abord, l'analyseur demandera: "L'instance personne1 a-t-elle un attribut sayName?" Réponse: "Non.
Ensuite, il a continué à chercher, puis a demandé: "Le prototype de person1 a-t-il un attribut sayName?" "Réponse:" Oui.
"Il lit donc la fonction stockée dans l'objet prototype.
Lorsque nous appelons person2.sayName (), le même processus de recherche sera répété et les mêmes résultats seront obtenus.
Et c'est le principe de base selon lequel plusieurs instances d'objet partagent les propriétés et les méthodes enregistrées par le prototype.
Pour résumer:
Trouvez d'abord en vous-même, et revenez quand vous le trouvez
Si vous ne pouvez pas le trouver par vous-même, vous rechercherez le long de la chaîne de prototypes et reviendrez lorsque vous le trouverez.
S'il n'a été trouvé qu'à la fin de la chaîne de prototypes, il renvoie undefined
L'objet d'instance lit et écrit les membres de l'objet prototype
Lire:
Trouvez d'abord en vous-même, et revenez quand vous le trouvez
Si vous ne pouvez pas le trouver par vous-même, vous rechercherez le long de la chaîne de prototypes et reviendrez lorsque vous le trouverez.
S'il n'a été trouvé qu'à la fin de la chaîne de prototypes, il renvoie undefined
Écriture de membre de type valeur (objet d'instance. Type de valeur membre = xx):
Lorsque l'instance s'attend à réécrire un membre de données ordinaire dans l'objet prototype, il ajoutera en fait le membre à lui-même
En d'autres termes, le comportement bloquera en fait l'accès aux membres de l'objet prototype
Écriture de membre de type de référence (objet d'instance. Type de référence membre = xx):
Comme ci-dessus
Modification de type complexe (objet d'instance. Membre.xx = xx):
Il trouvera également le membre sur son propre corps en premier, et le modifiera directement s'il le trouve sur son propre corps.
Si vous ne pouvez pas le trouver par vous-même, continuez à rechercher le long de la chaîne de prototypes, si vous le trouvez, modifiez-le
Si le membre n'est trouvé qu'à la fin de la chaîne de prototypes, une erreur est signalée (instance object.undefined.xx = xx)
Syntaxe de prototype plus simple
Nous avons remarqué que dans l'exemple précédent, vous devez taper Person.prototype à chaque fois que vous ajoutez une propriété et une méthode. Afin de réduire les entrées inutiles, il est plus courant de réécrire l'ensemble de l'objet prototype avec un littéral d'objet contenant toutes les propriétés et méthodes:
la fonction Person (nom, âge) { this.name name = this.age = Age } Person.prototype = { type: 'Human', the sayHello: function () { the console.log ('I'm' + this .name + ', je suis cette année' + this.age + 'year old') } } Dans cet exemple, nous réinitialisons Person.prototype à un nouvel objet. L'avantage de ceci est que l'ajout de membres à Person.prototype est simple, mais cela pose également un problème, c'est-à-dire que l'objet prototype perd le membre constructeur.
Par conséquent, afin de garder le pointeur du constructeur correct, la méthode d'écriture recommandée est:
la fonction Person (name, Age) { this.name name = this.age = Age } Person.prototype = { constructor: the Person, // => Le constructeur manuel pointera vers le type de constructeur correct : 'Human', the sayHello : function () { console.log ('Je m'appelle' + this.name + ', je suis cette année' + this.age + 'year old') } } Prototype d'objet natif Toutes les fonctions ont des objets de propriété prototype.
Object.prototype
Fonction.prototype
Array.prototype
String.prototype
Numéro.prototype
Date.prototype