ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript によるオブジェクト指向プログラミングの詳細な紹介

JavaScript によるオブジェクト指向プログラミングの詳細な紹介

黄舟
黄舟オリジナル
2017-03-04 15:54:00994ブラウズ

はじめに

JavaScript は強力なオブジェクト指向プログラミング言語ですが、従来のプログラミング言語とは異なり、プロトタイプベースの OOP モデルを使用しているため、ほとんどの開発者にとってその構文は理解できません。さらに、JavaScript は関数をプライマリ オブジェクトとして扱うため、言語に詳しくない開発者にとっては大きな混乱を引き起こす可能性があります。そのため、これを簡単な紹介として前面に掲載することにしました。また、JavaScript でのオブジェクト指向プログラミングのリファレンスとしても使用できます。

このドキュメントは、オブジェクト指向プログラミングのルールのプレビューを提供するものではなく、そのインターフェイスの概要を提供します。

名前空間

サードパーティのライブラリ、フレームワーク、および Web 依存関係がますます登場するため、JavaScript の開発では名前空間が不可欠になっており、グローバルな名前空間の競合を回避する必要があります。

残念ながら、JavaScript は名前空間のコンパイルをサポートしていませんが、オブジェクトを使用して同じ結果を達成することができます。 JavaScript では、名前空間インターフェイスを実装するためのパターンが多数ありますが、ここでは、この分野で最も一般的に使用されるパターンであるネストされた名前空間について説明します。

ネストされた名前空間

ネストされた名前空間パターンは、オブジェクト リテラルを使用して、特定のアプリケーションの特定の名前を持つ機能をバンドルします。

最初にグローバル オブジェクトを作成し、それを MyApp という変数に割り当てます。

// global namespace
var MyApp = MyApp || {};

上記の構文は、MyApp が定義されているかどうかを確認します。すでに定義されている場合は、それを自分自身に割り当てるだけですが、代わりに関数と変数を保持する空のコンテナーを作成します。

同じ手法を使用してサブ名前空間を作成することもできます。例:

// sub namespaces
MyApp.users = MyApp.user || {};

コンテナを開始すると、既存の定義と競合する危険を冒さずに、(コンテナ) 内で関数と変数を定義し、グローバル名前空間でそれらを呼び出すことができます。

// 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();

JavaScript 命名パターンの内部概要は、Goggle の Addy Osmani によって記事「Essential JavaScript Namespacing Patterns」で紹介されました。さまざまなモードを試してみたい場合は、ここから始めるのが最適です。

オブジェクト

JavaScript コードを書いたことがあるなら、すでにオブジェクトを使用したことがあるでしょう。 JavaScript には 3 種類のオブジェクトがあります:

ネイティブ オブジェクト

ネイティブ オブジェクトは言語仕様の一部であり、実行されるオペレーティング環境に関係なく使用できます。ネイティブ オブジェクトには、Array、Date、Math、parseInt などが含まれます。すべてのネイティブ オブジェクトについて詳しくは、JavaScript 組み込みオブジェクト リファレンスを参照してください

var cars = Array(); // Array is a native object

ホスト オブジェクト

ネイティブ オブジェクトとは異なり、ホスト オブジェクトは JavaScript コードが実行される環境によって作成されます。環境が異なれば、異なるホスト オブジェクトが作成されます。これらのホスト オブジェクトを使用すると、ほとんどの場合、ホスト オブジェクトと対話できます。ブラウザ (実行環境の 1 つ) 上で実行されるコードを作成する場合、ウィンドウ、ドキュメント、場所、履歴などのホスト オブジェクトが存在します。

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

ユーザー オブジェクト

ユーザー オブジェクト (または埋め込まれたオブジェクト) は、コード内で定義され、実行時に作成されるオブジェクトです。 JavaScript で独自のオブジェクトを作成するには 2 つの方法があります。詳細は以下で説明します。

オブジェクト リテラル

名前空間を作成する前のデモで、すでにオブジェクト リテラルについて触れました。ここで、オブジェクト リテラルの定義を明確にしましょう。オブジェクト リテラルは、中括弧のペアで囲まれた名前と値のペアのコンマ区切りのリストです。オブジェクト リテラルには変数 (プロパティ) と関数 (メソッド) を含めることができます。 JavaScript の他のオブジェクトと同様に、関数のパラメーターまたは戻り値としても使用できます。

ここでオブジェクト リテラルを定義し、それを変数に割り当てます:

// declaring an object literal

var dog = {

	// object literal definition comes here...

};

このオブジェクト リテラルにプロパティとメソッドを追加し、グローバル スコープでそれらにアクセスします:

// 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!

これは前の名前空間と非常によく似ていますが、実際にはそうではありません。偶然ではありません。リテラル オブジェクトの最も一般的な使用法は、グローバル スコープ内の変数またはオブジェクトとの競合を避けるために、カプセル化されたパッケージにコードをカプセル化することです。同様の理由で、構成パラメータをプラグインやオブジェクトに渡すためにもよく使用されます。

デザイン パターン に精通している方なら、オブジェクト リテラルはある程度シングルトン、つまりインスタンスが 1 つだけあるパターンであることがわかります。オブジェクト リテラルには本質的にインスタンス化および継承する機能がありません。次に、JavaScript でカスタム オブジェクトを作成する別の方法について学ぶ必要があります。

Constructor

コンストラクターを定義する

関数は JavaScript の第一級市民であり、他のエンティティによってサポートされるすべての操作関数がサポートされることを意味します。 JavaScript の世界では、関数を実行時に動的に構築したり、パラメーターとして使用したり、他の関数の戻り値として使用したり、変数に代入したりすることができます。さらに、関数は独自のプロパティやメソッドを持つこともできます。 JavaScript の関数の性質により、関数はインスタンス化および継承できるものになります。

コンストラクターを使用してカスタム オブジェクトを作成する方法を見てみましょう:

// 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)!

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。