簡(jiǎn)述原型鏈?zhǔn)鞘裁?,有什么用處?若想訪問(wèn)一個(gè)對(duì)象的原型,應(yīng)該使用什么方法?

背景介紹

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

構(gòu)造函數(shù) ,是一種特殊的方法。主要用來(lái)在創(chuàng)建對(duì)象時(shí)初始化對(duì)象。每個(gè)構(gòu)造函數(shù)都有prototype(原型)屬性


image.png

2.原型模式

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


image.png

原型鏈

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


image.png

知識(shí)剖析

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

構(gòu)造函數(shù) ,是一種特殊的方法。主要用來(lái)在創(chuàng)建對(duì)象時(shí)初始化對(duì)象。 即為對(duì)象變量賦初始值。每個(gè)構(gòu)造函數(shù)的實(shí)例都將共享構(gòu)造函數(shù)的初始值。 構(gòu)造函數(shù)的出現(xiàn)是為了解決使用Object構(gòu)造函數(shù)和字面量表示法不方便創(chuàng)建大量重復(fù)對(duì)象的問(wèn)題。

傳統(tǒng)創(chuàng)建對(duì)象實(shí)例的方法
   var person={
       name:'張女士',
       age:'80',
       gender:'女'
   };
 console.log(person)

注:這個(gè)方法如果用于創(chuàng)建大量相同屬性和方法的對(duì)象時(shí),會(huì)產(chǎn)生大量重復(fù)代碼

構(gòu)造函數(shù)的方法
     //構(gòu)造函數(shù)方法創(chuàng)建對(duì)象實(shí)例
     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)

原型模式

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

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

注:每個(gè)原型對(duì)象都有constructor屬性,由于簡(jiǎn)寫模式重寫了默認(rèn)的prototype對(duì)象,所以constructor也會(huì)被重新定義,不再指向他的構(gòu)造函數(shù),所以可以自己寫一個(gè)constructor屬性指向他的構(gòu)造函數(shù)

原型鏈

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

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.常見(jiàn)問(wèn)題

若想訪問(wèn)一個(gè)對(duì)象的原型,應(yīng)該使用什么方法?

4.解決方法

在對(duì)象實(shí)例中,訪問(wèn)對(duì)象原型的方法
  • 1、使用proto屬性
    此屬性是瀏覽器支持的一個(gè)屬性,并不是ECMAScript里的屬性

  • 2.Object.getPrototypeOf

  • 3.使用constructor.prototype的方法
    對(duì)于不支持proto的瀏覽器,可以使用constructor,訪問(wèn)到對(duì)象的構(gòu)造函數(shù),在用prototype訪問(wèn)到原型

在構(gòu)造函數(shù)中,訪問(wèn)對(duì)象原型的方法

1.使用prototype屬性


5.編碼實(shí)戰(zhàn)


6.拓展思考

使用原型鏈解釋ANUGLAR作用域

在開(kāi)發(fā)過(guò)程中,我們可能會(huì)出現(xiàn)控制器的嵌套,看下面這段代碼:

    <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>

我們可以看到界面顯示了兩個(gè)1,而我們只在OuterCtrl的作用域里定義了a變量,但界面給我們的結(jié)果是,兩個(gè)a都有值,現(xiàn)在自控制器里的a是從父控制器里繼承過(guò)來(lái)的

我們可以父子級(jí)的作用域看成兩個(gè)原型對(duì)象,其中一個(gè)原型對(duì)象繼承另一個(gè)原型對(duì)象的實(shí)例

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的實(shí)現(xiàn)機(jī)制其實(shí)也就是把這兩個(gè)控制器中的$scope作了關(guān)聯(lián),外層的作用域?qū)嵗蔀榱藘?nèi)層作用域的原型。

既然作用域是通過(guò)原型來(lái)繼承的,自然也就可以推論出一些特征來(lái)。比如說(shuō)這段代碼,點(diǎn)擊按鈕的結(jié)果是什么?
<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>

點(diǎn)了按鈕之后,兩個(gè)a不一致了,里面的變了,外面的沒(méi)變,這是為什么?

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)

因?yàn)樵谠玩溨?,訪問(wèn)一個(gè)實(shí)例屬性時(shí),會(huì)在實(shí)例本身查找,如果找不到,則搜索實(shí)例的原型,如果再搜索不到,則繼續(xù)沿著原型鏈往上查找。找到之后則會(huì)賦給該實(shí)例,所以inner上面就被賦值了一個(gè)新的a,outer里面的仍然保持原樣,這也就導(dǎo)致了剛才看到的結(jié)果。

上下級(jí)共享變量

比如說(shuō),我們就是想上下級(jí)共享變量,不創(chuàng)建新的,該怎么辦呢?

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(bǔ)寫在一個(gè)對(duì)象里,當(dāng)inner找到對(duì)象data并賦值到自己身上時(shí),其實(shí)是復(fù)制了對(duì)象的指針(參考高程第4章復(fù)制引用類型和基本類型的區(qū)別),我們對(duì)對(duì)象里的屬性的改動(dòng)都會(huì)反映到所有引用該對(duì)象的元素上。
反映到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>

這樣點(diǎn)擊按鈕兩個(gè)控制器的a都會(huì)+1


7、參考文獻(xiàn):

參考一: JavaScript高級(jí)程序設(shè)計(jì)

參考二: 作用域與事件


8.更多討論

Q1:什么是原型?
A1:我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè)prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象.這個(gè)對(duì)象稱為原型對(duì)象,簡(jiǎn)稱原型

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

Q3:構(gòu)造函數(shù)實(shí)例的方法和原型模式相比,缺點(diǎn)在哪里?
A3:構(gòu)造函數(shù)的在每次實(shí)例化之后都會(huì)在實(shí)例上重新創(chuàng)建方法,即,構(gòu)造函數(shù)的不同實(shí)例之間的同一個(gè)方法是不相等的,而原型模式只是復(fù)制了方法的指針。


PPT
視頻

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容