詳解js原型欺矫,構(gòu)造函數(shù)以及class之間的原型關(guān)系

原型

概念

在構(gòu)造函數(shù)創(chuàng)建的時候,系統(tǒng)默認的幫構(gòu)造函數(shù)創(chuàng)建并關(guān)聯(lián)一個對象 這個對象就是原型

作用

在原型中的所有屬性和方法,都可以被和其關(guān)聯(lián)的構(gòu)造函數(shù)創(chuàng)建出來的所有的對象共享

訪問原型

構(gòu)造函數(shù)名.prototype 實例化的對象.__proto __

原型的簡單使用
  1. 利用對象的動態(tài)特性為原型對象增加成員
  2. 直接替換原型對象(jq核心方法的實現(xiàn) 就是使用原型替換的思想)
 function Person (name) {
      this.name = name
    }

    Person.prototype.fn = function () {
      console.log('hello world')
      console.log(this.name)
    }

    var p = new Person('小紅')

    p.fn()
    p.name = '曉麗'
    p.fn()
    console.log(Person.prototype)
    console.log(p)
image

image

1. prototype

含義: 是一個函數(shù)的屬性适掰,這個屬性是一個指針,指向一個對象
作用: 構(gòu)造函數(shù)調(diào)用 訪問該構(gòu)造函數(shù)所關(guān)聯(lián)的原型對象

2. proto

含義: 是一個對象擁有的內(nèi)置屬性荠列,是js內(nèi)部使用尋找原型鏈的屬性类浪,通過該屬性可以允許實例對象直接訪問到原型

3. constructor

含義:原型對象的constructor 指向其構(gòu)造函數(shù),如果替換了原型對象之后,這個constructor屬性就不準(zhǔn)確肌似,需要手動補充一下


image

原型鏈

image

構(gòu)造函數(shù)以及js原生Object對象之間的原型關(guān)系

image

原型的注意事項

  • 當(dāng)對象在訪問屬性和方法的時候费就,會現(xiàn)在自身查找,如果沒有才回去原型中找川队。(一級一級傳遞 形成了原型鏈)
  • 替換原型對象的時候力细,替換之前構(gòu)造函數(shù)創(chuàng)建的對象A和替換之后創(chuàng)建的對象B,A和B的原型是不一致的呼寸。
  • 對象能夠訪問的原型艳汽,就是在對象創(chuàng)建的那一刻猴贰,和構(gòu)造函數(shù)關(guān)聯(lián)的那個原型

擴展以及延伸

image

構(gòu)造函數(shù)

在很多編程語言中对雪,如java,objectC,c++等米绕,都存在類的概念瑟捣,類中有私有屬性,私有方法等栅干,通過類來實現(xiàn)面對對象的繼承迈套,但是,在ES5以及以前中不像上面這幾種語言一樣碱鳞,有嚴格的類的概念桑李。js通過構(gòu)造函數(shù)以及原型鏈來實現(xiàn)繼承。

特點
  • 首字母必須為大寫窿给,用來區(qū)分普通函數(shù)
  • 內(nèi)部使用的this對象贵白,來指向即將要生成的實例對象
  • 使用new 關(guān)鍵字來生成實例對象(下面為new關(guān)鍵字的具體實現(xiàn))
    var obj = new Date()
    // 可以分解為
    var obj = {};
    obj.__proto__ = Date.prototype;
    Base.call(obj)

缺點
  • 所有的實例對象都可以繼承構(gòu)造器函數(shù)中的屬性和方法,但是同一個對象實例之間崩泡,無法共享屬性禁荒。
  • 如果方法在構(gòu)造函數(shù)內(nèi)部,每次new一個實例對象的時候角撞,都會創(chuàng)建內(nèi)部的這些方法呛伴,并且不同的實例對象之間勃痴,不能共享這些方法,造成了資源的浪費(于是有了原型這個概念)

實現(xiàn)方式 (簡單列舉幾種)

構(gòu)造函數(shù)模式(自定義構(gòu)造函數(shù))

構(gòu)造函數(shù)與普通函數(shù)的區(qū)別

//構(gòu)造函數(shù)
function Egperson (name,age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
        alert(this.name);
     }
}
var person = new Egperson('mike','18'); //this-->person
person.sayName();  //'mike'


//普通函數(shù)
function egPerson (name,age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
        alert(this.name);
     }
}
egPerson('alice','23'); //this-->window
window.sayName();  //'alice'

工廠模式

function CreatePerson(name, age, gender){
    var obj = {};
    obj.name = name;
    obj.age = age;
    obj.gender = gender;
    //由于是函數(shù)調(diào)用模式热康,所以this打印出來是window
    console.log(this);
    return obj;
}
var p = CreatePerson("小明", 18, "male");  // 調(diào)用方式是函數(shù)的調(diào)用方式

寄生模式

function CreatePerson(name, age, gender){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.gender = gender;
    //這里的this指向new 創(chuàng)建出來的對象
    console.log(this);
    return obj;
}
var p = new CreatePerson("小明", 18, "male");  // 調(diào)用方式是函數(shù)的調(diào)用方式

動態(tài)原型模式

function Person(name, age, job) {
    //屬性
    this.name = name;
    this.age = age;
    this.job = job;
    
    //方法
    if(typeof this.sayName != "function") {
        //所有的公有方法都在這里定義
        Person.prototype.sayName = function() {
            alert(this.name);
        }沛申;

        Person.prototype.sayJob = function() {
            alert(this.job);
        };


        Person.prototype.sayAge = function() {
            alert(this.age);
        };
    }
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.sayName();        //Nicholas
person2.sayName();        //Greg


js實現(xiàn)繼承的方式: 混入式繼承,原型繼承以及經(jīng)典繼承姐军,ES6的Class也可以實現(xiàn)繼承

Class 詳解

基本上污它,ES6 的class可以看作只是一個語法糖,它的絕大部分功能庶弃,ES5 都可以做到衫贬,新的class寫法只是讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已歇攻。

// ES5
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);
// ES6

//定義類
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}


特點

  • class中的constructor函數(shù)相當(dāng)于ES5中的構(gòu)造函數(shù)(聲明屬性以及靜態(tài)方法固惯,這種類創(chuàng)建屬性和創(chuàng)建方法參照上面動態(tài)原型模式的構(gòu)造函數(shù)。個人感覺有很多相似之處
  • 類中定義方法的時候缴守,前面不加function葬毫,后面不加,可被實例對象也就是子類繼承的方法 定義在類的prototype屬性中屡穗。
  • 類的內(nèi)部定義的所有方法都是不可枚舉的
  • 類和模塊內(nèi)部默認采用嚴格模式
  • 子類繼承父類以后贴捡,必須在constructor中調(diào)用時super方法,否則不能新建實例村砂,因為子類沒有屬于自己的this對象烂斋,而是繼承了父類的this對象對其進行加工

類中的原型鏈關(guān)系

每一個對象都有proto屬性,指向?qū)?yīng)的構(gòu)造函數(shù)的prototype屬性础废。Class 作為構(gòu)造函數(shù)的語法糖汛骂,同時有prototype屬性和proto屬性,因此同時存在兩條繼承鏈评腺。

  1. 子類的proto屬性帘瞭,表示構(gòu)造函數(shù)的繼承,總是指向父類蒿讥。
  2. 子類prototype屬性的proto屬性蝶念,表示實例方法的繼承,總是指向父類的prototype屬性芋绸。
class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

類的繼承內(nèi)部實現(xiàn)

class A {
}

class B {
}

// B 的實例繼承 A 的實例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 繼承 A 的靜態(tài)屬性
Object.setPrototypeOf(B, A);

const b = new B();

Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

作為一個對象媒殉,子類(B)的原型(proto屬性)是父類(A);

作為一個構(gòu)造函數(shù)侥钳,子類(B)的原型對象(prototype屬性)是父類的原型對象(prototype屬性)的實例适袜。

Object.create(A.prototype);
// 等同于
B.prototype.__proto__ = A.prototype;

實例的 proto 屬性

子類實例的proto屬性的proto屬性,指向父類實例的proto屬性舷夺。也就是說苦酱,子類的原型的原型售貌,是父類的原型。

var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');

p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true

類中this指向問題

類的方法內(nèi)部含有this疫萤,默認指向類的實例颂跨。但是當(dāng)類中的實例方法提取出來使用的時候,this指向運行時所在環(huán)境扯饶。

解決方法(新版react中恒削,在聲明綁定方法的時候 三種方式與此相同)

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }

  // ...
}

class Logger {
  constructor() {
    this.printName = (name = 'there') => {
      this.print(`Hello ${name}`);
    };
  }

  // ...
}

ES5與ES6 實現(xiàn)繼承的區(qū)別

  1. 在ES5中,繼承實質(zhì)上是子類先創(chuàng)建屬于自己的this尾序,然后再將父類的方法添加到this(也就是使用Parent.apply(this)的方式

  2. 而在ES6中钓丰,則是先創(chuàng)建父類的實例對象this,然后再用子類的構(gòu)造函數(shù)修改this每币。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末携丁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子兰怠,更是在濱河造成了極大的恐慌梦鉴,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揭保,死亡現(xiàn)場離奇詭異肥橙,居然都是意外死亡,警方通過查閱死者的電腦和手機秸侣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門存筏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人塔次,你說我怎么就攤上這事方篮。” “怎么了励负?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長匕得。 經(jīng)常有香客問我继榆,道長,這世上最難降的妖魔是什么汁掠? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任略吨,我火速辦了婚禮,結(jié)果婚禮上考阱,老公的妹妹穿的比我還像新娘翠忠。我一直安慰自己,他們只是感情好乞榨,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布秽之。 她就那樣靜靜地躺著当娱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪考榨。 梳的紋絲不亂的頭發(fā)上跨细,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音河质,去河邊找鬼冀惭。 笑死,一個胖子當(dāng)著我的面吹牛掀鹅,可吹牛的內(nèi)容都是我干的散休。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼乐尊,長吁一口氣:“原來是場噩夢啊……” “哼溃槐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起科吭,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤昏滴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后对人,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谣殊,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年牺弄,在試婚紗的時候發(fā)現(xiàn)自己被綠了姻几。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡势告,死狀恐怖蛇捌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咱台,我是刑警寧澤络拌,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站回溺,受9級特大地震影響春贸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遗遵,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一萍恕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧车要,春花似錦允粤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽司光。三九已至,卻和暖如春阔挠,著一層夾襖步出監(jiān)牢的瞬間飘庄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工购撼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留跪削,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓迂求,卻偏偏與公主長得像碾盐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子揩局,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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