封裝一個函數 , 用來建立對象並給對象中特定的屬性添加值 , 優勢是能夠循環調用 , 缺點是每個建立出來的對象都是獨立的 , 不能肯定它是哪一個類型的對象 ( 或者說是想要將哪一個對象做爲模板進行建立 )瀏覽器
function func(name, age) { var o = { "name" : name, "age" : age }; return o; } var person = func("oswald", 24); var person2 = func("oswald", 24); // {name: "oswald", age: 24} console.log(person === person2); // false console.log(person.__proto__ === Object.prototype); // true console.log(person2.__proto__ === Object.prototype); // true // 每一個對象都是獨立的而且指向 Object.prototype 的 , 不能辨別基於哪一個對象爲原型
構造函數模式就是經過構造函數來建立自定義類型的對象 , 建立出來的對象都指向了同一個構造函數的 prototype , 解決了工廠模式中不能識別對象模板的問題函數
function Person(name, age) { this.name = name; this.age = age; this.sayName = function(){ console.log(this.name); }; /* 沒有顯式的建立對象 用 this 代替新對象 沒有 return 語句 */ } var person1 = new Person("oswald", 24); console.log(person1); // {name: "oswald", age: 24} console.log(person1.__proto__ === Person.prototype); // true // person1 指向了 構造函數 func.prototype console.log(person1 instanceof Person); // true // person1 對象是構造函數 Person ( 也能夠叫作 Person 類型 ) 的實例 console.log(person1 instanceof Object); // true // person1 對象也是 Object 類型的實例
可是 , 上面的構造函數模式也不是沒有缺點的 : 單純的構造函數模式建立對象的時候 , 對象中每一個方法都會在實例對象上從新建立一次 , 也就是說每次都會建立一個看起來相同可是徹底不是一個"方法"的方法oop
function Person(name, age) { this.name = name; this.age = age; this.sayName = function(){ console.log(this.name); }; } var person1 = new Person("oswald", 24); var person2 = new Person("oswald", 24); console.log(person1.sayName = person2.sayName); // false // 每次建立對象的時候 , 都會生成一個功能同樣可是是不一樣 Function 實例的方法
function Person(){ Person.prototype.name = "oswald"; Person.prototype.age = 24; Person.prototype.sayName = function(){ console.log(this.name); }; } var person1 = new Person(); console.log(person1); // {} 獲得一個空對象 , 由於建立的屬性和方法都在實例的原型對象上 console.log(Person.prototype); // {name: "oswald", age: 24, sayName: f(...)} console.log(person1.name); // oswald 獲得一個名字 , 由於實例對象能夠從它的原型對象上查找屬性和方法 var person2 = new Person(); console.log(person2.sayName === person1.sayName); // true // person1 和 person2 訪問的 sayName 方法都是原型對象上的方法 , 不是它們自身的 , 這樣就解決了構造函數模式屢次建立方法實例的缺點
若是當前實例對象中已經有了想要查找的屬性和方法 , 會直接使用實例對象的屬性和方法 , 若是沒有才去原型對象中查找this
function Person(){ Person.prototype.name = "oswald"; Person.prototype.age = 24; } var person1 = new Person(); console.log(person1.name); // oswald person1.name = "yin"; console.log(person1.name); // yin
function Person(){ Person.prototype.name = "oswald"; Person.prototype.age = 24; } var person1 = new Person(); console.log(person1.__proto__ === Person.prototype); // true console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
function Person(){ Person.prototype.name = "oswald"; Person.prototype.age = 24; } var person1 = new Person(); console.log(person1.__proto__ === Person.prototype); // true
function Person(){ } var person1 = new Person(); person1.prototype = { name: "yin", age: 23 } console.log(person1.name); // undefined
使用繼承自 Object 對象的 hasOwnProperty( ) 方法能夠檢車要查找的屬性或方法是否來自實例對象而不是實例對象的原型對象prototype
function Person(){ Person.prototype.name = "oswald"; Person.prototype.age = 24; } var person1 = new Person(); console.log(person1.name); // oswald console.log(person1.hasOwnProperty("name")); // false 當前查找到的 name 屬性來自原型對象 person1.name = "yin"; console.log(person1.name); // yin console.log(person1.hasOwnProperty("name")); // true 當前查找到的 name 屬性來自實例對象
function Person(){ Person.prototype.name = "oswald"; Person.prototype.age = 24; } var person1 = new Person(); person1.color = "red"; for( poop in person1 ){ console.log(poop); // color, name, age }
function Person(){ Person.prototype.name = "oswald"; Person.prototype.age = 24; } var person1 = new Person(); person1.color = "red"; var p1keys = Object.key(person1); console.log(p1keys); // color
function Person(){ Person.prototype.name = "oswald"; Person.prototype.age = 24; } var person1 = new Person(); person1.color = "red"; var p1keys = Object.getOwnPropertyNames(person1); console.log(p1keys); // color var Ppkeys = Object.getOwnPropertyNames(Person.prototype); console.log(Ppkeys); // constructor、name、age
原型中的全部屬性和方法都是共享的 , 若是有多個實例 , 經過其中一個實例改變原型對象中的屬性和方法 , 其餘實例訪問的屬性會跟着改變code
將對象的私有屬性和方法經過構造函數模式建立 , 將對象的公共屬性和方法經過原型模式建立對象
function Person(name,age){ /* 私有屬性 */ this.name = name; this.age = age; } Person.prototype.sayName = function(){ /* 公共方法 */ console.log(this.name); } var person1 = new Person("oswald", 24); person1.sayName(); // oswald
原型鏈就是鏈接實例對象和原型對象的連接繼承
/* 通常函數的原型鏈 */ function func(){ console.log(123); } console.log(func.__proto__); // Function.prototype // func 函數是 Function 構造函數的實例對象 console.log(func.__proto__.__proto__); // Object.prototype // func 函數的原型對象是 Object 構造函數的實例對象 console.log(func.__proto__.__proto__.proto__); // null // 這裏就是原型鏈的頭了 , 全部原型鏈查到 Object.prototype 再往上就會返回 null /* 通常構造函數的原型鏈 */ function Obj(){ Func.prototype.name = "oswald"; } var obj = new Obj(); console.log(obj); // {} console.log(obj.name); // oswald // obj 對象經過原型鏈查找到了 name 屬性 console.log(Obj.__proto__); // Obj.prototype // obj 對象是 Obj 構造函數的實例對象 console.log(Obj.__proto__.__proto__); // Object.prototype // obj 對象的原型對象是 Object 構造函數的實例 console.log(Obj.__proto__.__proto__.__proto__); // null // 到頭了
原型鏈繼承的原理就是實例對象能夠訪問原型對象的屬性和方法 , 並經過原型鏈向上查找ip
function Oswald(){ this.color = "red"; Oswald.prototype.sayName = function(){ console.log(this.name) }; } var oswald = new Oswald(); function Yin(name, age){ this.name = name; this.age = age; Yin.prototype.sayAge = function(){ console.log(this.age); } } Yin.prototype = oswald; /* 原型鏈繼承的核心 , 把父類型 ( Oswald ) 的實例對象 ( oswald ) 設置爲子類型 ( Yin ) 的原型屬性 ( Yin.prototype ) */ var yin = new Yin("oswald", 24); // 必需要先繼承再建立實例 , 不然會出現 Yin.prototype != yin.__proto__ 的狀況 yin.sayName(); // oswald 繼承了 Oswald 類型原型上的方法 yin.sayAge(); // 24 繼承了 Yin 類型原型上的方法
function Oswald(color){ this.color = color; this.sayName = function(){ console.log(this.name) }; } function Yin(name, age, color){ Oswald.call(this, color); // 在 Yin 構造函數建立的新對象中調用 Oswald 函數 this.name = name; this.age = age; } var yin = new Yin("yin", 24, "red"); yin.sayName(); // yin
可是借用構造函數繼承只可以繼承父類型本身的屬性和方法 , 不能繼承原型鏈上 , 這個時候咱們能夠使用原型鏈和借用構造函數的組合式繼承 , 可是這個方法會調用兩次父類型構造函數原型鏈
function Super(color){ this.color = color; // 本身的屬性 Super.prototype.sayName = function(){ console.log(this.name) }; // 原型鏈上的方法 } function Sub(name, age, color){ Super.call(this, color); // 第二次調用 Super , 被當作普通函數調用 // 繼承 Super 構造函數本身的屬性和方法 this.name = name; this.age = age; } Sub.prototype = new Super(); // 第一次調用 Super , 被當作構造函數調用 // 繼承 Super 原型鏈上的屬性和方法 var yin = new Sub("yin", 24, "red"); yin.sayName(); // yin
ES 5 中使用 Object.create( o ) 方法規範了原型式繼承 , 這個方法會返回一個新對象 , 新對象的原型對象指向傳入的參數對象 o
function obj(o){ // Object.create( ) 方法的原理 function F(){}; F.prototype = o; return new F(); } var person = { name: "oswald", color: ["red"] } var person1 = obj(person); var person2 = Object.create(person); console.log(person1 === person2); // false console.log(person1.__proto__ === person2.__proto__); // true
目前最優的繼承模式
function Super(color){ this.color = color; Super.prototype.sayName = function(){ console.log(this.name); } } function Sub(name, color){ Super.call(this, color); // 借調 Super 構造函數繼承實例屬性 this.name = name; } var F = Object.create(Super.prototype); F.constructor = Sub; Sub.prototype = F; var yin = new Sub("oswald","red"); yin.sayName(); // oswald