原型(prototype)相關方法,jquery類型判斷源碼分析。

前言

介紹原型的概念,和相關屬性,以及jquery判斷純淨對象的實現,不當心點進來的直接 ctrl+f 搜你想找的屬性。jquery

  1. 什麼是原型數組

  2. isPrototypeOf() || Object.getPrototypeOf()函數

  3. hasOwnProperty() || inthis

  4. jQuery.isPlainObject() 源碼解讀spa

什麼是原型

prototype(原型,雛形,藍本) 說新上市的一部手機的原型機,就能夠用這個單詞。
每個函數默認都有一個prototype(原型)屬性,這個屬性指向函數的原型對象。就是說函數的原型是一個對象。先來打印一個函數的這個prototype屬性,來看看他是什麼樣的。prototype

function Zoom(){};
var proto = Zoom.prototype;
console.log(proto);

圖片描述

眼見爲實,這就是Zoom函數的原型對象,其中還有一個constructor 屬性,咱們並未對prototype原型對象進行修改,但卻有一個constructor屬性。默認狀況下,函數的原型對象都會獲取到一個constructor屬性。
constructor(構造器)英文中的解釋爲構造器。圖中constructor的屬性值爲Zoom函數。便於記憶,也能夠理解爲,函數的原型是由函數產生的,那構造出原型的東西,就是函數自己。也就是:code

Zoom.prototype.constructor = Zoom;
//語言描述就是:zoom函數的原型對象的constructor屬性,指向函數自己。

當咱們經過new操做符,獲取了一個構造函數的實例後(就是產生了一個對象)。先來看一個這樣的對象:對象

function Zoom(){
  this.bird = function(){
    console.log('bird');
  }
};
//在函數的原型上擴展了一個方法
Zoom.prototype.cat = function(){
  console.log("cat");
}
var zoom = new Zoom();
console.log(zoom);

圖片描述

能夠看到實例化的zoom對象下有一個__proto__屬性,而這個屬性就指向構造函數Zoom的原型對象。重點是,__proto__鏈接的是實例對象與構造函數原型對象,而不是,實例對象和構造函數。blog

isPrototypeOf() || getPrototypeOf()

function Zoom(){
  this.bird = function(){
    console.log('bird');
  }
};
//在函數的原型上擴展了一個方法
Zoom.prototype.cat = function(){
  console.log("cat");
}
Zoom.prototype.fish= function(){
  console.log("fish");
}
var zoom1 = new Zoom();
var zoom2 = new Zoom();
zoom1.cat ();//cat
zoom2.fish();//fish
console.log(zoom1);
console.log(zoom2);

圖片描述

根據上一節說的,zoom1,zoom2實例對象都有一個屬性__proto__指向構造函數的原型對象,換句話說,就是實例對象和構造函數沒什麼直接的聯繫。
可是咱們發現,這兩個實例都不包含方法,卻可以使用a,b 方法,這就是經過查找對象屬性的過程來實現的。
當咱們在調用一個對象的屬性值是,首先會從實例對象自己開始搜索,若是找到了就返回屬性值,沒有找到就在原型對象上查找。這也是原型對象的屬性或方法能夠共享的緣由。
那如何知道一個對象,例如兩個實例的原型鏈中,是否有構造函數的原型對象(Zoom)的方法呢。這就是isPrototypeOf()。用來肯定一個對象是否存在於另外一個對象的原型鏈中。繼承

console.log(Zoom.prototype.isPrototypeOf(zoom1));//true

雖然原型能夠共享,可是不能經過實例對象修改原型:

zom1.cat = function (){
  console.log('zom1 輸出的 cat');
}
zom1.cat ();//z1 輸出的 cat
zom2.cat ();//原型輸出的cat

這個其實很好理解,由於對象屬性查找是從實例向原型上查找,因此寫在實例上的方法若是和原型上的方法同名的話,會屏蔽原型上的方法,能夠簡單理解爲就近原則。

hasOwnProperty() || in

既然同一個方法能夠出如今實例中,也能夠出如今原型中,如何能夠判斷是否在實例中呢。
hasOwnProperty() 方法會返回一個布爾值,指示對象是否具備指定的屬性做爲自身(不繼承)屬性。
若是判斷在zoom1對象自身是否有a屬性,就能夠:

zoom1.hasOwnProperty(bird); // true
zoom1.hasOwnProperty(fish); // false

由於bird 是zoom1 自身的屬性,因此返回true,而fish是zoom1原型的的屬性,因此返回false。
另外一個要介紹的in方法,它比hasOwnProperty判斷的範圍更大,不管在原型上或者是在實例上,若是存在要檢測的屬性,都會返回true。

'bird' in zoom1; // true
'fish' in zoom1; // false

那就能夠理解爲,在in方法的判斷範圍中中排除hasOwnProperty的判斷範圍,剩下的不就是屬性只出如今原型中的可能。
轉爲簡單邏輯就是,若是in爲真則可能在實例中也可能在原型中,若是hasOwnProperty方法爲假,就確定不是在實例中。因此in爲真,hasOwnProperty爲假,就必定是在原型中:

function hasProtoTypeProto(attr,obj){
   return !obj.hasOwnProperty(attr) && (attr in obj)
}
hasProtoTypeProto('fish',zoom1) //true
hasProtoTypeProto('bird',zoom1) //false

isPlainObject()

是jquery提供的外部能夠直接使用的方法,這個方法是用來判斷一個對象是不是純粹的對象,即對象字面量,而不是一個構造函數的實例。其中涵蓋了大部分原型相關的方法。咱們先來看一下如何使用。

var log = console.log.bind(console);
log(jQuery.isPlainObject({x:0}));//true
log(jQuery.isPlainObject(new Object({})))//true
log(jQuery.isPlainObject([0]))//false
function Zoom(){
  this.fish = function(){
    return "Im a fish";
  }
}
var zoom = new Zoom();
log(jQuery.isPlainObject(zoom))//false

能夠看到只有純粹的對象纔會返回真,typeof類型檢測出數組爲object,或是構造函數的實例都是返回假。咱們看一看在jquery 3.1版本中是如何實現這個方法的。分析過程請見代碼:

//傳入須要判斷的對象
function isPlainObject(obj) {
//沒有具體的意義只是保存其餘執行的結果。
var proto, Ctor;
if (!obj || toString.call(obj) !== "[object Object]") {
  // toString 在以前的代碼中已被定義爲 Object.prototype.toString,代碼等價於Object.prototype.toString.call( obj ) !== "[object Object]"
  //在任何值上調用Object 原生的toString()方法,都會返回一個[object NativeConstructorName] 格式的字符串,
  //[[Class]]是一個內部屬性,全部的對象(原生對象和宿主對象)都擁有該屬性,這個屬性中就指定了上述字符串中的構造函數名(NativeConstructorName)
  // 相似的 [object Array] [object Function] [object RegExp]
  //可是這個判斷不能排除一個實例對象,由於上[[Class]] 屬性默認保存對象的類型,因此也會返回Object;
  //因此除了對象的其餘類型,都會返回false
  return false;
}
proto = getProto(obj);
// getProto 以前已經被定義爲 getProto = Object.getPrototypeOf 返回對象構造函數的原型
// Objects with no prototype (e.g., `Object.create( null )`) are plain
// 上一行是坐着的註釋,即爲經過Object.create( null ) 方法,能夠建立一個沒有原型的對象null
// 因此若是proto 爲false,表示對象沒有原型,只會是null,而null也是一個純粹的對象,因此返回真
if (!proto) {
  return true;
}

Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
// Objects with prototype are plain iff they were constructed by a global Object function
// hasOwn以前被定義爲,hasOwn = {}.hasOwnProperty 
// hasOwn.call( proto, "constructor" ) 傳入對象構造函數的原型,判斷原型上面有沒有constructor屬性(而不是在原型鏈的其餘位置),由於constructor屬性只會出如今Object函數的原型上,其餘函數原型的constructor屬性,都是從Object原型上繼承來的
// 因此有constructor屬性表示是經過Object對象獲得的,可是還不能肯定爲是Object的實例,或是字面量方式獲得。最後保存proto.constructor 便是傳入對象的構造函數


return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
// fnToString被定義爲hasOwn.toString 便是{}.hasOwnProperty.toString.call(Ctor);
// ObjectFunctionString定義爲fnToString.call( Object ) 便是{}.hasOwnProperty.toString.call(Object);
// 因此 typeof Ctor = 'fuction" 爲函數就是須要判斷對象的原型是函數類型,{}.hasOwnProperty目的是獲得一個函數,一個函數的toString方法會以字符串的格式顯示函數
// 若是兩個字符串相等,表示兩個函數相等。也就表示傳入對象的構造函數就是Object,因此他是一個純淨的對象。

}

至此就是全文的全部內容,有疑問盡情留言,必定回覆。

相關文章
相關標籤/搜索