本篇主要是記錄一下對js中對於原型的理解...編程
原型涉及到構造函數, 原型對象, 實例化對象三者之間的關係...數組
構造函數安全
function Person (name,age) { //(1)建立一個空對象: {} //(2)將this指向這個空對象 : this = {} //(3)執行構造函數賦值代碼(完成對象的賦值) this.name = name; this.age = age; this.sayHi = function () { console.log(this.name + 'hello world'); }; //(4)返回這個對象 }; var man = new Person('huahua',18); 1.何爲構造函數? 構造函數:首先,它是函數,而且任何的函數均可以做爲構造函數存在,它的本質是初始化對象。 構造函數都是和new關鍵詞一塊兒使用的。 new就是在建立對象,從聲明開始一共作了4件事(如上),構造函數就是在爲初始化的對象添加屬性和方法(成員) 2.構造函數的特色: a:構造函數的首字母必須大寫,用來區分於普通函數 b:內部使用的this對象,來指向即將要生成的實例對象 c:使用New來生成實例對象
實例對象函數
1.上面的 man 就是經過Person這個構造函數實例化出來一個對象,咱們稱爲 **實例化對象**;何爲對象的實例化呢? 2.在我看來就是給一個空對象添加了一些屬性和方法,使其具備了一些特徵和行爲...也就是上面new關鍵字乾的事;跟面向對象中的一些概念比較相似... 面向對象編程: 面向對象就是對現實中的事物進行抽象化...而後再給其設置特徵屬性和行爲使之具體化; 面向對象就是對面向過程進行封裝後的結果... 3.實例對象中存在一個__proto__屬性; 這個屬性指向了構造函數的原型prototype... 注意: 實例對象訪問成員的規則:先看本身有沒有這個成員,若是有則訪問,沒有則訪問原型的
原型對象this
上面已經聊過構造函數和實例化對象了,那麼原型對象又是什麼呢? 當咱們在聲明一個函數時, 系統會幫咱們建立一個與該函數對應的屬性prototype,咱們稱它爲原型對象; 以上面的Person爲例,這個prototype是該函數的一個屬性,咱們能夠調用Person.prototype來修改其成員或者進行重寫; 原型對象中有一個構造器指針constructor屬性來指向對應的構造函數,他的做用是可讓實例對象知道本身是哪個構造函數生成的; 如 man.constructor 即man.__proto__.constructor指向了 Person.
下面用一張圖來表示他們之間的關係...spa
原型對象中能夠存儲不少成員屬性和方法,多個實例對象之間就能共享這些屬性和方法; 相似實現了面向對象中 繼承 的效果...prototype
面向對象的三大特性:指針
封裝:將功能代碼封裝到對象中,只暴露外部接口(API),使用者無需關心內部實現 繼承:一個對象擁有另外一個對象全部的成員變量(屬性和方法) 多態: 一個對象在不一樣狀況下的多種狀態; 一個對象通過不一樣操做後會有不一樣的行爲.... (js從語法的角度上來講沒有多態,由於js是基於對象的語言) js實現繼承的方式: 1. 咱們能夠遍歷父對象,將父對象的屬性動態添加到子對象中 (適用於一個子對象的繼承) for (var key in father){ son[key] = father[key]; }; 2. 替換原型:將父對象做爲子對象構造函數的原型(可是會丟失以前的原型對象的成員) // 子對象用構造函數來實例化 function Son(name, age) { this.name = name; this,age = age; } Son.prototype.father = { parent: 'laosong', age: 47, } // Son 原型對象中的成員 var son = new Son('xiaowang', 24) var father = { name: 'laowang', age: 48, } Son.prototype = father; // 至關於Son的原型被從新賦值,替換了,laosong不在了 3. 綜合上面兩種狀況: 將父對象的成員動態添加到子對象的原型中, 這樣就不會丟失了 for (var key in father){ Son.prototype[key] = father[key]; }; /**混合式繼承封裝 @param method:子對象的構造函數 @param father:要繼承的父對象 */ function extendMehtd ( method,father ) { for (var key in father){ method.prototype[key] = father[key]; } }; 4. 構造函數實現繼承 // 經過更改this的指向來實現 function Person(name, age) { this.name = name || 'hello'; this.age = age || 200; }; function Stu(sex, name, age) { this.sex = sex; // 調用Person構造函數,修改Person中的this指向爲當前Student這個構造函數中new建立的對象 // 繼承Person中默認的初始化屬性 Person.call(this, name, age); }; var s = new Stu('male'); console.log(s); // age: 200,name: "hello",sex: "male"
js中, 每個實例對象都存在一個__proto__屬性指向了本身的原型prototype; 可是原型自己也是一個對象,
也有本身的__proto__屬性,指向本身的原型,以此類推就造成一個鏈式結構,稱之爲原型鏈...對象訪問原型鏈中成員規則:就近原則
先看對象本身有沒有,有則訪問,沒有則看原型有沒有,有則訪問,沒有則看原型的原型有沒有,以此類推...直到原型鏈的終點(null);
若是尚未 : 若是是訪問屬性:則返回undefined 若是訪問的是方法:則會報錯 xxxx is not a functioncode
以數組對象爲例:對象
以上即是JS中完整的原型鏈圖解了...
1.靜態成員和實例成員 靜態成員: 函數對象持有的成員(屬性,方法) 實例成員: 構造函數實例化出來的對象持有的成員 2.instanceof 關鍵字 語法: 對象 instanceof 構造函數 做用: 用來檢測右邊函數的原型 是否 在左邊對象的原型鏈中(true/false) 如: Object instanceof Object // true 3.Object.prototype(對象原型) --全部對象的原型鏈中都會指向它;因此全部的對象均可以訪問Object.prototype原型中的成員 經常使用幾個成員: 1.hasOwnProperty(): 檢查對象是否包含某個成員; 條件: 本身的成員 2.isPrototypeOf(): 檢查(左邊)一個對象是否是(右邊)另外一個對象的原型 3.propertyIsEnumerable(): 檢查對象是否能夠枚舉某個屬性 條件: (1)是本身的成員 (2)能夠被for-in循環遍歷 (本身的和原型的) 4.Function.prototype(函數對象Function的原型) --全部的函數對象原型都會指向Function構造函數的原型,全部的函數對象均可以訪問Function.prototype中的成員 經常使用的一些成員: 1. name:獲取函數名 (比較雞肋) 2.caller:獲取調用本函數的引用;經過console.log(fn.caller)能夠知道本身在哪一個地方被人調用(全局調用函數,這裏的caller指向null) 3.length:獲取函數形參的數量; fun.length 能夠知道函數設置的形參個數 4.arguments:獲取函數全部的實參; 能夠理解爲函數內部一個隱藏的形參,做用是獲取函數全部的實參,與形參一一對應... arguments對象的兩個經常使用屬性: 1.callee:指向函數自身, 應用於匿名函數的遞歸調用... arguments.callee === fn //true 2. length: 實參的個數 arguments是一個僞數組... 5.給內置的構造函數原型添加自定義成員 當內置構造函數自帶的方法不夠用,沒法實現需求時,咱們就須要給添加自定義方法;直接添加可能會出現多我的員操做出現相同的方法名,致使被覆蓋掉了 因此須要採用安全的方法添加來避免覆蓋... 使用替換原型繼承(自定義構造函數,將原型指向內置對象) // 經過構造函數的方式來添加; function NewArr(name) { this.name = name; }; NewArr.prototype = []; // 修改成一個空數組對象;此時NewArr的原型擁有數組對象全部的方法 NewArr.prototype.hello = { name: 'hello world', }; NewArr.prototype.min = function () { var min = Infinity; for(var i=0; i< this.length; i++) { if (this[i] < min) { min = this[i]; } }; return min; }; // 建立一個新對象 var arr1 = new NewArr('huhua'); var arr3 = new NewArr(); arr3.push(1,2,3,-1); console.log(arr3.min()); console.log(arr1); console.log(arr1.__proto__); console.log(NewArr); console.log(NewArr.prototype); console.log(NewArr.__proto__.constructor); var arr2 = [12,241,21]; console.log(arr2.min()); // 不能訪問 Array.prototype:對象類型賦值的時候拷貝的是地址,修改了NewArr的原型以後,Array.prototype也會修改 []: 因爲空數組的原型會指向Array.prototype,根據原型鏈中成員訪問規則,NewArr實例對象能夠訪問數組成員的成員 而且,修改MyArr的原型對象,本質上是修改這個空數組,不會對Array.protpotype形成影響