1.建立對象
1) 工廠模式
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
var p1 = createPerson("terry",11,"boss");
var p2 = createPerson("larry",12,"daBoss");app
工廠模式的問題
var t1 = typeOf p1; //object 沒法對象識別,即全部對象都是Object類型
2) 構造函數模式
js中能夠自定義構造函數,從而自定義對象類型的屬性和方法,構造函數自己也是函數,只不過能夠用來建立對象函數
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var p1 = new Person("terry",11,"boss");
var p2 = new Person("larry",12,"daBoss");
使用new操做符調用構造函數建立對象實際上經歷了以下幾個步驟
1) 建立一個新對象
2) 將構造函數的做用域賦給新對象(this指向這個新對象)
3) 執行構造函數中的代碼
4) 返回新對象。
這種建立對象的方法能夠將實例標識爲一種特定類型(例如Person類型)。
var t1 = typeOf p1; //t1爲Personthis
1.構造函數當作函數
Person("larry",12,"daBoss")
當在全局做用域中調用一個函數時,this老是指向Global對象(window對象)。
2.構造函數的問題
每一個方法都須要在每一個實例上從新建立一遍,可是毫無必要。
能夠在全局範圍中聲明一個函數,而後將引用傳遞給對象中的函數屬性。可是這樣作會致使全局函數過多,體現不了對象的封裝性
console.log(p1.sayName == p2.sayName); //falseprototype
3) 原型模式
每一個函數都有一個屬性:prototype(原型屬性),這個屬性是一個指針,指向一個對象,該對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。
function Person(){指針
}
Person.prototype.name = "tom";
Person.prototype.age = 22;
Person.prototype.job="boss";
Person.prototype.sayName = function(){
alert(this.name);
}
var p1 = new Person();
p1.name = "terry";對象
var p2 = new Person();
p2.name = "larry";
建立了自定義的構造函數以後,其原型對象默認會取得constructor屬性;當調用構造函數建立一個新實例後,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象。(指向的是原型對象而不是構造函數)繼承
1.屬性的訪問
每當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具備給定名字的屬性。
1) 首先從對象實例自己開始查找
2) 若是不在對象實例中,則繼續搜索指針指向的原型對象。
2.刪除實例屬性
當爲對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性。經過delete操做符能夠徹底刪除實例屬性。
3.檢測屬性是否存在於實例中
hasOwnProperty(p); 判斷p指定的屬性是否存在於實例中,若是存在返回true
console.log(p1.hasOwnProperty("name")); //false 存在於原型中而不是實例對象中
4.原型與in操做符
1) 在for-in 能夠訪問存在於實例中的屬性,以及原型中的屬性
2) 單獨使用
a in b; 經過b對象能夠訪問到a屬性的時候返回true,不管該對象在實例中仍是在原型中
console.log("name" in p1); //true
判斷一個屬性是否在原型
function hasPrototypeProperty(obj,name){
//不在實例中可是能夠訪問到的屬性屬於原型屬性
return !obj.hasOwnProperty(name) && (name in obj);
}
5.原生對象的原型
經過原生對象的原型,不只能夠取得全部默認方法的調用,並且能夠定義新方法。能夠向修改自定義對象的原型同樣修改原生對象的原型,能夠隨時添加方法。
String.prototype.startsWith = function(text){
return this.indexOf(text) == 0;
}
var msg = "Hello world";
alert(msg.startsWith("Hello")); //true
6.原型對象的問題
全部實例在默認狀況下都將取得相同的屬性值,這種共享對於函數來講很是合適,可是包含引用數據類型的值就不太好
Person.prototype = {
name : "briup",
friends : ["larry","terry"]
}
var p1 = new Person();
var p2 = new Person();
p1.name = "terry";
p1.friends.push("tom");ip
p1.friends; //["larry","terry","tom"]
p2.friends; //["larry","terry","tom"]原型鏈
7.更簡單的原型語法
將原型對象設置爲等於一個對象字面量形式建立的新對象。實例對象使用效果相同,可是原型中的constructor屬性再也不指向Person,由於每建立一個對象,就會同時建立它的 prototype對象,這個對象也自動得到constructor屬性。這裏咱們重寫了prototype對象所以該原型中constructor屬性就變成了新對象的constructor屬性(Object)
p1.constructor.prototype.constructor //Object
function Person(){
}
Person.property = {
//constructor: Person, 若是constructor比較重要,能夠指定它的值,Enumerable ,true,默認爲false
name:"tom",
age :22,
job :"boss",
sayName:function(){
alert(this.name);
}
}
//定義constructor屬性,不可遍歷
Object.defineProperty(Person.prototype,"constructor",{
enumerable : false,
value : Person
});
4) 組合使用構造函數模式和原型模式
構造函數用於定義實例屬性,原型模式用於定義方法和共享屬性。這種模式是目前在ECMAScript中使用最普遍,認同度最高的一種建立自定義類型的方法。
function Person(name,age){
this.name = name,
this.age = age,
this.friends = []
}
Person.prototype = {
constructor : Person,
sayName:function(){
alert(this.name);
}
}
var p1 = new Person("terry",11);
var p2 = new Person("larry",12);
p1.friends.push("tom");
p2.friends.push("jacky");
console.log(p1);
console.log(p2);作用域
2.繼承
1) 原型鏈
每一個構造函數都有一個原型對象,原型對象中都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。當原型對象等於另一個類型的實例即繼承。調用某個方法或者屬性的步驟
a.搜索實例
b.搜索原型
c.搜索父類原型
//定義父類類型
function Animal(){
this.name = "animal"
}
Animal.prototype = {
sayName : function(){
alert(this.name);
}
}
//定義子類類型
function Dog(){
this.color = "灰色"
}
//經過將子對象的原型對象指向父對象的一個實例來完成繼承
Dog.prototype = new Animal();
//子對象的方法實際上是定義在了父類對象的實例上。
Dog.prototype.sayColor = function(){
alert(this.color);
}
var dog = new Dog();
console.log(dog);
dog.sayColor();
dog.sayName();
1.默認原型
全部函數默認原型都是Object的實例,默認原型中都會包含一個內部指針,指向Object.prototype.
2.肯定原型和實例的關係
1) 經過使用instanceof
instance instanceof Object //true
instance instanceof SuperType //true
instance instanceof SubType //true
2) 經過使用isPrototypeOf()
只要是原型鏈中出現過的原型,均可以說是該原型鏈所派生的實例的原型
Object.prototype.isPrototypeOf(instance) //true
SuperType.prototype.isPrototypeOf(instance) //true
SubType.prototype.isPrototypeOf(instance) //true
3.謹慎定義方法
子類型覆蓋超類型中的某個方法,或者是須要添加超類中不存在的方法,都須要將給原型添加方法的代碼放在繼承以後(即替換原型的語句以後)
4.原型鏈問題
1)經過原型來實現繼承時,原型實際上會變成另外一個類型的實例,原來的實例屬性也就變成了如今的原型屬性
2)在建立子類型的實例時,不能向超類型的構造函數傳遞參數。
所以實踐中不多會單獨使用原型鏈
2) 借用構造函數
也稱 "僞造對象" 或 "經典繼承",在子類型構造函數的內部調用超類型構造函數。函數不過是在特定環境中執行代碼的對象,所以經過apply(),call()方法能夠在(未來)新建對象上執行構造函數,即 在子類型對象上執行父類型函數中定義的全部對象初始化的代碼。結果每一個子類實例中都具備了父類型中的屬性以及方法
function Animal(name){
this.name = name;
this.colors = ["red","gray"];
}
function Dog(name){
//繼承了Animal
Animal.call(this,name);
this.color = "gray";
}
Animal.prototype.sayName = function(){
alert(this.name);
}
var dog = new Dog();
dog.colors.push("hhh");
console.log(dog);
var animal = new Animal();
console.log(animal);
//若是將函數定義在構造函數中,函數複用無從談起
dog.sayName();
//在超類型的原型中定義的方法,對於子類型而言是沒法看到的
3) 組合函數
也稱"僞經典繼承",將原型鏈和借用構造函數的技術組合在一塊兒。原理是:使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數實現對實例屬性的繼承。
function Animal(name){
this.name = name;
this.colors = ["red","gray"];
}
function Dog(name){
//繼承了Animal(屬性)
Animal.call(this,name);
this.color = "gray";
}
Animal.prototype.sayName = function(){
alert(this.name);
}
//繼承方法
Dog.prototype = new Animal();
Dog.prototype.constructor = Animal;
var dog = new Dog(); dog.colors.push("hhh"); console.log(dog); var animal = new Animal(); console.log(animal); dog.sayName(); //能夠調用