title: JS對象(1)從新認識面向對象
date: 2016-10-05
tags: JavaScriptjava
從設計模式上看,對象是計算機抽象現實世界的一種方式。編程
面向對象編程(object-oriented programming)的最主要目的是提升程序的重複使用性。設計模式
咱們說,對象具備屬性(property)和方法(function),(其實本質上方法也是一種屬性)在面向對象的設計模式中,屬性是一種描述,描述對象的狀態,好比一我的一天 24 小時的情緒,每一個時刻他的情緒都具備不一樣的狀態,喜怒哀樂,抑鬱癲狂,貪嗔癡念,固然情緒只是生而爲人的衆多狀態中的一種而已。而對象中的方法是一種功能,它的功能就是操做對象的屬性。好比,吃飯能夠改變一我的的狀態,對吃貨而已,吃飽之後會以爲特別幸福,吃飯是一種功能,它改變了一我的的情緒,讓人以爲幸福。瀏覽器
那麼,反過來細想,人是一個對象,它具備情緒,他還要吃飯。因此能夠說,對象是對屬性和方法的一種封裝。數據結構
對象中的幾個概念:閉包
一切事物皆對象架構
對象具備封裝和繼承特性編程語言
對象與對象之間使用消息通訊,各自存在消息隱藏函數
然而,面向對象僅僅是一個概念或者編程思想而已,它不該該依賴於某個語言存在。好比 Java 採用面向對象思想構造其語言,它實現了類、繼承、派生、多態、接口等機制。可是這些機制,只是實現面向對象編程的一種手段,而非必須。換言之,一門語言能夠根據其自身特性選擇合適的方式來實現面向對象。spa
JavaScript 語言是經過一種叫作原型(prototype) 的方式來實現面向對象編程的。
因此不管是基於類的(class-based)面向對象,仍是 基於原型的 (prototype-based) 面向對象 也都只是爲實現面向對象這一理念在構造客觀世界的兩種不一樣方式而已。
在基於類的面向對象方式中,對象(object) 依靠 類(class) 來產生。而在基於原型的面向對象方式中,對象(object) 則是依靠 構造器(constructor) 利用 原型(prototype) 構造出來的。
而關於這兩種方式誰更爲完全地表達了面向對象的思想,目前尚有爭論。
類是一個抽象概念而並不是實體,而對象的產生是一個實體的產生;
原型方式中的構造器 (constructor) 和原型 (prototype) 自己是其餘對象經過原型方式構造出來的對象。
在類式面嚮對象語言中,對象的狀態 (state) 由對象實例 (instance) 所持有,對象的行爲方法 (method) 則由聲明該對象的類所持有,而且只有對象的結構和方法可以被繼承;而在原型式面嚮對象語言中,對象的行爲、狀態都屬於對象自己,而且可以一塊兒被繼承。
JavaScript 是一種基於原型的編程語言,並無 class 語句,而是把函數用做類。固然,ES6 提供了 class 的語法糖
ECMAScript 是一門完全的面向對象的編程語言,JavaScript 是其中的一個變種 (variant)。它提供了 6 種基本數據類型,即 Boolean、Number、String、Null、Undefined、Object。爲了實現面向對象,ECMAScript設計出了一種很是成功的數據結構 - JSON(JavaScript Object Notation), 這一經典結構已經能夠脫離語言而成爲一種普遍應用的數據交互格式。
ECMAScript除了字面式聲明(literal notation) 方式以外, 容許經過構造器(constructor) 建立對象。每一個構造器其實是一個函數(function) 對象, 該函數對象含有一個prototype
屬性用於實現基於原型的繼承(prototype-based inheritance) 和共享屬性(shared properties)。對象能夠由 「new 關鍵字 + 構造器調用」 方式來建立。
但須要指出的是,這與java語言的new含義毫無關係,由於其對象構造的機理徹底不一樣。ECMAScript 徹底能夠用其它非new表達式來用調用構造器建立對象。
涉及到繼承這一塊,Javascript 只有一種結構,那就是:對象。在 javaScript 中,每一個對象都有一個指向它的原型對象(prototype)的內部屬性_proto_屬性。而這個原型對象又有本身的原型,直到某個對象的原型爲 null 爲止(也就是再也不有原型指向),組成這條鏈的最後一環。這種一級一級的鏈結構就稱爲原型鏈(prototype chain)。
一張經典的 JavaScript 原型鏈圖能夠揭示一切:
要理解上圖,只須要搞明白兩個屬性(指針)的區別,便是_proto_ 屬性和 prototype 屬性。
每一個構造函數都有一個 prototype 屬性,該屬性是一個指針,指向該構造函數的原型。
而實例中包含一個屬性 _proto_ 屬性,該屬性是一個指針,指向該實例所屬的構造函數建立的原型。
不一樣的瀏覽器對實例中的 _proto_ 屬性表示不一樣。
每建立一個構造函數便會相應的建立一個與其相對應的原型對象(由構造函數建立出來的原型對象,JS 引擎自動建立)。
在默認狀況下,構造函數建立的原型對象都會有一個 constructor 屬性,而這個屬性指向 prototype 屬性所在的那個函數。
因此,咱們能夠得出一個結論,構造函數的 prototype 和由該構造函數建立的實例的 _proto_ 指向的是內存中的的同一塊地址,也正是如此,才使得在原型中定義的方法和屬性具備共享的基礎。
有了 原型鏈,即可以定義一種所謂的 屬性隱藏機制,並經過這種機制實現繼承。ECMAScript 規定,當要給某個對象的屬性賦值時,解釋器會查找該對象原型鏈中第一個含有該屬性的對象(注:原型自己就是一個對象,那麼原型鏈即爲一組對象的鏈。對象的原型鏈中的第一個對象是該對象自己)進行賦值。
因此,當試圖訪問一個對象的屬性時,它不只僅在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依此層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾。
基於原型的繼承方式,雖然實現了代碼複用,但其行文鬆散且不夠流暢,可閱讀性差,不利於實現擴展和對源代碼進行有效地組織管理。不得不認可,類式繼承方式在語言實現上更具健壯性,且在構建可複用代碼和組織架構程序方面具備明顯的優點。
JavaScript 沒有實現面向對象中的信息隱藏,即私有和公有。與其餘類式面向對象那樣顯式地聲明私有公有成員的方式不一樣,JavaScript 的信息隱藏就是靠閉包實現的。