濰坊網站設計—JavaScript里的類和繼承

2018.04.16 濰坊網站設計,JavaScript

54


  1、對象(Object)

 

  ECMA-262對對象的定義是:無序屬性的集合,其屬性可以包含基本值、對象或者函數。

 

  直觀點描述,就是由多個鍵值對組成的散列表。

 

  JS創建對象的方法和其它語言大同小異:

 

  //通過構造函數創建

 

  var zhangsan=new Object();

 

  zhangsan.name="張三";

 

  zhangsan.age=20;

 

  zhangsan.sayHi=function(){

 

  alert("Hi,I'm"+this.name);

 

  };

 

  //通過對象字面量創建

 

  var lisi={

 

  name:"李四",

 

  age:21,

 

  sayHi:function(){

 

  alert("Hi,I'm"+this.name);

 

  }

 

  };

 

  當需要大量創建相同結構的對象時,可以使用對象工廠(Object Factory):

 

  //對象工廠

 

  function createPerson(name,age){

 

  return{

 

  name:name,

 

  age:age,

 

  sayHi:function(){

 

  alert("Hi,I'm"+this.name);

 

  }

 

  };

 

  }

 

  var zhangsan=createPerson("張三",20);

 

  var lisi=createPerson("李四",21);

 

  但通過這種方式創建出來的實例,不能解決類型識別問題,只知道它是一個對象,但具體什么?無法判斷:

 

  zhangsan instanceof?

 

  lisi.constructor=?

 

  這時,“類”就登場了。



濰坊網站設計

 

  2、類(Class)

 

  2.1、構造函數模式

 

  事實上,JS中每個函數(function)本身就是一個構造函數(constructor),就是一個類:

 

  //構造函數模式

 

  function Person(name,age){

 

  this.name=name;

 

  this.age=age;

 

  this.sayHi=function(){

 

  alert("Hi,I'm"+this.name);

 

  };

 

  }

 

  var zhangsan=new Person("張三",20);

 

  var lisi=new Person("李四",21);

 

  alert(zhangsan instanceof Person);//true

 

  alert(lisi.constructor===Person);//true

 

  這里面其實有個問題:

 

  alert(zhangsan.sayHi===lisi.sayHi);//false

 

  多個實例中的同名方法并不相等,也就是說存在多個副本。而這些行為是相同的,應該指向同一個引用才對。

 

  為了解決這個問題,JS為每個函數分配了一個prototype(原型)屬性,該屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。



濰坊網站設計

 

  2.2、原型模式

 

  原型(Prototype):指向一個對象,作為所有實例的基引用(base reference)。

 

  //構造函數+原型組合模式

 

  function Person(name,age){

 

  this.name=name;

 

  this.age=age;

 

  }

 

  Person.prototype.sayHi=function(){

 

  alert("Hi,I'm"+this.name);

 

  };

 

  var zhangsan=new Person("張三",20);

 

  var lisi=new Person("李四",21);

 

  alert(zhangsan.sayHi===lisi.sayHi);//true

 

  在Person中,sayHi是原型成員(prototype),name和age是特權成員(privileged),它們都是公共成員(public)。

 

  注:“特權”是道格拉斯提出的名詞。道格拉斯·克羅克福德(Douglas Crockford),Web界人稱道爺,JSON創立者,《Java語言精粹》作者,JSLint、JSMin、ADsafe開發者。

 

  類的原型帶有一個constructor屬性,指向該類的構造函數(如果重新分配了原型指針,需要手動添加constructor屬性);類的實例上會自動生成一個屬性指向該類原型(在Chrome上可以通過“__proto__”訪問到該對象,而IE上該屬性則是不可見的)。

 

  Person、Person的原型、Person的實例間的關系如下:

 

  需要注意的是,原型成員保存引用類型值時需謹慎:

 

  Person.prototype.friends=[];

 

  zhangsan.friends.push("王五");

 

  alert(lisi.friends);//["王五"]

 

  張三的基友莫名其妙就變成李四的基友了,所以friends應該添加為特權成員,而不是原型成員。

 

  2.3、類的結構

 

  綜上所述,JS中的類的結構大致如下:

 

  類由構造函數和原型組成

 

  構造函數中可以聲明私有成員和添加特權成員

 

  原型中可以添加原型成員

 

  私有成員可以被特權成員訪問而對原型成員不可見

 

  特權成員和原型成員都是公共成員

 

  3、繼承(Inherit)

 

  在JS中繼承是如何實現的呢?

 

  3.1、拷貝繼承

 

  最簡單直接的方式莫過于屬性拷貝:

 

  //拷貝繼承

 

  function extend(destination,source){

 

  for(var property in source){

 

  destination[property]=source[property];

 

  }

 

  }

 

  extend(SubClass.prototype,SuperClass.prototype);

 

  這種方式雖然實現了原型屬性的繼承,但有一個非常明顯的缺陷:子類實例無法通過父類的instanceof驗證,換句話說,子類的實例不是父類的實例。

 

  3.2、原型繼承

 

  在Chrome的控制臺中查看HTMLElement的原型,大致如下:

 

  可以清晰看到,HTMLElement的原型是Element的實例,而Element的原型又是Node的實例,從而形成了一條原型鏈(Prototype-chain),JS的原生對象就是通過原型鏈來實現繼承。

 

  這里順道說下解釋器對實例屬性的查找過程:

 

  在特權屬性中查找

 

  特權屬性中沒找到,再到原型屬性中查找

 

  原型屬性中沒找到,再到原型的原型屬性中查找

 

  直到根原型還沒找到,返回undefined

 

  這就說明為什么我們自定義的類明明沒有聲明toString()方法,但仍然可以訪問到,因為所有對象都繼承自Object。

 

  因此,我們也可以通過原型鏈來實現繼承:

 

  //原型鏈繼承

 

  function User(name,age,password){

 

  //繼承特權成員

 

  Person.call(this,name,age);

 

  this.password=password;

 

  }

 

  //繼承原型

 

  User.prototype=new Person();

 

  //修改了原型指針,需重新設置constructor屬性



濰坊網站設計公司

 

  User.prototype.constructor=User;

 

  var zhangsan=new User("張三",20,"123456");

 

  zhangsan.sayHi();//Hi,I'm張三

 

  運行正常,貌似沒什么問題,但其實里面還是有些坑:

 

  父類的構造函數被執行了2次:繼承特權成員時1次,繼承原型時又1次。

 

  父類初始化兩次,這有時會導致一些問題,舉個例子,父類構造函數中有個alert,那么創建子類實例時,會發現有兩次彈框。

 

  不僅如此,還導致了下面的問題。從控制臺中查看子類的實例,結構如下:

 

  可以看到子類的原型中也包含了父類的特權成員(直接創建了一個父類實例,當然會有特權成員),只不過因為解釋器的屬性查找機制,被子類的特權成員所覆蓋,只要子類的特權成員被刪除,原型中相應的成員就會暴露出來:

 

  delete zhangsan.name;

 

  alert(zhangsan.name);//此時訪問到的就是原型中的name

 

  那怎么辦呢?對此道爺提供了一個很實用的解決方案——原型式寄生組合繼承。

 

  3.3、原型式寄生組合繼承

 

  我們的目的是子類原型只繼承父類的原型,而不要特權成員,原理其實很簡單:創建一個臨時的類,讓其原型指向父類原型,然后將子類原型指向該臨時類的實例即可。實現如下:

 

  function inheritPrototype(subClass,superClass){

 

  function Temp(){}

 

  Temp.prototype=superClass.prototype;

 

  subClass.prototype=new Temp();

 

  subClass.prototype.constructor=subClass;

 

  }

 

  inheritPrototype(User,Person);  

 

  轉載請注明:濰坊網站設計http://www.144610.tw/newsshow/214.html


關鍵詞

最新案例

聯系電話 400-6065-301

留言

(-^O^-)MG魔术兔_官方版 安徽宣城乐乐麻将血战到底 福建快三是不是全省销售 中国福利彩票怎么查看 微乐单机黑龙江大庆麻将 九乐棋牌下载安装苹果 注册送27元的大满贯 云南快乐十分前三遗漏查询 江西快3 开奖结果今天 休闲挂机游戏 河南麻将玩法介绍 电玩捕手机捕鱼 内蒙古十一选五基本走势图 双色球2020开奖结果 泳坛夺金组选24 金沙棋牌害人 卡五星微信群2元买马群