介紹原型的概念,和相關屬性,以及jquery判斷純淨對象的實現,不當心點進來的直接 ctrl+f 搜你想找的屬性。jquery
什麼是原型數組
isPrototypeOf() || Object.getPrototypeOf()函數
hasOwnProperty() || inthis
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
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() 方法會返回一個布爾值,指示對象是否具備指定的屬性做爲自身(不繼承)屬性。
若是判斷在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
是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,因此他是一個純淨的對象。 }
至此就是全文的全部內容,有疑問盡情留言,必定回覆。