由對象到原型

強大的對象

衆所周知,在面試中咱們常常會被面試官問到js的基本類型是什麼,這個問題雖然基礎但也正由於如此從而很考驗求職者的基礎。ok,那若是求職者回答出來了而且不想再考驗他基礎了,那就能夠換一個角度問他,js中最牛掰的類型是什麼?沒錯,答案就是除了基本類型以外的複雜類型 => Obejct。那麼接下來我要說明的,無非兩個問題,也許也是你看到這裏心中所想的:javascript

  • 1.到底啥是對象
  • 2.對象爲何牛掰

啥是對象?

  • 官方回答:無序屬性的集合,其屬性能夠包含基本 值、對象或者函數
  • 最簡單的來講:鍵值對,其中值能夠是數據或者函數
//一個簡單的例子
var person = new Object()
person.name = 'xiaoMing'
person.say= function(){
    console.log('my name is',this.name)
}
複製代碼

對象爲啥牛掰

迴歸本質,想一想編程世界,就是由兩大核心數據與代碼組成。數據本是靜態,而編程的世界確實五彩繽紛多樣的,所以代碼必須具有描述與操做數據的能力。然後基於此,各類各樣的編程思想編程庫編程框架,都是在以本身的方式更好地去提高這種能力(更快地操做數據、更優雅更高效地描述展現數據等)的關係。再回頭看看我上面寫的對象吧,它就是代碼具有這種能力的最好證實,也是javascript精巧的重要緣由,name是'xiaoMing'(描述數據),函數say輸出name(操做數據)。對象一會兒就把代碼的能力給展示出來了,還不牛掰嗎?反正我以爲挺牛掰,若是你不以爲,繼續往下看哈哈哈。。java

新的對象建立方式

咱們看到上面的代碼都是new Object的方式建立對象(也就是所謂的工廠模式),那創立了多個對象要如何區分呢?或者說如何分類的?能不能都是「人」的對象咱們都寫成,new People,都是「動物」的對象咱們都寫成new Animal,而不都是new Object?構造函數應運而生,也就是高程書上講的建立自定義的構造函數意味着未來能夠將它的實例標識爲一種特定的類型面試

function Person(){
   this.name = name;
   this.say = function(){
       console.log('my name is',this.name)
   }
}
var xiaoMing = new Person('xiaoMing');
var xiaoHong = new Person('xiaoMing');
//xiaoMing,xiaoHong都是Person
複製代碼

原型的到來

又有一個問題,咱們看到上面的代碼兩個實例xiaoMing和xiaoHong都有一個相同的say方法,可是經過上面的方式該方法被建立了兩次,有沒有辦法優化呢?有,咱們創造一個原型,把這個方法都放到上面去,你們都引用這個方法,那麼不是ok了?你建立多少實例都無所謂啊。原型是一個更爲獨立、複用、封裝、可繼承、可多態的強大對象編程

function Person(){
   this.name = name;
   this.say = function(){
       console.log('my name is',this.name)
   }
}

Person.prototype.say = function(){
       console.log('my name is',this.name)
}
var xiaoMing = new Person('xiaoMing');
var xiaoHong = new Person('xiaoHong');
複製代碼

那麼在代碼中用什麼來表示原型,又用什麼來指向原型呢?答案就是Prototype和proto。Person的prototype掛了全部實例共有的方法與屬性,xiaoMing等實例的_proto_.屬性指向原型。Person本事就做爲constructor.數組

注意:咱們通常不在原型上掛屬性,由於若是是一個包含引用類型值(諸如數組)的屬性時,原型上的屬性不會被屏蔽,改動結果會影響到別的實例框架

function Person(){}
Person.prototype.friends = ['xiaoGang'];

var xiaoMing = new Person();
var xiaoHong = new Person();
xiaoMing.friends.push('xiaoWang')
console.log(xiaoHong.friends) //["xiaoGang", "xiaoWang"]
複製代碼

原型鏈

由於一個實例多是有不一樣的對象慢慢衍生出來的,好比最初是Object,再到構造函數Person,再到某個實例,因此啊每一個實例會有一條_proto_鏈(_ proto _ . _ proto _ .toString),這個鏈就是原型鏈了。編程語言

原型的繼承

基於原型鏈,咱們其實能夠拓展一個功能,那就是既然_proto_能夠指向不一樣原型,那麼咱們 是否能夠去指定它指向多個咱們所但願的原型呢?這就是繼承。舉個生活中實際的例子,好比你,既繼承於你的爸爸,也繼承於你的媽媽,所以你能夠既眼睛長得像爸爸,鼻子長得像媽媽。來看看代碼中的實例:函數

function Person(name){
      this.name = name;
    }
    Person.prototype.say = function(){
      console.log('my name is',this.name)
    }

    function Student(num,name){
      this.studentNum = num;
      Person.call(this,name) //借用構造函數傳遞屬性
    }

    Student.prototype = new Person();
    Student.prototype.learn = function(){
      console.log('I am learning ...')
    }
    //糾正構造函數
    Student.prototype.constructor =Student;
    
    var xiaoMing = new Student(2,'xiaoMing');
    xiaoMing.say() //my name is xiaoMing
    xiaoMing.learn(); //I am learning ...
複製代碼

上述代碼中xiaoMing既繼承了Person,又繼承了Student,所以他又能夠say又能夠Learn。同時值得注意的是,咱們 借用了構造函數傳遞了屬性值。如此一來,原型的世界是否是瞬間被放大了不少呢?優化

  • 其餘的原型繼承模式 上述是一種比較經典的原型繼承方式,但發展至今,還有一些其餘的方式,但本質都是同樣,建立傳遞一個已經指向所想要繼承的原型,的_proto_屬性
    • 道格拉斯克勞福德(2006):
    function obejct(o){
      function F(){}
      F.prototype = o;
      //也能夠在這裏作一些擴展
      F.prototype.newFn = function(){}
      return new F();
    }
    Student.prototype = obejct(Person.prototype);
    複製代碼
    • 上述例子演化而來的ES5 API之Obejct.create
    Student.prototype = Object.create(Person.prototype)
    複製代碼

ES6-新時代繼承

到了ES6時代,繼承變得更加優雅簡潔了ui

class People {
      constructor(name){
        this.name  = name
      }
      say(){
        console.log('my name is',this.name)
      }
    }

    class Student extends People{
      constructor(props){
        super(props)
      }

      learn(){
        console.log('i am learning...')
      }
    }

    var xiaoMing = new Student('xiaoMing');
    xiaoMing.say()  //my name is xiaoMing
複製代碼

雖然說ES6這個語法有點像個語法糖,但仍有幾點值得說明注意:

  • 1.子類中的super會調用父類中的構造函數,這也是爲何子類能夠直接繼承父類中的屬性
  • 2.必需要在子類中調用super,才能夠在子類中使用this
  • 3.super關鍵字還能夠調用父類上的方法

此外,其實當一種語法已經‘語法糖’到讓人能夠毫無阻礙在開發中使用甚至捨棄以前API,那麼已經再也不是‘語法糖’了,更像是一個只是跟以前相似、卻更爲成熟的新型API

說完ES6,是時候說說「」了,由於它實在讓人聯繫到「類」,特別是「class」這個關鍵字。個人我的感悟是,像「類」,由於咱們以前也提到了 ,構造函數與工廠模式的重要差異就是給不一樣的對象一個標示,同時也能夠進行歸類。但 又不像「類」,在javascript中包括咱們本文提到的一切 都是圍繞對象所展開,這是一種基於對象的編程語言,甚至你所聯想到或者所認爲的「類」,在javascript的世界裏,這都是對象,由於你class出來的東西均可以用鍵值數來描述,這是一種更爲本質與微觀的見解。

總結

本文的描述內容主要針對如下幾方面:

  • js中的對象(具備描述與操做數據的能力)
    • 對象建立
      • 工廠模式
      • 構造函數
  • js中的原型(十分高級的對象)
    • 基本的原型描述
    • 原型鏈
    • 原型繼承

從上述方面依次往下看,咱們清晰能夠看到javascript語言生命力的不斷旺盛。甚至到後來的原型,已經能夠實現面向對象中封裝、多態、繼承的三大概念(本文爲了不大篇幅沒有具體展開,其實用多了你就能夠感覺到的),同時也具備很強大的靈活性。 若是如今你再切回到更宏觀更本質的角度來看,基本類型 => 對象 => 原型 => 對象 => 基本類型,是否會以爲javascript的世界清晰了不少呢

相關文章
相關標籤/搜索