對象樊破。每個對象都有一個內部鏈接到另一個對象,稱為它的原型 prototype唆铐。該原型對象有自己的原型哲戚,等等,直到達到一個以null為原型的對象艾岂。根據(jù)定義顺少,null沒有原型,并且作為這個原型鏈 prototype chain中的最終鏈接王浴。
屬性類型
- 數(shù)據(jù)屬性
// 用于修改屬性的描述符
var person = {};
Object.defineProperty(person,"name",{
Configurable:false,// 允許通過delete刪除屬性脆炎,重新定義屬性?默認true
Enumerable:false,// 允許枚舉氓辣?默認true
writable:false,// 允許修改值秒裕?默認true
Value:"nicholas",// 值默認 undefined
});
person.name;//nicholas
person.name = "greg";
person.name;//nicholas
var person = {};
Object.defineProperty(person,"name",{
Configurable:false,
Value:"nicholas",// 值默認 undefined
});
// 拋出錯誤
Object.defineProperty(person,"name",{
Configurable:true,
Value:"nicholas",// 值默認 undefined
});
- 訪問器屬性
Configurable:false,// 允許通過delete刪除屬性,重新定義屬性钞啸?默認true
Enumerable:false,// 允許枚舉几蜻?默認true
get,// 讀 類型function 值默認 undefined
set,// 寫 類型function 值默認 undefined
var book = {
_year:2004,
edition:1
};
Object.defineProperty(book,"year",{
get: function(){
return this._year;
},
set: function(newValue){
this._year = newValue;
this.edition += 1;
}
});
book.year = 2005;
book.edition;//1
// 屬性前的 _ 是一個記號癞松,表示只能通過對象方法訪問的屬性
// 僅制定get,屬性不可寫,嘗試寫會被忽略
// defineProperty 出現(xiàn)之前入蛆,是這樣寫的如下:
var book = {
_year:2004,
edition:1
};
book.__defineGetter__("year",function(){
return this._year;
});
book.__defineSetter__("year",function(newValue){
this._year = newValue;
this.edition += 1;
}
});
book.year = 2005;
book.edition;//1
定義多個屬性
var book = {};
Object.defineProperty(book,{
_year:{
value:2004
},
edition:{
value:1
},
year:{
get: function(){
return this._year;
},
set: function(newValue){
this._year = newValue;
this.edition+=1;
}
}
});
//_year, edition 兩個數(shù)據(jù)屬性,數(shù)據(jù)屬性
// year 一個訪問屬性(訪問器)响蓉,
// 讀取屬性的特性
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
descriptor.value;// 2004
descriptor.configurable;// false
typeof descriptor.get;// undefined
// 訪問屬性的特性
var descriptor = Object.getOwnPropertyDescriptor(book,"year");
descriptor.value;// undefined
descriptor.configurable;// false
typeof descriptor.get;// function
創(chuàng)建對象
- 工廠模式
function createdPerson(name,age){
var person = new Object();
person.name = name;
persion.age = age;
return person;
}
var person1 = createdPerson("xiaoming",29);
var person2 = createdPerson("xiaohong",29);
- 構造函數(shù)
// 上面代碼改良如下
function Person(name, age){
this.name = name;
this.age = age;
}
var person1 = new Person("xiaoming",29);
var person2 = new Person("xiaohong",29);
// 通過構造函數(shù)創(chuàng)建的實例,constructor屬性保存指向Person
person1. constructor;//Person
person1 instance Object;//true
person1 instance Person;//true
// 通上哨毁,所有對象繼承自Object
// 構造函數(shù)的缺點
function Person(name, age){
this.name = name;
this.age = age;
this.say = function (){
alert(this.name);
}
}
var person1 = new Person("xiaoming",29);
var person2 = new Person("xiaohong",29);
// 這樣person1和person2會存在枫甲,兩個相同功能的function對象,造成資源的浪費
// 改進
function Person(name, age){
this.name = name;
this.age = age;
this.say = say;
}
function say(){
alert(this.name);
}
原型對象
// 上面的解決方案扼褪,導致新的問題:在全局作用域的函數(shù)缺只能被某個對象是用想幻,且若很多方法的話
// 需定義很多的全局函數(shù)。則毫無封裝话浇。脏毯。
// 原型模式:(原理,基于函數(shù)的prototype屬性幔崖,此屬性指向函數(shù)的原型對象)
//上面代碼改稱為
function Persion(){
}
Persion.prototype.name = "xiaoming";
Persion.prototype.age = 29;
Persion.prototype.say = function (){
alert(this.name);
};
var person1 = new Person();
person1.say();//xiaoming
// 這些屬性是所有實例共享的
Person,Persion.prototype,person1,person2關系如下:
對象食店,原型,實例關系的參考
注意:當我們創(chuàng)建一個函數(shù)赏寇,系統(tǒng)就會為這個函數(shù)自動分配一個prototype指針吉嫩,指向它的原型對象。并且可以發(fā)現(xiàn)嗅定,這個原型對象包含兩個部分(constructor 和 proto)其中constructor指向函數(shù)自身自娩。(這里形成了一個小閉環(huán)) 當我們將該函數(shù)作為模版創(chuàng)建實例(new方法)的時候,我們發(fā)現(xiàn)創(chuàng)建出的實例是一個與構造函數(shù)同名的object渠退,這個object是獨立的忙迁,他只包含了一個proto指針(實例沒有prototype,強行訪問則會輸出undefined)碎乃,這個指針指向上面提到的構造函數(shù)的prototype原型對象姊扔。 這時候我們發(fā)現(xiàn)三者形成了一個大"閉環(huán)"荠锭。之所以加上引號旱眯,因為構造函數(shù)和實例之間無法直接訪問晨川,需要通過proto指針間接讀取证九。
prototype:此屬性只有函數(shù)有
constructor:函數(shù)的原型對象的屬性,指向函數(shù)本身
prototype:函數(shù)的屬性共虑,指向函數(shù)的原型對象
proto:實例的屬性愧怜,指向函數(shù)的原型對象,構造函數(shù)和實例之間無法直接訪問妈拌。
person1.__proto__ == Person.prototype;// true
person1.__proto__.constructor == People;//true
// 查找對象是否存在原型關系
Person.prototype.isPrototypeof(person1);// true
Person.prototype.isPrototypeof(person2);// true
// 獲取Prototype
Object.getPrototypeof(person1) == Person.prototype;//true
Object.getPrototypeof(person1).name;//xiaoming
這種情況下,person1.name;實際是查詢的原型鏈上的name屬性拥坛,若查找.一個不存在的屬性蓬蝶,那么會遍歷原型鏈所有的屬性,很浪費性能
// 原型鏈
function Persion(){
}
Persion.prototype.name = "xiaoming";
Persion.prototype.age = 29;
Persion.prototype.say = function (){
alert(this.name);
};
var person1 = new Person();
person1.say();//xiaoming 來自原型
var person2 = new Person();
person2.name = "xiaohong";
person2.say();//xiaohong 來自實例
// 原型鏈的查找猜惋,先查自身的屬性丸氛,查不到依次往上查原型鏈,
// delete 操作符著摔,刪除實例的屬性
delete person2.name;
person2.say();//xiaoming 來自原型
// hasOwnProperty(); 檢測一個屬性存在實例還是原型缓窜,參數(shù)是屬性名
// 原理見下圖
// in操作符:無論存在于原型還是實例,存在即返回true
var person1 = new Person();
var person2 = new Person();
person1.hasOwnProperty("name");//false
"name" in person1;//true
person1.name = "xiaoming";
person1.hasOwnProperty("name");//true
"name" in person1;//true
// 應用
// 判斷是原型屬性
function hasPrototypeProperty(object , name){
return ! object.hasOwnProperty(name)&&(name in object);
}
//for in 可以獲取對象所有可枚舉的屬性[enumerable]谍咆,還可以使用Object.keys()方法
function Person(){
}
Person.prototype.name = "xiaoming";
Person.prototype.age = 29;
Person.prototype.say = function (){
alert(this.name);
}
// 獲取實例屬性
var keys = Object.keys(Person.prototype);// name,age,say
var p1 = new Person();
p1.name = "11";
p1.age = 22;
var keys = Object.keys(p1);// name age
// 獲取所有屬性無論是否可以枚舉
var keys = Object.getOwnPropertyNames(Person.prototype);// constructor,name,age,say
// 更簡單的原型語法
// 此方式會導致enumerable屬性被標記為true禾锤,原聲的enumerable默認事false不可枚舉
function Person(){
}
// 重寫原型
Person.prototype = {
constructor:Person,//將其設為Person保證通過該屬性獲取適當值
name:"nike",
age:29,
say:function (){
alert(this.name);
}
};
// 結合defineproperty函數(shù)
//最上面有defineProperty函數(shù)的講解,第一個參數(shù)實例摹察,第二個參數(shù)屬性名恩掷,第三個參數(shù)設置
// 這是數(shù)據(jù)屬性的設置
Object.defineProperty(Person.property,"constructor",{
enumerable:false,
value:Person
});
}
// 注意一下倆例子,如圖供嚎,實例的原型黄娘,是一個對象,有自己的屬性
// 動態(tài)原型模式(懶加載)
function Person(name ,age){
this.name = name;
this.age = age;
if(typed this.sayname != "function"){
Person.prototype.sayname = function (){
alert(this.name);
}
}
}
// 裝飾模式
// 創(chuàng)建一個具有額外方法的特殊數(shù)組
function SpecialArray(){
var values = new Array();
// 數(shù)組的push屬性(類型function),調用apply函數(shù)克滴,添加值
values.push.apple(values,arguments);//values :當前作用域 arguments :參數(shù)列表
// 添加方法
values.toPipedString = function(){
return this.join("|");
}
return values;
}
var colors = new SpecialArray("red","blue","green");
colors.toPipedString();//red|blue|green
// 穩(wěn)妥構造寸宏,創(chuàng)建私有屬性
function Person (name,age,job){
var o = new Object();
o.sayName = function(){
alert(name);
}
return o;
}
// 這種情況下,只有sayName才能訪問name
var friend = new Person("xiaoming",29,"ht");
friend.sayName();//xiaoming
原型鏈
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function (){
return this.property;
}
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
}
// 測試
var instance = new SubType();
instance.getSuperValue();// true
// 原理見下圖
// 完整的原型鏈
// 確定原型和實例的關系
// 添加原型方法的代碼一定放在替換原型之后
// 繼承
function SuperType(name){
this.name = name;
}
function SubType(){
SuperType.call(this,"Nicholas");
this.age = 23;
}
var instance = new SubType();
instance.name; // Nicholas
instance.age; // 23
// 組合繼承
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
alert(this.name)
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
// 繼承方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new SubType("Nicholas",29);
instance1.colors.push("black");//red,blue,green,black
instance1.sayAge;//Nicholas
instance1.sayName;//29
var instance2 = new SubType("Grey",27);
instance2.colors;//red,blue,green
instance2.sayAge;//Grey
instance2.sayName;//27
// 原型式繼承
//eg
function object(o){
function F(){ }
F.prototype = o;
return new F();
}
var person = {
name:"nick",
friends:["shelby","court"];
};
var anotherObj = object(person);
anotherObj.name = "greg";
anotherObj.friends.push("rob");
var yeObj = object(person);
yeObj.name = "linda";
yeObj.friends.push("bar");
person.friend;//"shelby","court", rob, bar
// 上面的思想 在 js 有個方法規(guī)范了
// 重寫為
var person = {
name:"nick",
friends:["shelby","court"];
};
var anotherObj = Object.create(person);
anotherObj.name = "greg";
anotherObj.friends.push("rob");
var yeObj = Object.create(person);
yeObj.name = "linda";
yeObj.friends.push("bar");
person.friend;//"shelby","court", rob, bar
// create 有兩個參數(shù)偿曙,第二個參數(shù)與Object.defineProperties()方法的第二個參數(shù)作用一致氮凝,覆蓋原型上的同名屬性
// eg
var person = {
name:"nick",
friends:["shelby","court"];
};
var anotherObj = Object.create(person,{
name:{
value:"greg"
}
});
anotherObj.name;//greg
// 寄生式繼承(和原型式緊密)
function object(o){
function F(){ }
F.prototype = o;
return new F();
}
function createrAnother(o){
var clone = object(o);
clone.sayHi = function (){
alert("hi");
}
return clone;
}
// 使用
var person = {
name:"nick",
friends:["shelby","court"];
};
var anotherPerson = createrAnother(person);
anotherPerson.sayHi();// hi
// 寄生組合式
// 先看一個組合繼承的例子
function SuperType(name){
this.name = name;
this.colors = ["red","green"];
}
SuperType.protoType.sayName = function (){
alert(this.name);
}
function SubType(name,age){
SuperType.Call(this,name);//第一次
this.age = age;
}
SubType.prototype = new SuperType();//第二次
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function (){
alert(this.age);
}
// 注意了 SuperType() 一共調用了兩次
// 為解決調用了兩次 SuperType() ,寄生組合應運而生
//上面代碼的這一部分
SubType.prototype = new SuperType();//第二次
SubType.prototype.constructor = SubType;
// 可以替換為
function inheritPrototype(SubType,SuperType){
var prototype = object(SubType, SuperType);// 創(chuàng)建
prototype.constructor = SubType;// 增強
SubType.prototype = prototype;// 制定
}
inheritPrototype(SubType, SuperType);//用這句話代替
// 替換的圖示如下: