js 面向對象的設計(基于原型鏈)

對象樊破。每個對象都有一個內部鏈接到另一個對象,稱為它的原型 prototype唆铐。該原型對象有自己的原型哲戚,等等,直到達到一個以null為原型的對象艾岂。根據(jù)定義顺少,null沒有原型,并且作為這個原型鏈 prototype chain中的最終鏈接王浴。

屬性類型

  1. 數(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
});
  1. 訪問器屬性
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)建對象

  1. 工廠模式
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);
  1. 構造函數(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關系如下:

EB2704EC-9242-4027-B33F-2B289E45A266.png

對象食店,原型,實例關系的參考
注意:當我們創(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ù)是屬性名
// 原理見下圖
EB411223-B4E6-4542-80F1-8C7BD0F8A32E.png
// 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
});
}
// 注意一下倆例子,如圖供嚎,實例的原型黄娘,是一個對象,有自己的屬性
43BF19B3-7244-4521-8CEC-BC5190A52C25.png

32514972-B563-4A0B-A29B-E1EE25704248.png
// 動態(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
// 原理見下圖
62C5E929-EB58-4A82-BE54-0D2191D8D44D.png
// 完整的原型鏈
93D05E75-F7D8-4E6C-A98F-4609D8B45684.png
// 確定原型和實例的關系
// 添加原型方法的代碼一定放在替換原型之后
// 繼承
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);//用這句話代替
// 替換的圖示如下:
C92D62CF-F471-4EBC-8647-0569129C6734.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市望忆,隨后出現(xiàn)的幾起案子罩阵,更是在濱河造成了極大的恐慌,老刑警劉巖启摄,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稿壁,死亡現(xiàn)場離奇詭異,居然都是意外死亡歉备,警方通過查閱死者的電腦和手機傅是,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蕾羊,“玉大人喧笔,你說我怎么就攤上這事」暝伲” “怎么了书闸?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長利凑。 經(jīng)常有香客問我浆劲,道長嫌术,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任牌借,我火速辦了婚禮度气,結果婚禮上,老公的妹妹穿的比我還像新娘膨报。我一直安慰自己蚯嫌,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布丙躏。 她就那樣靜靜地躺著择示,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晒旅。 梳的紋絲不亂的頭發(fā)上栅盲,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音废恋,去河邊找鬼谈秫。 笑死,一個胖子當著我的面吹牛鱼鼓,可吹牛的內容都是我干的拟烫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼迄本,長吁一口氣:“原來是場噩夢啊……” “哼硕淑!你這毒婦竟也來了?” 一聲冷哼從身側響起嘉赎,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤置媳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后公条,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拇囊,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年靶橱,在試婚紗的時候發(fā)現(xiàn)自己被綠了寥袭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡关霸,死狀恐怖传黄,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情谒拴,我是刑警寧澤尝江,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站英上,受9級特大地震影響炭序,放射性物質發(fā)生泄漏。R本人自食惡果不足惜苍日,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一惭聂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧相恃,春花似錦辜纲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至杀糯,卻和暖如春扫俺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背固翰。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工狼纬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骂际。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓疗琉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親歉铝。 傳聞我的和親對象是個殘疾皇子盈简,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內容