1. JavaScript ist eine objektorientierte Sprache Bevor wir erklären, dass JavaScript eine objektorientierte Sprache ist, diskutieren wir die drei grundlegenden Merkmale von objektorientiert: Kapselung, Vererbung, Polymorphismus. In diesem Artikel stellen wir Ihnen JavaScript objektorientiert noch einmal vor.
Kapselung
kombiniert abstrakte Attribute und Methoden, und die Attributwerte sind intern geschützt und können nur durch bestimmte Methoden geändert und gelesen werden
, nehmen wir als Beispiel den Code. Zuerst erstellen wir einen Person
-Konstruktor, der zwei Eigenschaften hat: name
und id
, sowie eine sayHi
-Methode zum Begrüßen:
//定义Person构造函数 function Person(name, id) { this.name = name; this.id = id; } //在Person.prototype中加入方法 Person.prototype.sayHi = function() { console.log('你好, 我是' + this.name); }
Jetzt generieren wir ein Instanzobjekt p1
und rufen die Methode sayHi()
auf
//实例化对象 let p1 = new Person('阿辉', 1234); //调用sayHi方法 p1.sayHi();
Im obigen Code p1
kennt dieses Objekt die Methode sayHi()
nicht. Wie wird sie implementiert? , aber Sie können diese Methode immer noch verwenden, um zu kapseln. Sie können auch private und öffentliche Eigenschaften des Objekts im Konstruktor implementieren, und zwar mit und kann nur über die Methode salary
getSalary()
function Person(name, id) { this.name = name; this.id = id; let salary = 20000; this.getSalary = function (pwd) { pwd === 123456 ? console.log(salary) : console.log('对不起, 你没有权限查看密码'); } }
kann ein Objekt eines bestimmten Typs die Attribute und Methodennamen eines Objekts erhalten Ein anderer Typ. Um
zu erben, verwenden Sie geradeals übergeordneten Klassenkonstruktor. Erstellen wir einen neuen Unterklassenkonstruktor
. Hier verwenden wir die Methode Person
, um die Vererbung zu implementieren Student
call()
function Student(name, id, subject) { //使用call实现父类继承 Person.call(this, name, id); //添加子类的属性 this.subject = subject; } let s1 = new Student('阿辉', 1234, '前端开发');
Die gleiche Operation erzeugt unterschiedliche Ausführungsergebnisse für verschiedene Objekte, was als Polymorphismus bezeichnet wird
Nehmen wir jetzt auch den-Konstruktor als Beispiel: Wir fügen eine
-Methode Person
Person
study
zum
function Person(name, id) { this.name = name; this.id = id; } Person.prototype.study = function() { console.log('我在学习') };
erbt, und fügen außerdem eine Student
-Methode hinzu Person
study
function Student(name, id, subject) { //继承Person父类构造函数 Person.call(this, name, id); //添加子类的属性 this.subject = subject; //添加study方法 this.study = function() { console.log('我在学习' + this.subject); } }
und rufen die s1
-Methode s2
study
let s1 = new Student('阿辉', 1234, '前端开发'); let s2 = new Student('阿傻', 5678, '大数据开发'); s1.study(); //我在学习前端开发 s2.study(); //我在学习大数据开发
auf bzw. Wir alle rufen die s1
-Methode auf, aber die Ergebnisse sind inkonsistent, was tatsächlich einen Polymorphismus erreicht s2
study
kann aus der obigen Analyse, JavaScript, demonstriert werden ist eine objektorientierte Sprache, da sie alle Merkmale der objektorientierten Sprache implementiert. Tatsächlich ist die objektorientierte Sprache für ihre Existenz nicht von einer bestimmten Sprache abhängig -orientiertes Denken zum Konstruieren In seiner Sprache implementiert es Mechanismen wie Klassen, Vererbung, Ableitung, Polymorphismus, Schnittstellen usw. Diese Mechanismen sind jedoch nur ein Mittel zur objektorientierten Umsetzung, aber nicht notwendig. Mit anderen Worten: Eine Sprache kann basierend auf ihren eigenen Merkmalen einen geeigneten Weg zur Implementierung der Objektorientierung wählen. Da die meisten Programmierer zunächst höhere Programmiersprachen wie Java und C++ erlernen, akzeptieren sie vorläufig die eigentliche objektorientierte Methode „Klasse“ und verwenden daher zur Beurteilung üblicherweise die Konzepte in klassenbasierten objektorientierten Sprachen ob die Sprache eine objektorientierte Sprache ist. Auch hier fällt es vielen Menschen mit Erfahrung in anderen Programmiersprachen schwer, JavaScript-Objekte zu lernen.
Tatsächlich implementiert JavaScript die objektorientierte Programmierung durch eine Methode namens
. Lassen Sie uns den Unterschied zwischen klassenbasierter objektorientierter und prototypbasierter objektorientierter diskutieren.
2. Vergleich zwischen klassenbasierter objektorientierter und prototypbasierter objektorientierterIn objektorientierten Sprachen, die auf
Klassenbasieren (wie Java und C++), basieren sie auf Klassen und Instanzen. Darunter definiert die -Klasse alle Attribute für Objekte mit bestimmten Eigenschaften. Eine Klasse ist ein abstraktes Ding und kein spezifisches Individuum unter allen Objekten, die sie beschreibt. Andererseits ist eine Instanz eine Instanziierung einer Klasse und Mitglied dieser.
Prototyp-basiert objektorientiertDiese Unterscheidung gibt es nicht in Sprachen, die auf Prototyp Es gibt also ein sehr interessantes Differenzzusammenfassung 在ES5中创建对象有两种方式, 第一种是使用对象字面量的方式, 第二种是使用构造函数的方式。该两种方法在特定的使用场景分别有其优点和缺点, 下面我们来分别介绍这两种创建对象的方式。 我们通过对象字面量的方式创建两个 上面的代码就是使用对象字面量的方式创建实例对象, 使用对象字面量的方式在创建单一简单对象的时候是非常方便的。但是,它也有其缺点: 在生成多个实例对象时, 我们需要每次重复写 虽然都是学生的对象, 但是看不出 为了解决以上两个问题, JavaScript提供了构造函数创建对象的方式。 构造函数就其实就是一个普通的函数,当对构造函数使用 我特意在构造函数中打印出 采用普通方式调用 当我们采用 为什么要使用 我们给之前的 现在我们来实例化 这样生成的实例对象表面上看没有任何问题, 但是其实是有很大的性能问题!我们来看下面一段代码: 其实对于每一个实例对象 那么我们在MDN中看一下在JavaScript中我们使用的
上面的方法只是 现在我们应该知道应该将 现在我们实例化 从上面的代码我们可以看出, “学生”这个对象可以分为小学生, 中学生和大学生等。我们现在新建一个小学生的构造函数 那么如何让 代码的第一行, 我们将 代码的第二行也许有的读者会不能理解是什么意思。
然而, 当我们使用实例化 而且, 这明显是错误的, 上面就是我们的如何使用 使用 使用 同样对于上面的 需要注意的是, 使用 JavaScript中的继承方式不仅仅只有上面提到的几种方法, 在《JavaScript高级程序设计》中, 还有实例继承,拷贝继承,组合继承,寄生组合继承等众多继承方式。在寄生组合继承中, 就很好的弥补了 基于原型的继承方式,虽然实现了代码复用,但是行文松散且不够流畅,可阅读性差,不利于实现扩展和对源代码进行有效的组织管理。不得不承认,基于类的继承方式在语言实现上更健壮,且在构建可服用代码和组织架构程序方面具有明显的优势。所以,ES6中提供了基于类 我们使用ES6的 上面的代码定义了一个 上面的第一行的代码中, E6中 上面代码代码中, 我们通过了 JavaScript 被认为是世界上最受误解的编程语言,因为它身披 c 语言家族的外衣,表现的却是 LISP 风格的函数式语言特性;没有类,却实也彻底实现了面向对象。要对这门语言有透彻的理解,就必须扒开其 c 语言的外衣,从新回到函数式编程的角度,同时摒弃原有类的面向对象概念去学习领悟它(摘自参考目录1)。现在的前端中不仅普遍的使用了ES6的新语法,而且在JavaScript的基础上还出现了TypeScript、CoffeeScript这样的超集。可以预见的是,目前在前端生态圈一片繁荣的情况下,对JSer的需求也会越来越多,但同时也对前端开发者的JavaScript的水平提出了更加严苛的要求。使用面向对象的思想去开发前端项目也是未来对JSer的基本要求之一! 相关推荐: JavaScript面向对象基础与this指向问题的具体分析
basieren (wie JavaScript): es gibt sie nur Objekte! Ob es sich um einen Konstruktor, eine Instanz oder einen Prototyp handelt, selbst ist ein Objekt. Prototypenbasierte Sprachen verfügen über das Konzept sogenannter Prototypobjekte, aus denen neue Objekte primitive Eigenschaften erhalten können. __proto__
-Attribut in JavaScript (ein nicht standardmäßiges Attribut unter ES6), um auf das Prototypobjekt zuzugreifen. Sie werden feststellen, dass der oben erwähnte Konstruktor, die Instanz und der Prototyp selbst __proto__
Punkte haben zum Prototypobjekt. Am Ende wird die Prototypenkette auf den Object
-Konstruktor verweisen. Der Prototyp des Prototypobjekts von Object
ist jedoch null
. Wenn Sie es nicht glauben, können Sie versuchen, Object.prototype.__proto__ === null
in . true
ist jedoch typeof null === 'object'
. An diesem Punkt sollten Sie meiner Meinung nach verstehen können, warum es in prototypbasierten Sprachen wie JavaScript keinen Unterschied zwischen Klassen und Instanzen gibt, aber true
alles ein Objekt ist! 二. ES5中的面向对象
*这里的ES5并不特指ECMAScript 5, 而是代表ECMAScript 6 之前的ECMAScript!
(一) ES5中对象的创建
1. 使用对象字面量的方式
student
对象,分别是student1
和student2
。var student1 = {
name: '阿辉',
age: 22,
subject: '前端开发'
};
var student2 = {
name: '阿傻',
age: 22,
subject: '大数据开发'
};
name
,age
,subject
属性,写起来特别的麻烦student1
和student2
之间有什么联系。2. 使用构造函数的方式
new
进行实例化时,会将其内部this
的指向绑定实例对象上,下面我们来创建一个Student
构造函数(构造函数约定使用大写开头,和普通函数做区分)。function Student (name, age, subject) {
this.name = name;
this.age = age;
this.subject = subject;
console.log(this);
}
this
的指向。上面我们提到,构造函数其实就是一个普通的函数, 那么我们使用普通函数的调用方式尝试调用Student
。Student('阿辉', 22, '前端开发'); //window{}
Student
时, this
的指向是window
。下面使用new
来实例化该构造函数, 生成一个实例对象student1
。let student1 = new Student('阿辉', 22, '前端开发'); //Student {name: "阿辉", age: 22, subject: "前端开发"}
new
生成实例化对象student1
时, this
不再指向window
, 而是指向的实例对象本身。这些, 都是new
帮我们做的。上面的就是采用构造函数的方式生成实例对象的方式, 并且当我们生成其他实例对象时,由于都是采用Student
这个构造函数实例化而来的, 我们能够清楚的知道各实例对象之间的联系。let student1 = new Student('阿辉', 22, '前端开发');
let student2 = new Student('阿傻', 22, '大数据开发');
let student3 = new Student('阿呆', 22, 'Python');
let student4 = new Student('阿笨', 22, 'Java');
(二) ES5中对象的继承
1.
prototype
的原型继承prototype
是JavaScript这类基于原型继承的核心, 只要弄明白了原型和原型链, 就基本上完全理解了JavaScript中对象的继承。下面我将着重的讲解为什么要使用prototype
和使用prototype
实现继承的方式。prototype
?Student
构造函数新增一个study
方法function Student (name, age, subject) {
this.name = name;
this.age = age;
this.subject = subject;
this.study = function() {
console.log('我在学习' + this.subject);
}
}
Student
构造函数, 生成student1
和`student2
, 并分别调用其study
方法。let student1 = new Student('阿辉', 22, '前端开发');
let student2 = new Student('阿傻', 22, '大数据开发');
student1.study(); //我在学习前端开发
student2.study(); //我在学习大数据开发
console.log(student1.study === student2.study); //false
studentx
,其study
方法的函数体是一模一样的,方法的执行结果只根据其实例对象决定(这就是多态),然而生成的每个实例都需要生成一个study
方法去占用一份内存。这样是非常不经济的做法。新手可能会认为, 上面的代码中也就多生成了一个study
方法, 对于内存的占用可以忽略不计。String
实例对象有多少方法?String
实例对象中的一部分方法(我一个屏幕截取不完!), 这也就是为什么我们的字符串能够使用如此多便利的原生方法的原因。设想一下, 如果这些方法不是挂载在String.prototype
上, 而是像上面Student
一样写在String
构造函数上呢?那么我们项目中的每一个字符串,都会去生成这几十种方法去占用内存,这还没考虑Math
,Array
,Number
,Object
等对象!study
方法挂载到Student.prototype
原型对象上才是正确的写法,所有的studentx
实例都能继承该方法。function Student (name, age, subject) {
this.name = name;
this.age = age;
this.subject = subject;
}
Student.prototype.study = function() {
console.log('我在学习' + this.subject);
}
student1
和student2
let student1 = new Student('阿辉', 22, '前端开发');
let student2 = new Student('阿傻', 22, '大数据开发');
student1.study(); //我在学习前端开发
student2.study(); //我在学习大数据开发
console.log(student1.study === student2.study); //true
student1
和student2
的study
方法执行结果没有发生变化,但是study
本身指向了一个内存地址。这就是为什么我们要使用prototype
进行挂载方法的原因。接下来我们来讲解一下如何使用prototype
来实现继承。如何使用
prototype
实现继承?Pupil
。function Pupil(school) {
this.school = school;
}
Pupil
使用prototype
继承Student
呢? 其实我们只要将Pupil
的prototype
指向Student
的一个实例即可。Pupil.prototype = new Student('小辉', 8, '小学义务教育课程');
Pupil.prototype.constructor = Pupil;
let pupil1 = new Pupil('北大附小');
Pupil
的原型对象(Pupil.prototype
)指向了Student
的实例对象。Pupil.prototype = new Student('小辉', 8, '小学义务教育课程');
Pupil.prototype.constructor = Pupil;
Pupil
作为构造函数有一个protoype
属性指向原型对象Pupil.prototype
,而原型对象Pupil.prototype
也有一个constructor
属性指回它的构造函数Pupil
。如下图所示:Student
去覆盖Pupil.prototype后
, 如果没有第二行代码的情况下, Pupil.prototype.constructor
指向了Student
构造函数, 如下图所示:pupil1.constructor
会默认调用Pupil.prototype.constructor
, 这个时候pupil1.constructor
指向了Student
:Pupil.prototype = new Student('小辉', 8, '小学义务教育课程');
let pupil1 = new Pupil('北大附小');
console.log(pupil1.constructor === Student); //true
pupil1
明明是用Pupil
构造函数实例化出来的, 怎么其constructor
指向了Student
构造函数呢。所以, 我们就需要加入第二行, 修正其错误:Pupil.prototype = new Student('小辉', 8, '小学义务教育课程');
//修正constructor的指向错误
Pupil.prototype.constructor = Pupil;
let pupil1 = new Pupil('北大附小');
console.log(pupil1.constructor === Student); //false
console.log(pupil1.constructor === Pupil); //ture
prototype
实现继承的例子, 需要特别注意的: 如果替换了prototype对象, 必须手动将prototype.constructor
重新指向其构造函数。2. 使用
call
和apply
方法实现继承call
和apply
是我个人比较喜欢的继承方式, 因为只需要一行代码就可以实现继承。但是该方法也有其局限性,call
和apply
不能继承原型上的属性和方法, 下面会有详细说明。call
实现继承Student
构造函数, 我们使用call
实现Pupil
继承Student
的全部属性和方法://父类构造函数
function Student (name, age, subject) {
this.name = name;
this.age = age;
this.subject = subject;
}
//子类构造函数
function Pupil(name, age, subject, school) {
//使用call实现继承
Student.call(this, name, age, subject);
this.school = school;
}
//实例化Pupil
let pupil2 = new Pupil('小辉', 8, '小学义务教育课程', '北大附小');
call
和apply
只能继承本地属性和方法, 而不能继承原型上的属性和方法,如下面的代码所示, 我们给Student
挂载study
方法,Pupil
使用call
继承Student
后, 调用pupil2.study()
会报错://父类构造函数
function Student (name, age, subject) {
this.name = name;
this.age = age;
this.subject = subject;
}
//原型上挂载study方法
Student.prototype.study = function() {
console.log('我在学习' + this.subject);
}
//子类构造函数
function Pupil(name, age, subject, school) {
//使用call实现继承
Student.call(this, name, age, subject);
this.school = school;
}
let pupil2 = new Pupil('小辉', 8, '小学义务教育课程', '北大附小');
//报错
pupil2.study(); //Uncaught TypeError: pupil2.study is not a function
apply
实现继承
使用apply
实现继承的方式和call
类似, 唯一的不同只是参数需要使用数组的方法。下面我们使用apply
来实现上面Pupil
继承Student
的例子。//父类构造函数
function Student (name, age, subject) {
this.name = name;
this.age = age;
this.subject = subject;
}
//子类构造函数
function Pupil(name, age, subject, school) {
//使用applay实现继承
Student.apply(this, [name, age, subject]);
this.school = school;
}
//实例化Pupil
let pupil2 = new Pupil('小辉', 8, '小学义务教育课程', '北大附小');
3. 其他继承方式
call
和apply
无法继承原型属性和方法的缺陷,是最完美的继承方法。这里就不详细的展开论述,感兴趣的可以自行阅读《JavaScript高级程序设计》。三. ES6中的面向对象
class
的语法。但class
本质上是ES6提供的一颗语法糖,正如我们前面提到的,JavaScript是一门基于原型的面向对象语言。(一) ES6中对象的创建
class
来创建Student
//定义类
class Student {
//构造方法
constructor(name, age, subject) {
this.name = name;
this.age = age;
this.subject = subject;
}
//类中的方法
study(){
console.log('我在学习' + this.subject);
}
}
//实例化类
let student3 = new Student('阿辉', 24, '前端开发');
student3.study(); //我在学习前端开发
Student
类, 可以看到里面有一个constructor
方法, 这就是构造方法,而this
关键字则代表实例对象。也就是说,ES5中的构造函数Student
, 对应的是E6中Student
类中的constructor
方法。Student
类除了构造函数方法,还定义了一个study
方法。需要特别注意的是,在ES6中定义类中的方法的时候,前面不需要加上function
关键字,直接把函数定义进去就可以了。另外,方法之间不要用逗号分隔,加了会报错。而且,类中的方法全部是定义在原型上的,我们可以用下面的代码进行验证。console.log(student3.__proto__.study === Student.prototype.study); //true
console.log(student3.hasOwnProperty('study')); // false
student3.__proto__
是指向的原型对象,其中Student.prototype
也是指向的原型的对象,结果为true
就能很好的说明上面的结论: 类中的方法全部是定义在原型上的。第二行代码是验证student3
实例中是否有study
方法,结果为false
, 表明实例中没有study
方法,这也更好的说明了上面的结论。其实,只要理解了ES5中的构造函数对应的是类中的constructor
方法,就能推断出上面的结论。(二) ES6中对象的继承
class
可以通过extends
关键字来实现继承, 这比前面提到的ES5中使用原型链来实现继承, 要清晰和方便很多。下面我们使用ES6的语法来实现Pupil
。//子类
class Pupil extends Student{
constructor(name, age, subject, school) {
//调用父类的constructor
super(name, age, subject);
this.school = school;
}
}
let pupil = new Pupil('小辉', 8, '小学义务教育课程', '北大附小');
pupil.study(); //我在学习小学义务教育课程
extends
实现Pupil
子类继承Student
父类。需要特别注意的是,子类必须在constructor
方法中首先调用super
方法,否则实例化时会报错。这是因为子类没有自己的this
对象, 而是继承父类的this
对象,然后对其加工。如果不调用super
方法,子类就得不到this
对象。四.结束语
Das obige ist der detaillierte Inhalt vonNeues Verständnis von objektorientiertem JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!