JS基礎(chǔ)整理(2)—定義類和繼承的幾種方法

1. 創(chuàng)建類

1.1. 簡單的類

類指一組對象從同一個原型對象繼承屬性肢础,原型對象是類的核心特征。
定義一個原型對象,然后用Object.create()創(chuàng)建一個繼承它的對象,我們就定義了一個JavaScript類快耿。

//工廠函數(shù),用于創(chuàng)建Range對象
function range(from, to) {
  //使用Object.create()創(chuàng)建一個對象芳绩,繼承原型對象
  let r = Object.create(range.methods);
  r.from = from;
  r.to = to;
  return r;
}
// 定義一個原型對象
range.methods = {
  includes(x){
    //通過this引用調(diào)用from和to的對象
    return this.from<=x && x<=this.to;
  },
  // 生成器函數(shù):讓這個類的實例可迭代
  *[Symbol.iterator](){
    for(let x = Math.ceil(this.from); x<=this.to; x++){
      yield x;
    }
  },
  toString(){return "("+this.from +"..."+this.to+")";}
}

let r = range(1,10); // 創(chuàng)建一個對象
console.log(r.includes(2));
console.log(r.includes(11));
console.log(r.toString());
console.log([...r]);

1.2. 使用構(gòu)造函數(shù)的類

上面的方法定義了JavaScript類掀亥,但是它沒有定義構(gòu)造函數(shù)。構(gòu)造函數(shù)是專門用于初始化新對象的函數(shù)妥色,使用new關(guān)鍵字調(diào)用構(gòu)造函數(shù)會自動創(chuàng)建新對象搪花。構(gòu)造函數(shù)調(diào)用的關(guān)鍵在于構(gòu)造函數(shù)的prototype屬性被用作新對象的原型。

只有函數(shù)對象才有prototype屬性嘹害,同一個構(gòu)造函數(shù)創(chuàng)建的所有對象都繼承同一個對象撮竿。

在不支持的ES6 class關(guān)鍵字的JavaScript版本中,用以下的方法創(chuàng)建類笔呀。

// 構(gòu)造函數(shù)
function Range(from, to){
  this.from = from;
  this.to = to;
}
// 所有Range對象都繼承這個對象幢踏,prototype這個名字是強制的
Range.prototype={
  //不要使用箭頭函數(shù),因為箭頭函數(shù)沒有prototype屬性凿可,this是從定義它的上下文繼承的
  includes:function(x) {
    return this.from<=x && x<=this.to;
  },
  [Symbol.iterator]:function*() {
    for(let x = Math.ceil(this.from); x<=this.to; x++){
      yield x;
    }
  },
  toString:function(){return "("+this.from +"..."+this.to+")";}
}
//以new關(guān)鍵字調(diào)用構(gòu)造函數(shù)
let r = new Range(1,10);
console.log(r.includes(2));
console.log(r.includes(11));
console.log(r.toString());
console.log([...r]);

對比以上兩個例子惑折,有以下區(qū)別:

  • 工廠函數(shù)命名為range(),構(gòu)造函數(shù)命名為Range()枯跑;
  • 創(chuàng)建對象的時候惨驶,工廠函數(shù)使用raneg(),構(gòu)造函數(shù)使用new Range()

函數(shù)體內(nèi)有一個特殊表達式new.target用于判斷函數(shù)是否作為構(gòu)造函數(shù)敛助,如果new.target不是undefined粗卜,說明函數(shù)作為構(gòu)造函數(shù),會自動創(chuàng)建新對象

function F(){
  if(!new.target) return new F();
}

上面提到纳击,構(gòu)造函數(shù)調(diào)用的關(guān)鍵在于構(gòu)造函數(shù)的prototype屬性被用作新對象的原型续扔。所以,每個普通JavaScript函數(shù)自動擁有一個prototype屬性焕数,這個屬性有一個不可枚舉的constructor屬性纱昧。
constructor屬性的值就是該函數(shù)對象本身

let F = function (x) {this.x = x}
let p = F.prototype;
let c = p.constructor;
console.log("c === F: ", c === F); // true, F.prototype.constructor === F

注意,上面的Range的例子中堡赔,由于用自己定義的對象Range.prototype = {}重寫了預(yù)定義的Range.prototype對象识脆,所以Range的實例都沒有constructor屬性。

let o = new F();
console.log(o.constructor === F); // true
console.log(r.constructor === Range); // ?

上面r.constructor === Range返回的是false善已。
常用的方法是使用預(yù)定義的原型對象及其constructor屬性灼捂,然后通過以下方式添加方法:

Range.prototype.includes = function(x){}

1.3. 使用ES6的class

ES6引入class關(guān)鍵字,可以使用新語法創(chuàng)建類

class Range{
  //實際上定義的函數(shù)不叫constructor
  //class會定義一個新變量Range换团,并將這個特殊構(gòu)造函數(shù)的值賦給改變量
  constructor(from, to){
    this.from = from;
    this.to = to;
  }
  //methods
  //方法之間沒有逗號
  //不支持key:value形式
  includes(x){
    //通過this引用調(diào)用from和to的對象
    return this.from<=x && x<=this.to;
  }
  *[Symbol.iterator](){
    for(let x = Math.ceil(this.from); x<=this.to; x++){
      yield x;
    }
  }
  toString(){return "("+this.from +"..."+this.to+")";}
}

2. 繼承的幾種方法

// 父類
function Animal(name){
  this.name = name || "cat";
  this.sleep = function(){console.log(`${this.name} is sleeping.`);}
}
  1. 原型鏈繼承
function Cat(){}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

let cat = new Cat();
cat.sleep();
  1. 構(gòu)造繼承
function Cat2(name){
  //使用父類的構(gòu)造函數(shù)來增強子類實例, 復(fù)制父類的實例屬性給子類
  Animal.call(this);
  this.name = name;
}
//實例并不是父類的實例悉稠,只是子類的實例
//只能繼承父類的屬性和方法,不能繼承原型鏈的
//無法實現(xiàn)函數(shù)復(fù)用艘包,每個子類都有父類實例函數(shù)的副本的猛,影響性能
let c2 = new Cat2("cat2");
c2.sleep();
  1. 實例繼承
function Cat3(name){
  //為父類實例添加新特性,作為子類實例返回
  let instance = new Animal();
  instance.name = name||'Tom';
  return instance;
}
//實例是父類的實例想虎,不是子類的實例
let c3 = new Cat3();
c3.sleep();
  1. 拷貝繼承
function Cat4(name){
  let a = new Animal();
  //拷貝父類的屬性和方法
  //效率較低衰絮,內(nèi)存占用高
  //無法獲取父類不可枚舉的方法(不能使用for in訪問)
  for(let p in a){
    Cat4.prototype[p] = a[p];
  }
  this.name = name;
}

let c4 = new Cat4('cat4');
c4.sleep();
  1. 組合繼承
function Cat5(name){
  Animal.call(this);
  this.name = name ||'Tom';
}
//通過調(diào)用父類構(gòu)造,可以繼承實例屬性/方法磷醋,也可以繼承原型屬性/方法
//將父類實例作為子類原型猫牡,實現(xiàn)函數(shù)復(fù)用
//調(diào)用了兩次父類構(gòu)造函數(shù),生成了兩份實例
Cat5.prototype = new Animal();
Cat5.prototype.constructor = Cat;
//既是子類的實例邓线,也是父類的實例
let c5 = new Cat5('cat5');
c5.sleep();
  1. 寄生組合繼承
//調(diào)用兩次父類的構(gòu)造的時候淌友,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點
function Cat6(name){
  Animal.call(this);
  this.name = name || "Tom";
}

(function(){
  //通過寄生方式骇陈,砍掉父類的實例屬性
  let Super = function(){};
  Super.prototype = Animal.prototype;
  //將實例作為子類的原型
  Cat6.prototype = new Super();
})();
Cat6.prototype.constructor = Cat6;

let c6 = new Cat6('cat6');
c6.sleep();
  1. 基于ES6的class的繼承
class Span extends Range{
  constructor(start, length){
    if(length>0){
      super(start, start+length);
    }else{
      super(start+length, start);
    }
  }
}

最近在看高頻面試題震庭,經(jīng)常看到繼承的問題你雌,之后再來補充一下~~~~
還有類涉及對象和原型鏈的問題器联,可能也會總結(jié)一下

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末二汛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拨拓,更是在濱河造成了極大的恐慌肴颊,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渣磷,死亡現(xiàn)場離奇詭異婿着,居然都是意外死亡,警方通過查閱死者的電腦和手機醋界,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門竟宋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人形纺,你說我怎么就攤上這事丘侠。” “怎么了逐样?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵婉陷,是天一觀的道長。 經(jīng)常有香客問我官研,道長秽澳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任戏羽,我火速辦了婚禮担神,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘始花。我一直安慰自己妄讯,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布酷宵。 她就那樣靜靜地躺著亥贸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浇垦。 梳的紋絲不亂的頭發(fā)上炕置,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音男韧,去河邊找鬼朴摊。 笑死,一個胖子當著我的面吹牛此虑,可吹牛的內(nèi)容都是我干的甚纲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼朦前,長吁一口氣:“原來是場噩夢啊……” “哼介杆!你這毒婦竟也來了鹃操?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤春哨,失蹤者是張志新(化名)和其女友劉穎荆隘,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悲靴,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡臭胜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年莫其,在試婚紗的時候發(fā)現(xiàn)自己被綠了癞尚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡乱陡,死狀恐怖浇揩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情憨颠,我是刑警寧澤胳徽,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站爽彤,受9級特大地震影響养盗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜适篙,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一往核、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嚷节,春花似錦聂儒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至效斑,卻和暖如春非春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缓屠。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工税娜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人藏研。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓敬矩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蠢挡。 傳聞我的和親對象是個殘疾皇子弧岳,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容