原型與原型鏈

JavaScript 中,萬物皆對象!但對象也是有區別的。分爲普通對象和函數對象,Object ,Function 是JS自帶的函數對象。javascript

怎麼區分?其實很簡單,凡是經過 new Function() 建立的對象都是函數對象,其餘的都是普通對象。Function Object 也都是經過 New Function()建立的。html

構造函數

構造函數可用來建立特定類型的對象。像Object和Array這樣的原生構造函數,在運行時會自動出如今執行環境中。java

function Person() {

}
var person = new Person();
person.name = 'Kevin';
console.log(person.name) // Kevin
複製代碼

按照國際慣例,構造函數始終都應該以一個大寫字母開頭,而非構造函數則應該以一個小寫字母開頭。git

要建立Person的新實例,必須使用new操做符。通常是經歷如下四個步驟:github

  • 一、建立一個新對象;
  • 二、將構造函數的做用域賦給新對象(此this的指向了這個新對象)
  • 三、爲這新對象添加構造函數的屬性
  • 四、返回新對象

上面例子中,person 就用有一個constructor (構造函數)屬性,該屬性是指向Person。api

原型對象

不管何時,只要建立一個新函數,就會根據一組特定的規則爲該函數建立一個prototype屬性,這個屬性指向函數的原型對象。普通對象沒有 prototype,但有 __proto__ 屬性。數組

如上面例子,Person.prototype 指向了原型象,而 Person.prototype.constructor 又指回了Person。函數

而 person 只是一個對象實例。ui

而person能夠訪問保存在原型中的值,但卻不能重寫原型中的值。若是咱們在 person 中添加一個屬性,而該屬性與實例原型中的一個屬性同名,那個person中的屬性會暫時屏蔽原型中的屬性,刪除後,仍是讀回原型中的屬性this

function Person() {

}
Person.prototype.name = 'Perty';
var person = new Person();
person.name = 'Kevin';
console.log(person.name) // Kevin
delete person.name
console.log(person.name) // Perty
複製代碼

解析器的操做是:

  • 「實例person有name屬性嗎?」
  • 「有」 —— 因而就讀取實例中的name屬性,輸出 Kevin

刪除後,再運行時:

  • 「實例person有name屬性嗎?」
  • 「沒有」
  • 「person的原型有name屬性嗎?」
  • 「有」 —— 因而就讀取原型中的name的屬性,輸出 Perty

原型鏈

在建立對象(不管是普通對象仍是函數對象)的時候,都有一個叫作 __proto__ 的內置屬性,用於指向建立它的函數對象的原型對象 prototype。以上面的例子爲例:

console.log(person.__proto__ === Person.prototype)  // true
複製代碼

一樣的,Person.prototype 也一樣有 proto 屬性,它指向建立它的函數對象(Object)的prototype

console.log(Person.prototype === Object.prototype) // true
複製代碼

繼續,Object.prototype對象也有__proto__屬性,但它比較特殊,爲null

console.log(Object.prototype.__proto__) // null
複製代碼

咱們把這個有 __proto__ 串起來的直到 Object.prototype.__proto__ 爲null的鏈叫作原型鏈。

person.__proto__ ==> Person.prototype.__proto__ ==> Object.prototype.__proto__ ==> null

最佳實例zepto

讀過zepto的源碼都瞭解到$實際上是一個函數,同時在 $ 身上又掛了不少屬性和方法。

如下代碼:

<p id="p1">段落1</p>
<p id="p2">段落2</p>
<p id="p3">段落3</p>
<script type="text/javascript" src="js/zepto-1.1.6.js"></script>
複製代碼

$('p')返回的是類數組的同樣東西。

咱們先一步步來分析一下它到底是不是數組:

var arr = [1,2,3];
var $p = $('p');

// 對比1
arr.__proto__.constructor === Array;  // true
$p.__proto__.constructor === Array;  // false

// 對比2
arr instanceof Array;  // true
$p instanceof Array;  // false
複製代碼

從原型指向中,能夠看出$p真的只是相似數組同樣的數組。而數組是沒有addClass,removeClass等等的屬性。

咱們能夠看console中,運行console.log($p.__proto__),就能夠看$p可調用的方法(這也是平時在使用zepto時,忘記api方法名時,能夠即時查出來)

同理,咱們查看數組下面的方法console.log(arr.__proto__)

能夠看到在arr中是沒有addClass,removeClass等等的屬性的。

那咱們也爲arr建立一個addClass方法試試:

var arr = [1,2,3]
arr.__proto__ = {
    addClass: function () {
      console.log(123);
    }
};
arr.addClass();   // 123
複製代碼

此時咱們再去拿arr來作第一次的那幾個驗證,獲得的結果就和以前的$p同樣了,即arr此時也稱了一個不是數組的數組

這樣就能夠成功建立了addClass方法,而這個解析器的操做是:

  • 建立一個新的數組實例[1,2,3]
  • 新實例添加一個屬性方法addClass

運行arr.addClass()時:

  • 「實例arr有addClass屬性嗎?」
  • 「有」 —— 因而就讀取實例中的addClass屬性,輸出 123

這裏只是淺析zepto的基本設計,也是瞭解zepto設計原理的開始,想要更深刻理解zepto,請移步zepto

相關文章
相關標籤/搜索