JavaScript的創世神話——一切源於對象

《聖經》裏的第一章創世紀中其中有一段經典記載上帝是如何創造人的。
神說:「咱們要照着咱們的形象,按照咱們的樣式造人」。不謀而合的是,JavaScript中彷佛也遵循着上帝的旨意去創造程序世界,一切皆對象的認知裏背後是世間萬物皆源於一個原型,一個統一的形式,一個柏拉圖口中的理念......javascript

JavaScript的編程藝術也由此演繹開來~~~css

目錄:
1.面向對象編程的一些初步思考
2.類與對象
3.構造函數constructor
3.1 new命令
3.2 this關鍵字
3.3.原型對象
4.一些相關的方法html

創世紀

1.面向對象編程的一些初步思考

"面向對象編程"(Object Oriented Programming,縮寫爲OOP)自己是一種編程的思惟模式,它把世界的一切看做是對象的集合,世界的運轉就是靠一個個對象分工、合做的結果。java

有了這麼一種宏觀維度的編程認知,那麼咱們在編程時也就不會困在代碼的死衚衕了出不來。jquery

給一段小代碼作初步解釋:
咱們在網頁開發中,常常要進行事件綁定。編程

var btn = document.getElementById('button');
btn.addEventListener('click',function(){console.log('click me')})

初學時,筆者也是無心識的看到addEventListener()方法能夠進行事件綁定以實現點擊特定元素後,就能夠實現需求就沒有再往深層次去想太多;瀏覽器

隨着學習的深刻,在進入OOP的章節,咱們就會發現「一切皆對象」這句話的深入性。函數

上面的代碼中,btn自己就一個對象,點擊時該對象將會調用自身的方法addEventListener()去幹事。學習

一個完整的解釋就是誰(對象)幹了什麼(方法),btn被點擊時幹了輸出‘click me ’。this

固然這只是其中一個案例,JS的編程中這種編程模式貫通始終,如:

//一、定義一個對象
var sheep = {
  color: white,
  echo: function(){
      console.log('mae~~~')
  }
} 
sheep.echo()
//這隻羊發出mae的叫聲

//2.設置對象的樣式
$('.passage').css({
      width: '100px',
      color: 'pink'
  }
)
//這個名爲passge的jquery對象被設置寬和色
......

總之,JavaScript的編程沒法脫離對象而獨存

2.類與對象

說了那麼久,若是連對象是什麼都沒一個清晰的界定,那麼在編程過程當中仍是存在模糊地帶。

  • 什麼是對象
    對象能夠說是對現實事物的抽象,對象封裝了屬性和方法,屬性值指的是對象的狀態,方法指的是對象的行爲。

好比,咱們把NBA的巨星kobe抽象爲javascript裏的對象:

var kobe = {
    team: 'Los Angeles Lakers',
    position: 'shooting guard',
    ability :  function(){
        console.log('impress those who like basketball')
  }
}

現實世界的科比抽象爲js中kobe這一對象,效力於洛杉磯湖人和位置是得分後衛是他的屬性,能力是影響許多愛好籃球的人是他的方法。

  • 什麼是'類'(構造函數)

按照聖經的記載,在第一章的創世紀中寫道:「神按照着本身的形象造人。」

現實世界的萬物(對象)的演化不是憑空誕生的,它須要按照一個模板去構造出各類實例出來。

所以,類(構造函數)就是提供這麼一種模板的‘對象’(函數自己在js中就是對象),它是對象的抽象

可是,js中並無類的概念,而是經過構造函數替代了類的功能,爲某一類的對象提供共同的屬性和方法。

經過構造函數可以創造各類具備特定類的屬性和方法的實例對象。

好比,定義一個籃球運動員的類(構造函數):

//定義一個類,該類包含了一個籃球運動員應有的屬性和方法
var BasketballPlayer = function(){
   this.height = '180+',
   this.muscle = true,
   this.ability = function(){
        console.log('shooting and passing ')
    }
}

//由類(構造函數)創造出3個實例對象
var kobe = new BasketballPlayer();
var james =new BasketballPlayer();
var curry =new BasketballPlayer();

這裏有個小問題,構造函數BasketballPlayer又是有誰構造出來呢?看了下面的代碼便知~~~

BasketballPlayer instanceof Object//true
構造函數或者是函數在js中天生就是構造函數Object的實例

因此說,全部的實例對象都有類(構造函數)創造出來,而全部的構造函數都是由最爲通常的類,即Object構造出來,故一切皆對象

【注】
類在js中表現爲構造函數,爲了準確起見,下面統一稱爲構造函數,咱們只須要知道兩者起到的做用是一致就行。

3.構造函數

前面咱們瞭解到,世間萬物(實例對象)都是按照特定的模板(類或構造函數)構造的,而全部的構造函數都是由最通常的構造函數Object構造的。

可是,咱們或許看到下面的代碼會產生這麼一種疑惑:
1.構造函數中的Book的this是幹嗎的,有什麼門道?
2.利用構造函數Book構造實例javascript時new起到什麼做用
3.爲何我構造出一個實例對象後,這個構造函數可以返回一個對象給我
總結成一句就是:在整個造物的過程當中,構造函數的運做機制是怎麼樣的???

var Book = function(){
    this.material = 'paper';
    this.color = 'white';
    this.utility = function(){
       console.log('IQ+');
  }
    this.whoAmI = function(){
      console.log(this)
  }
}

var javascript = new Book()
  • this在構造函數的做用

    關鍵字this在js中顯得十分重要,它在不一樣的運行環境(屬性和方法的調用者)指向的環境(對象)不一樣,也就是說this的指向是動態的,可是不管this的指向是誰,只要清楚屬性和方法的調用者是誰那麼this就指向誰。

//在瀏覽器全局環境下,即window對象下
var print = function(){
      console.log(this)
 }
print()//this指向Window,由於這是Window對象調用了print方法

//在特定對象的環境下
var o = {
    print: function(){
      console.log(this)
  }
}
o.print()//this指向o,由於這是o對象調用print方法

所以,回到構造函數中的this來,當執行 var javascript = new Book()時,此時是javascript這個實例對象調用了構造函數Book,函數內部的this指向javascript這一實例對象

javascript.whoAmI()//this此時指向javascript對象

【注】更多this的知識詳見what's this???

  • new命令的原理

    接下來談一談new命令的原理。

new命令的做用,就是執行構造函數,返回一個實例對象

與普通的正常調用函數不一樣,在函數執行前面附加new命令,函數執行如下步驟:

1.建立一個空對象,做爲將要返回的實例對象;

2.將這個空對象的原型``__proto__``指向構造函數的prototype屬性以實現繼承機制
3.將這個空對象賦值給函數內部的this關鍵字
4.開始執行構造函數內部的代碼
  • 原型對象

    上面咱們基本瞭解構造函數在創造實例對象時的部分運做機制,明白了this關鍵字和new命令在構造實例時所起的做用。

如今有一個最最重要的疑問是實例對象到底是如何繼承構造函數中設定好的屬於該類的共有的屬性和方法呢?

prototype object

JavaScript中每一個實例對象繼承自另外一個對象,後者被稱爲原型對象,原型對象上的屬性和方法都能被派生對象共享,這就是JavaScript著名的繼承機制的基本設計。

先上一段代碼用於講解:

function Dog(name,color){
  this.name = name;
  this.color = color;
}

Dog.prototype.spark = function(){
    console.log('Wow~~~')
}
var tony = new Dog('tony','white')

1.經過構造函數Dog生成的實例對象tony,會自動爲實例對象tony分配原型對象;

2.每個構造函數都有一個prototype屬性,該屬性就是實例對象的原型對象

3.實例對象內部有一個__proto__屬性,該屬性在被構造函數一創造出來就指向構造函數的protype屬性

這樣一來,咱們經過構造函數Dog中的原型對象prototype實現了實例對象tony對Dog的共有屬性和方法的繼承。

所以,咱們能夠得出的思考是,原型對象定義全部實例對象的共有的屬性和方法,全部的實例對象無非是從原型對象衍生出的子對象,只不過在後來給它添加了特有的屬性和方法罷了。

prototype chain

實例對象tony的__proto__指向構造函數Dog的prototype對象,所以繼承了Dog中prototype的屬性和方法;

而構造函數自己也存在__proto__指向更通常的函數(本質上也是對象)的prototype對象;

更進一步,該函數也存在__proto__指向最通常的構造函數Object的prototype對象

這種層層嵌套的關係造成一條原型鏈(prototype chain)。

一隻名叫tony的狗,首先繼承了構造函數Dog的原型對象,而Dog的原型對象中的__proto__有繼承了函數的原型對象,函數對象中的__proto__有繼承了Oject的原型對象。

這裏再一次體現了構造函數Object的威力,全部的對象無非都是Object的衍生,均繼承Object.prototype的屬性和方法,更加深入表達「一切皆對象」的思想。

4.一些相關的方法

  • instanceof 運算符
    instanceof運算符返回一個布爾值,表示指定對象是否爲某個構造函數的實例

tony instanceof Dog//true
or
Dog.prototype.isPrototypeOf(tony)//true
  • Object.getPrototypeOf()

Object.getPrototypeOf()返回一個對象的原型,這是獲取原型對象的標準方法

  • Object.setPrototypeOf()
    Object.setPrototypeOf

方法能夠爲現有對象設置原型,返回一個新對象,該方法接受兩個參數,第一個是現有對象,第二個是原型對象。

var foo = { x:1 };
var bar = Object.setPrototypeOf({},foo)
bar.x === 1//true

咱們以前以new命令去構建實例對象,本質上就是把一個空對象的proto指向構造函數的prototype,而後在實例對象上執行構造函數

var  Person = function(){
    this.race = 'monkey'
};
var Asian = new Person();
//等價於
var Asian = Object.setPrototypeOf({},Person.prototype);
Person.call(Asian)
  • Object.create()
    Object.create方法用於從原型對象生成新的實例對象,能夠替代new命令

var  Person = {
    race: 'monkey'
}
var Asian = Object.create(Person)
//等價於
var Person = function(){
    this.race='monkey'
}
var Asian = new Person()
  • Object.isPrototypeOf()
    對象實例的isPrototypeOf方法,用來判斷一個對象是不是另外一個對象的原型。

var obj1 = {};
var obj2 = Object.create(obj1);
obj1.isPrototypeOf(obj2)//true
  • Object.prototype.hasOwnProperty()
    對象實例的hasOwnProperty方法返回一個布爾值,用於判斷某個屬性定義在對象自身,仍是定義在原型鏈上。

var o = {
  name:'teren'
}
o.hasOwnProperty('name')//true
o.hasOwnProperty('toString')//false
Object.getPrototypeOf(o).hasOwnProperty('toString')//true
參考文獻:

阮一峯-JavaScript標準參考教程
飢人谷學習筆記

相關文章
相關標籤/搜索