原型、構(gòu)造函數(shù)與面向?qū)ο缶幊?/h1>

1. 復(fù)雜的原型鏈

原型是 JavaScript 向面向?qū)ο缶幊陶Z言進(jìn)化的產(chǎn)物徘键。

為什么要面向?qū)ο缶幊塘范裕繛榱舜a復(fù)用,為了抽象出類似的概念吹害,為了讓代碼更有邏輯螟凭,更符合我們本身的世界觀。

在我們的意識(shí)中它呀,其實(shí)世界的事物都是一個(gè)一個(gè)對(duì)象螺男。例如,男人和女人纵穿,都屬于人的一個(gè)子分支下隧,他們兩者都具有人的一些共同點(diǎn)特點(diǎn),或者說谓媒,男人和女人這兩個(gè)對(duì)象都“繼承”了“人”這個(gè)對(duì)象的一些特點(diǎn)淆院,而又有各自的不一樣屬性【涔撸或者說:人土辩,就是男人和女人的原型。

在 JavaScript 中抢野,Function ,Object 分別是函數(shù)拷淘,對(duì)象的原型,而其實(shí)函數(shù)也是一個(gè)特殊的對(duì)象指孤,因此函數(shù)原型創(chuàng)作的函數(shù)也是特殊的對(duì)象启涯。

var fun =function() {
  console.log('this is a function');
};

var obj = {
  string: 'obj',
};

console.log(Function.prototype.isPrototypeOf(fun));
console.log(Object.prototype.isPrototypeOf(fun));

console.log(Function.prototype.isPrototypeOf(obj));
console.log(Object.prototype.isPrototypeOf(obj));

console.log(Object.prototype.isPrototypeOf(Function));

// true, true, true, false, true

利用Object.prototype.isPrototypeOf(targetObj)方法,可以檢測(cè)Object測(cè)試對(duì)象是否在檢測(cè)目標(biāo)的原型鏈上恃轩,根據(jù)輸出可以看到结洼,Function 對(duì)象和 Object 對(duì)象在所有函數(shù)的原型鏈上,其中ObjectFunction的原型详恼,而Object是所有對(duì)象的原型补君,包括 Function對(duì)象。

我們可以使用一個(gè) ES5 的方法來獲取一個(gè)對(duì)象的原型:
Object.getPrototypeOf(obj)

var Person = function (name) {
  this.name = name;
};
var alice = new Person('alice');

console.log(Object.getPrototypeOf(alice));
console.log(Object.getPrototypeOf(alice) === Person.prototype);
// Person.prototype, true

可以看到昧互,Person 原型挽铁,生成了一個(gè)實(shí)例 alice伟桅,因此Person對(duì)象 是alice的原型。

類似的叽掘,Person也應(yīng)該有原型楣铁,所謂原型,就是生成它的那個(gè)角色更扁。上面實(shí)例中盖腕,Person 是一個(gè)函數(shù),那它的原型是什么呢浓镜?我們用代碼探究:

console.log(Object.getPrototypeOf(Person));
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// ? () { [native code] } , true

由于創(chuàng)造函數(shù)的函數(shù)(Function.prototype)是 JavaScript 語言的源代碼溃列,因此不存在它是什么,但是我們通過第二個(gè)判斷得知(三個(gè)等號(hào))膛薛,生產(chǎn)函數(shù)的函數(shù)就是Function.prototype听隐。

Function.prototype本身是一個(gè)對(duì)象,因此它也是有原型的哄啄,而它的原型就是創(chuàng)造對(duì)象的對(duì)象雅任,Object.prototype。

console.log(
  Object.getPrototypeOf(Function.prototype) === Object.prototype);
// true

Object.prototype 有沒有原型呢咨跌?有的沪么,為了避免原型鏈沒有盡頭,ES 規(guī)定了Object.prototype的原型就是 null锌半。

因此我們可以畫出原型鏈:
alice -> Person -> Function -> Object -> null

prototype chain

原型鏈之所以是復(fù)雜的禽车,就是因?yàn)樵诖a構(gòu)建工程的過程中,可以會(huì)產(chǎn)生無數(shù)的原型和原型生成的實(shí)際對(duì)象刊殉,而原型可能又會(huì)有它的原型哭当,等等,一層層的關(guān)系形成一個(gè)鏈條冗澈,這就是原型鏈。

但是原型鏈的靈活也在于此:一個(gè)實(shí)例可以調(diào)用它的原型的方法陋葡,以及原型的原型的方法亚亲。

也就是它在使用函數(shù)時(shí),會(huì)在自身的屬性/方法中查找腐缤,如果沒有找到該方法(函數(shù))捌归,它就會(huì)沿著原型鏈,查找它的原型中的方法岭粤,如果仍沒有惜索,繼續(xù)沿原型鏈進(jìn)行查找,知道原型鏈的盡頭:null剃浇。

可以看到巾兆,所有的對(duì)象都是FunctionObject原型的下游猎物,因此,所有的函數(shù)生產(chǎn)對(duì)象都可以調(diào)用 Function.prototypeObject.prototype中的方法角塑,call(this, arg1,arg2,...), apply(this,arg[]), bind(this), toString(), isPropertyOf(), hasOwnProperty()

2. 構(gòu)造函數(shù)與 new 命令

其實(shí)上面我們已經(jīng)涉及到了構(gòu)造函數(shù)蔫磨。構(gòu)造函數(shù)的實(shí)現(xiàn)沒有脫離原型鏈這一基礎(chǔ),只是在語法實(shí)現(xiàn)上來說圃伶,更靠近其他語言例如 Java 的寫法堤如。一個(gè)典型的構(gòu)造函數(shù):

function Person(name, email) {
  this.name = name;
  this.email = email;
  
  this.sayName =function() {
    console.log(this.name);
  };
  
  this.sayEmail = function() {
    console.log(this.email);
  };
}

一個(gè)典型的構(gòu)造函數(shù),有它的屬性窒朋,name, email搀罢,有它的方法,sayName(), sayEmail()侥猩,他的實(shí)例都會(huì)具有它的這些方法 由它可以生出很多實(shí)例榔至,但是需要配合 一個(gè)關(guān)鍵詞使用:new,這也是借鑒 Java 語言中的寫法拭宁。

// Java class
public class Dog {
  string name;
  int age;
  string color;

  public barking() {
    System.out.println("wo-wo-wo"); 
  }
}

Dog myDog = new Dog();
myDog.barking();

類似的洛退,在 JavaScript 中,構(gòu)造函數(shù)就相當(dāng)于是 Java 中的類杰标,它們都可以生成一個(gè)實(shí)例兵怯,并且實(shí)例可以調(diào)用構(gòu)造函數(shù)中的方法。

var alice = new Person('alice', 'alice@google.com');

alice.sayName();
alice.sayEmail();
// "alice"  "alice@google.com"

這其中涉及到 new 運(yùn)算符的實(shí)現(xiàn)腔剂,大致的實(shí)現(xiàn)步驟來說應(yīng)該是這樣:

  1. 生成一個(gè)新的空對(duì)象媒区。
  2. 將1.中生成的空對(duì)象的原型指向構(gòu)造函數(shù)的prototype屬性。
  3. 將這個(gè)空對(duì)象賦值給函數(shù)內(nèi)部中的this關(guān)鍵字掸犬。
  4. 開始執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼袜漩。
  5. 執(zhí)行完畢之后,如果原構(gòu)造函數(shù)沒有返回其他對(duì)象湾碎,則返回this宙攻。

此時(shí)的this值就指向當(dāng)前的實(shí)例,本例中就是alice介褥。

面試有可能會(huì)面的:實(shí)現(xiàn)一個(gè) new 命令座掘。

根據(jù)上面的四步,實(shí)現(xiàn)一個(gè) new 命令:

// constructor 是構(gòu)造函數(shù)柔滔,params是傳入構(gòu)造函數(shù)的參數(shù)
function _new (constructor, params) {
  var args = [...arguments]; // 將傳入?yún)?shù)變成數(shù)組
  var constructor  = args.shift(); // 取出構(gòu)造函數(shù)
  var context = Object.create(contructor.ptototype); 
  /* 以構(gòu)造函數(shù)的prototype為原型溢陪,創(chuàng)建一個(gè)prototpye對(duì)象,新建的對(duì)象具有所有構(gòu)造函數(shù)的prototype的屬性和方法睛廊。 */

  var result  = constructor.apply(context, args); //執(zhí)行構(gòu)造函數(shù)
  return (typeof result === 'object' && result!==null) ? result : context; 
// 如執(zhí)行結(jié)果非空且類型為對(duì)象形真,則返回該結(jié)果,否則超全,返回新創(chuàng)建的對(duì)象咆霜。
}

3. ES6 的 class

ES6 引入的 class 類的定義方法邓馒,從語法上更靠近了基于類的編程語言,實(shí)際上它的運(yùn)行機(jī)制仍是基于原型的裕便。

class Person {
  constructor(name, email) {
    this.name = name;
    this.email = email;   
  }

  // Methods
  sayName () {
    console.log(this.name);
  };
  sayEmail() {
    console.log(this.email);
  };
}

class的語法中绒净,基本的變量數(shù)據(jù)放到 constructor構(gòu)造數(shù)據(jù)中,而其他的成員函數(shù)偿衰,可以直接寫成函數(shù)的形式挂疆,不需要function關(guān)鍵詞,調(diào)用的時(shí)候直接在前面加上this.下翎。

class 還提供常用的gettersetter可以使用缤言。因此,setter视事,getter配合methods可以令function+new的奇怪組合消失了胆萧,讓上帝的歸上帝,凱撒的歸凱撒俐东,function回歸到它定義函數(shù)的本意跌穗,而不是定義一個(gè)'類'(雖然實(shí)際上仍是原型構(gòu)造函數(shù))。

  set setJob(offer) {
    this.offer = offer;
  }
  
  get job() {
    return this.upperName();
  }

  upperName() {
    return this.offer.toUpperCase();
  }  
  var alice = new Person('alice', 'alice@google.com');

  alice.setJob = 'Front-end';
  console.log(alice.job);  // "FRONT-END"

而且class的繼承也更像繼承了虏辫。一個(gè)子類可以繼承父類蚌吸,直接使用extends關(guān)鍵字,就像在別的基于類的函數(shù)(Java, C++)一樣砌庄。

class Woman extends Person {
  constructor(name, mail) {
    super(name, mail);
    this.name = name;
    this.mail = mail;

    this.organ = 'vaginal';
  }
  
  specialOrgan () {
    console.log(this.name + ' has '+ this.organ);
  }
}

var alice = new Woman('alice', 'alice@google.com');
alice.setJob = 'Front-end';
console.log(alice.job);
alice.specialOrgan();
//  "FRONT-END"  "alice has vaginal"

在繼承的子類Woman中羹唠,在調(diào)用父類中的成員數(shù)據(jù)前,需要調(diào)用super關(guān)鍵詞包括所有需要調(diào)用的數(shù)據(jù)娄昆,否則會(huì)出現(xiàn)引用錯(cuò)誤佩微。上面代碼可以看到,子類繼承了父類的settergetter萌焰,而且子類有自己的方法specialOrgan()哺眯,并且在子類的方法中成功地調(diào)用了父類的name屬性。

代碼調(diào)試地址
代碼調(diào)試地址

5. 參考閱讀

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末互纯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子磕蒲,更是在濱河造成了極大的恐慌留潦,老刑警劉巖只盹,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異兔院,居然都是意外死亡殖卑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門坊萝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孵稽,“玉大人,你說我怎么就攤上這事十偶∑邢剩” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵惦积,是天一觀的道長接校。 經(jīng)常有香客問我,道長狮崩,這世上最難降的妖魔是什么蛛勉? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮睦柴,結(jié)果婚禮上诽凌,老公的妹妹穿的比我還像新娘。我一直安慰自己爱只,他們只是感情好皿淋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恬试,像睡著了一般窝趣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上训柴,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天哑舒,我揣著相機(jī)與錄音,去河邊找鬼幻馁。 笑死诚些,一個(gè)胖子當(dāng)著我的面吹牛窍仰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼颜说,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了啸澡?” 一聲冷哼從身側(cè)響起飞崖,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后铲咨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躲胳,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年纤勒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坯苹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摇天,死狀恐怖粹湃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情闸翅,我是刑警寧澤再芋,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站坚冀,受9級(jí)特大地震影響济赎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜记某,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一司训、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧液南,春花似錦壳猜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至畅姊,卻和暖如春咒钟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背若未。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工朱嘴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粗合。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓萍嬉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親隙疚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子壤追,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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