JavaScript ist eine leistungsstarke objektorientierte Programmiersprache. Im Gegensatz zu herkömmlichen Programmiersprachen verwendet es jedoch ein prototypbasiertes OOP-Modell, sodass seine Syntax für die meisten unverständlich ist Entwickler. Darüber hinaus behandelt JavaScript Funktionen auch als primäre Objekte, was bei Entwicklern, die mit der Sprache nicht vertraut sind, zu größerer Verwirrung führen kann. Aus diesem Grund haben wir uns entschieden, es als kurze Einführung an den Anfang zu stellen, und es kann auch als Referenz für die objektorientierte Programmierung in JavaScript verwendet werden.
Dieses Dokument bietet keine Vorschau auf die Regeln der objektorientierten Programmierung, sondern einen Überblick über deren Schnittstellen.
Mit dem Aufkommen von immer mehr Bibliotheken, Frameworks und Webabhängigkeiten von Drittanbietern sind Namespaces bei der Entwicklung von JavaScript unerlässlich. Wir müssen versuchen, Konflikte zwischen globalen Namespace-Objekten und zu vermeiden Variablen.
Leider bietet JavaScript keine Unterstützung für die Namespace-Kompilierung, aber wir können Objekte verwenden, um das gleiche Ergebnis zu erzielen. In JavaScript gibt es viele Muster zum Implementieren von Namespace-Schnittstellen, wir behandeln jedoch verschachtelte Namespaces, das in diesem Bereich am häufigsten verwendete Muster.
Das verschachtelte Namespace-Muster verwendet Objektliterale, um Funktionalität mit einem bestimmten Namen für eine bestimmte Anwendung zu bündeln.
Wir erstellen zunächst ein globales Objekt und weisen es einer Variablen namens MyApp zu.
// global namespace var MyApp = MyApp || {};
Mit der obigen Syntax wird überprüft, ob MyApp definiert wurde. Wenn es bereits definiert ist, weisen wir es einfach uns selbst zu, erstellen aber stattdessen einen leeren Container für unsere Funktionen und Variablen.
Wir können dieselbe Technik auch verwenden, um Sub-Namespaces zu erstellen. Zum Beispiel:
// sub namespaces MyApp.users = MyApp.user || {};
Sobald wir unseren Container starten, können wir unsere Funktionen und Variablen innerhalb (des Containers) definieren und sie im globalen Namensraum aufrufen, ohne Konflikte mit bestehenden Definitionen zu riskieren.
// declarations MyApp.users = { existingUsers: '', // variable in namespace renderUsersHTML: function() { // function in namespace // render html list of users } }; // syntax for using functions within our namespace from the global scope MyApp.users.renderUsersHTML();
Eine interne Übersicht über Benennungsmuster in JavaScript wurde von Addy Osmani von Goggle im Artikel Essential JavaScript Namespace Patterns vorgestellt. Wenn Sie verschiedene Modi erkunden möchten, ist dies ein guter Ausgangspunkt.
Wenn Sie jemals JavaScript-Code geschrieben haben, haben Sie bereits Objekte verwendet. JavaScript verfügt über drei Arten von Objekten:
Native Objekte sind Teil der Sprachspezifikation und verfügbar, unabhängig davon, in welcher Ausführungsumgebung sie ausgeführt werden. Zu den nativen Objekten gehören: Array, Date, Math, parseInt usw. Weitere Informationen zu allen nativen Objekten finden Sie in der integrierten JavaScript-Objektreferenz
var cars = Array(); // Array is a native object
Im Gegensatz zu nativen Objekten werden Hostobjekte von der Umgebung erstellt, in der JavaScript-Code ausgeführt wird. Unterschiedliche Umgebungen erstellen unterschiedliche Hostobjekte. Diese Hostobjekte ermöglichen uns in den meisten Fällen die Interaktion mit ihnen. Wenn wir Code schreiben, der in einem Browser (einer der laufenden Umgebungen) ausgeführt wird, gibt es Hostobjekte wie Fenster, Dokument, Speicherort und Verlauf.
document.body.innerHTML = 'Hello World!'; // document is a host object // the document object will not be available in a // stand-alone environments such as Node.js
Benutzerobjekt (oder implantiertes Objekt) ist ein Objekt, das in unserem Code definiert und zur Laufzeit erstellt wird. Es gibt zwei Möglichkeiten, eigene Objekte in JavaScript zu erstellen, die im Folgenden beschrieben werden.
In der vorherigen Demonstration der Erstellung eines Namespace sind wir bereits mit Objektliteralen in Berührung gekommen. Lassen Sie uns nun die Definition eines Objektliterals klären: Ein Objektliteral ist eine durch Kommas getrennte Liste von Name-Wert-Paaren, die in ein Paar geschweifte Klammern eingeschlossen sind. Objektliterale können Variablen (Eigenschaften) und Funktionen (Methoden) haben. Wie andere Objekte in JavaScript kann es auch als Parameter oder Rückgabewert einer Funktion verwendet werden.
Definieren Sie nun ein Objektliteral und weisen Sie es einer Variablen zu:
// declaring an object literal var dog = { // object literal definition comes here... };
Fügen Sie Eigenschaften und Methoden zu diesem Objektliteral hinzu und greifen Sie dann im globalen Bereich darauf zu:
// declaring an object literal var dog = { breed: 'Bulldog', // object literal property bark: function() { // object literal method console.log("Woof!"); }, }; // using the object console.log( dog.breed ); // output Bulldog dog.bark(); // output Woof!
Das sieht dem vorherigen Namensraum sehr ähnlich, aber das ist kein Zufall. Die häufigste Verwendung von Literalobjekten besteht darin, Code in einem gekapselten Paket zu kapseln, um Konflikte mit Variablen oder Objekten im globalen Bereich zu vermeiden. Aus ähnlichen Gründen wird es auch häufig verwendet, um Konfigurationsparameter an Plugins oder Objekte zu übergeben.
Wenn Sie mit Entwurfsmustern vertraut sind, sind Objektliterale bis zu einem gewissen Grad Singletons, also ein Muster mit nur einer Instanz. Objektliterale verfügen von Natur aus nicht über die Fähigkeit zur Instanziierung und Vererbung. Als Nächstes müssen wir eine andere Möglichkeit kennenlernen, benutzerdefinierte Objekte in JavaScript zu erstellen.
Funktion ist ein erstklassiger Bürger von JavaScript, was bedeutet, dass alle von anderen Entitäten unterstützten Betriebsfunktionen unterstützt werden. In der Welt von JavaScript können Funktionen zur Laufzeit dynamisch erstellt, als Parameter verwendet, als Rückgabewerte anderer Funktionen verwendet und auch Variablen zugewiesen werden. Darüber hinaus können Funktionen auch eigene Eigenschaften und Methoden haben. Die Natur von Funktionen in JavaScript macht sie zu etwas, das instanziiert und vererbt werden kann.
Sehen wir uns an, wie Sie mit dem Konstruktor ein benutzerdefiniertes Objekt erstellen:
// creating a function function Person( name, email ) { // declaring properties and methods using the (this) keyword this.name = name; this.email = email; this.sayHey = function() { console.log( "Hey, I’m " + this.name ); }; } // instantiating an object using the (new) keyword var steve = new Person( "Steve", "steve@hotmail.com" ); // accessing methods and properties steve.sayHey();
创建构造函数类似于创建普通函数,只有一点例外:用 this 关键字定义自发性和方法。一旦函数被创建,就可以用 new 关键字来生成实例并赋予变量。每次使用 new 关键字,this 都指向一个新的实例。
构建函数实例化和传统面向对象编程语言中的通过类实例化并非完全不同,但是,这里存在一个可能不易被察觉的问题。
当使用 new 关键字创建新对象的时候,函数块会被反复执行,这使得每次运行都会产生新的匿名函数来定义方法。这就像创建新的对象一样,会导致程序消耗更多内存。这个问题在现代浏览器上运行的程序中并不显眼。但随着应用规则地扩大,在旧一点的浏览器、计算机或者低电耗设备中就会出现性能问题。不过不用担心,有更好的办法将方法附加给构造函数(是不会污染全局环境的哦)。
前面介绍中提到 JavaScript 是一种基于原型的编程语言。在 JavaScript 中,可以把原型当作对象模板一样来使用。原型能避免在实例化对象时创建多余的匿名函数和变量。
在 JavaScript 中,prototype 是一个非常特别的属性,可以让我们为对象添加新的属性和方法。现在用原型重写上面的示例看看:
// creating a function function Person( name, email ) { // declaring properties and methods using the (this) keyword this.name = name; this.email = email; } // assign a new method to the object’s prototype Person.prototype.sayHey = function() { console.log( "Hey, I’m " + this.name ); } // instantiating a new object using the constructor function var steve = new Person( "Steve", "steve@hotmail.com" ); // accessing methods and properties steve.sayHey();
这个示例中,不再为每个 Person 实例定义 sayHey 方法,而是通过原型模板在各实例中共享这个方法。
通过原型链,原型可以用来实例继承。JavaScript 的每一个对象都有原型,而原型是另外一个对象,也有它自己的原型,周而复始…直到某个原型对象的原型是 null——原型链到此为止。
在访问一个方法或属性的时候,JavaScript 首先检查它们是否在对象中定义,如果不,则检查是否定义在原型中。如果在原型中也没找到,则会延着原型链一直找下去,直到找到,或者到达原型链的终端。
现在来看看代码是怎么实现的。可以从上一个示例中的 Person 对象开始,另外再创建一个叫 Employee 的对象。
// Our person object function Person( name, email ) { this.name = name; this.email = email; } Person.prototype.sayHey = function() { console.log( "Hey, I’m " + this.name ); } // A new employee object function Employee( jobTitle ) { this.jobTitle = jobTitle; }
现在 Employee 只有一个属性。不过既然员工也属于人,我们希望它能从 Person 继承其它属性。要达到这个目的,我们可以在 Employee 对象中调用 Person 的构造函数,并配置原型链。
// Our person object function Person( name, email ) { this.name = name; this.email = email; } Person.prototype.sayHey = function() { console.log( "Hey, I’m " + this.name ); } // A new employee object function Employee( name, email, jobTitle ) { // The call function is calling the Constructor of Person // and decorates Employee with the same properties Person.call( this, name, email ); this.jobTitle = jobTitle; } // To set up the prototype chain, we create a new object using // the Person prototype and assign it to the Employee prototype Employee.prototype = Object.create( Person.prototype ); // Now we can access Person properties and methods through the // Employee object var matthew = new Employee( "Matthew", "matthew@hotmail.com", "Developer" ); matthew.sayHey();
要适应原型继承还需要一些时间,但是这一个必须熟悉的重要概念。虽然原型继承模型常常被认为是 JavaScript 的弱点,但实际上它比传统模型更强大。比如说,在掌握了原型模型之后创建传统模型简直就太容易了。
ECMAScript 6 引入了一组新的关键字用于实现 类。虽然新的设计看起来与传统基于类的开发语言非常接近,但它们并不相同。JavaScript 仍然基于原型。
以上就是JavaScript 中的面向对象编程的详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!