首頁  >  文章  >  web前端  >  JavaScript 中的物件導向程式設計的詳細介紹

JavaScript 中的物件導向程式設計的詳細介紹

黄舟
黄舟原創
2017-03-04 15:54:00988瀏覽

介紹

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 有三種類型的物件:

原生物件

原生物件是語言規範的一部分,不管在什麼樣的運行環境下運行,原生物件都可用。原生物件包括:Array、Date、Math 和 parseInt 等。若想了解所有原生對象,請參閱 JavaScript 內建物件參考

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

宿主物件

與原生物件不同,宿主物件是由 JavaScript 程式碼執行的環境所建立。不同的環境環境創建有不同的宿主物件。這些宿主物件在多數情況下都允許我們與之互動。如果我們寫的是在瀏覽器(這是其中一個運行環境)上運行的程式碼,會有 window、document、location 和 history 等宿主物件。

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 中有兩種方式可以創造自己的對象,以下詳述。

物件字面量

在前面示範建立命名空間的時候,我們已經接觸到了物件字面量。現在來搞清楚物件字面量的定義:物件字面量是置於一對花括號中的,由逗號分隔的名-值對清單。物件字面量可擁有變數(屬性)和函數(方法)。像 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!

這來看起來和前面的命名空間很像,但這並不是巧合。字面量物件最典型的用法就是把程式碼封裝起來,使其在一個封裝的套件中,以避免與全域作用域中的變數或物件發生衝突。由於類似的原因,它也常用於向插件或物件傳遞配置參數。

如果你熟悉設計模式的話,物件字面量在某種程度上來說就是單例,就是那種只有一個實例的模式。物件字面量先天不具備實例化和繼承的能力,我們接下來還要了解 JavaScript 中另一種建立自訂物件的方法。

建構子

定義建構子

函數是 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