簡述原型鏈是什么,有什么用處?若想訪問一個對象的原型,應該使用什么方法?

背景介紹

1.構造函數

構造函數 ,是一種特殊的方法。主要用來在創建對象時初始化對象。每個構造函數都有prototype(原型)屬性


image.png

2.原型模式

每個函數都有prototype(原型)屬性,這個屬性是一個指針,指向一個對象,這個對象的用途是包含特定類型的所有實例共享的屬性和方法,即這個原型對象是用來給實例共享屬性和方法的。
而每個實例內部都有一個指向原型對象的指針。


image.png

原型鏈

每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含指向原型對象內部的指針。我們讓原型對象的實例(1)等于另一個原型對象(2),
此時原型對象(2)將包含一個指向原型對象(1)的指針,
再讓原型對象(2)的實例等于原型對象(3),如此層層遞進就構成了實例和原型的鏈條,這就是原型鏈的概念


image.png

知識剖析

構造函數

構造函數 ,是一種特殊的方法。主要用來在創建對象時初始化對象。 即為對象變量賦初始值。每個構造函數的實例都將共享構造函數的初始值。 構造函數的出現是為了解決使用Object構造函數和字面量表示法不方便創建大量重復對象的問題。

傳統創建對象實例的方法
   var person={
       name:'張女士',
       age:'80',
       gender:'女'
   };
 console.log(person)

注:這個方法如果用于創建大量相同屬性和方法的對象時,會產生大量重復代碼

構造函數的方法
     //構造函數方法創建對象實例
     function Person(name,age,gender) {
     this.name=name;
     this.age=age;
     this.gender=gender;
     this.say=function () {
     alert(this.name)
           }
     }
    var person1=new Person('鐘女士',80,'女');
    var person2=new Person('張女士',80,'女');
    console.log(person2)
    console.log(person1)

原型模式

使用構造函數的問題是,每個方法都要在每個實例上重新創建一遍,即在構造函數的不同實例上的同名函數是不相等的。而我們創建每個構造函數都有一個prototype(原型)屬性,這個屬性是個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法,我們使用這個原型對象來共享實例的屬性和方法的模式就叫原型模式

  //原型模式創建對象
function Person(){
 }
Person.prototype.name='鐘女士';
Person.prototype.age=80;
Person.prototype.gender='女';
var person1= new Person();
console.log(person1)
//簡寫原型模式
Person.prototype={
   constructor:Person
   name:'鐘女士',
   age:80,
   gender:'女'
 }

注:每個原型對象都有constructor屬性,由于簡寫模式重寫了默認的prototype對象,所以constructor也會被重新定義,不再指向他的構造函數,所以可以自己寫一個constructor屬性指向他的構造函數

原型鏈

每個構造函數都有原型對象,每個構造函數實例都包含一個指向原型對象的內部指針(proto),如果我們讓第一個構造函數的原型對象等于第二個構造函數的實例,結果第一個構造函數的原型對象將包含一個指向第二個原型對象的指針,再然第三個原型對象等于第一個構造函數的實例,這樣第三個原型對象也將包含指向第一個原型對象的指針,以此類推,就夠成了實例于原型的鏈條,這就是原型鏈的基本概念

function One(){
 }
 function Two(){
 }
 function Three(){
 }
 Two.prototype=new One();
 Three.prototype=new Two();
 var three=new Three();
 console.log(three);
 console.log(three.__proto__===Three.prototype) //true
 console.log(three.__proto__.__proto__===Two.prototype) //true
 console.log(three.__proto__.__proto__.__proto__===One.prototype)  //true
 console.log(three.__proto__.__proto__.__proto__.__proto__===Object.prototype)  //true

3.常見問題

若想訪問一個對象的原型,應該使用什么方法?

4.解決方法

在對象實例中,訪問對象原型的方法
  • 1、使用proto屬性
    此屬性是瀏覽器支持的一個屬性,并不是ECMAScript里的屬性

  • 2.Object.getPrototypeOf

  • 3.使用constructor.prototype的方法
    對于不支持proto的瀏覽器,可以使用constructor,訪問到對象的構造函數,在用prototype訪問到原型

在構造函數中,訪問對象原型的方法

1.使用prototype屬性


5.編碼實戰


6.拓展思考

使用原型鏈解釋ANUGLAR作用域

在開發過程中,我們可能會出現控制器的嵌套,看下面這段代碼:

    <div ng-controller="OuterCtrl">
        <span>{{a}}</span>
         <div ng-controller="InnerCtrl">
            <span>{{a}}</span>
         </div>
     </div>
    <script>
    function OuterCtrl($scope) {
    $scope.a = 1;
    }
    function InnerCtrl($scope) {
    }
    </script>

我們可以看到界面顯示了兩個1,而我們只在OuterCtrl的作用域里定義了a變量,但界面給我們的結果是,兩個a都有值,現在自控制器里的a是從父控制器里繼承過來的

我們可以父子級的作用域看成兩個原型對象,其中一個原型對象繼承另一個原型對象的實例

function Outer() {
    this.a = 1;
}

function Inner() {
}

var outer = new Outer();
Inner.prototype=new Outer();
var inner = new Inner();
console.log(outer.a)
console.log(inner.a)

Angular的實現機制其實也就是把這兩個控制器中的$scope作了關聯,外層的作用域實例成為了內層作用域的原型。

既然作用域是通過原型來繼承的,自然也就可以推論出一些特征來。比如說這段代碼,點擊按鈕的結果是什么?
<div ng-controller="OuterCtrl">
    <span>{{a}}</span>
    <div ng-controller="InnerCtrl">
        <span>{{a}}</span>
        <button ng-click="a=a+1">a++</button>
    </div>
</div>
<script>
function OuterCtrl($scope) {
    $scope.a = 1;
}

function InnerCtrl($scope) {
}
</script>

點了按鈕之后,兩個a不一致了,里面的變了,外面的沒變,這是為什么?

function Outer() {
    this.a = 1;
}

function Inner() {
}

var outer = new Outer();
Inner.prototype=new Outer();
var inner = new Inner();
inner.a = inner.a + 1;
console.log(outer.a)
console.log(inner.a)

因為在原型鏈中,訪問一個實例屬性時,會在實例本身查找,如果找不到,則搜索實例的原型,如果再搜索不到,則繼續沿著原型鏈往上查找。找到之后則會賦給該實例,所以inner上面就被賦值了一個新的a,outer里面的仍然保持原樣,這也就導致了剛才看到的結果。

上下級共享變量

比如說,我們就是想上下級共享變量,不創建新的,該怎么辦呢?

function Outer() {
    this.data = {
        a: 1
    };
}

function Inner() {
}

var outer = new Outer();
Inner.prototype = outer;

var inner = new Inner();

console.log(outer.data.a);
console.log(inner.data.a);
inner.data.a += 1;

console.log(outer.data.a);
console.log(inner.data.a);

我們可以把a寫在一個對象里,當inner找到對象data并賦值到自己身上時,其實是復制了對象的指針(參考高程第4章復制引用類型和基本類型的區別),我們對對象里的屬性的改動都會反映到所有引用該對象的元素上。
反映到AngularJs,我們可以這么寫

<div ng-controller="OuterCtrl">
    <span>{{data.a}}</span>
    <div ng-controller="InnerCtrl">
        <span>{{data.a}}</span>
        <button ng-click="data.a=data.a+1">increase a</button>
    </div>
</div>
<script>
function OuterCtrl($scope) {
    $scope.data = {
        a: 1
    };
}

function InnerCtrl($scope) {
}
</script>

這樣點擊按鈕兩個控制器的a都會+1


7、參考文獻:

參考一: JavaScript高級程序設計

參考二: 作用域與事件


8.更多討論

Q1:什么是原型?
A1:我們創建的每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象.這個對象稱為原型對象,簡稱原型

Q2:用什么方法可以訪問原型鏈自身的屬性名?
A2:可以使用object.getownpropertynames()的方法訪問原型鏈自身屬性名

Q3:構造函數實例的方法和原型模式相比,缺點在哪里?
A3:構造函數的在每次實例化之后都會在實例上重新創建方法,即,構造函數的不同實例之間的同一個方法是不相等的,而原型模式只是復制了方法的指針。


PPT
視頻

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容