您好,登錄后才能下訂單哦!
這篇文章主要介紹Angular.JS中Scope繼承的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
基本原理
在JavaScript中,每創建一個構造函數(constructor),就會同時給該函數生成一個指向原型對象的屬性prototype。每個原型對象又獲得一個constructor屬性指向相應的構造函數,原型對象的其他屬性和方法從Object繼承而來。每個通過構造函數創建的實例,都包含一個指向構造函數原型對象的內部屬性[[Prototype]](在瀏覽器中通常實現為__proto__)。構造函數、原型對象和實例三者的關系如下 (圖片來源:《JavaScript高級程序設計(第3版)》):
person1和person2為構造函數Person創建的兩個實例,可以通過[[Prototype]]屬性訪問原型對象Person Prototype,獲得原型中定義的所有方法和屬性。Person構造函數的prototype屬性同樣指向Person Prototype原型對象。以上這些概念是理解原型繼承的基礎,下面我們來看原型鏈的概念。如果把一個類型的實例賦值給一個原型對象會發生什么?根據上圖中的關系,此時的原型對象包含指向另一個原型的屬性,而另一個原型中也包含著指向另一個構造函數的屬性。
效果如下圖:
SuperType為一個父類型,在原型中定義了屬性property和方法getSuperValue;SubType是一個子類型,定義了屬性subproperty和方法getSubValue。instance為SubType的一個實例。這里通過下面的關鍵代碼,將SubType的原型對象變為SuperType對象的實例:
SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function(){ return this.subproperty; };
我們看到,SubType的原型對象中有來自SuperType實例對象的property屬性,以及自己在原型上定義的getSubValue方法,通過[[Prototype]]屬性,又可以進一步訪問SuperType原型對象中的成員。假如SuperType的原型也被賦值成某個類型的實例,依次類推,那么可以通過[[Prototype]]屬性一直向上回溯,形成一條直通Object原型對象的原型鏈。上面的例子只展示了鏈條的前兩環。
通過原型鏈的實現,SubType的實例繼承了SuperType實例的的所有實例成員和原型成員。例如,若要訪問instance.getSuperValue
,首先在instance實例內部搜索,沒有該方法;然后通過原型鏈向上回溯,找到SubType原型對象,也沒有該方法;再通過[[Prototype]]屬性繼續回溯,來到SuperType的原型對象,找到該方法。
以上描述的這種繼承方式就是原型繼承。在ES5以后,可以使用Object提供的create方法規范化上述過程,詳細請參考這里。AngularJS的Scope繼承關系的實現類似上述過程。
Scope繼承實現
在Angular中,想要定義一個Scope的child Scope可以通過scope.$new方法實現,而$new方法本身的實現就體現了上述原型繼承的思想。首先,$new方法接受兩個參數:isolated和parent。第一個參數表示創建的child scope是否是一個隔離的(isolated)。隔離的scope不繼承parent scope的原型,只是在層次結構(hierachy)上屬于其child scope,這種結構是Digest過程的基礎。isolated scope的一個好處是避免parent scope的成員被更改,在directive的實現里很有用。第二個參數指定創建的child scope的parent scope,如果不指定,默認為當前調用$new方法的scope。Angular中$new的實現類似:
$new : function(isolate, parent) { var child; parent = parent || this; if (isolate) { child = new Scope(); child.$root = this.$root; } else { if (!this.$$ChildScope) { this.$$ChildScope = createChildScopeClass(this); } child = new this.$$ChildScope(); } child.$parent = parent; //... return child; },//...
可以看出,如果是isolate為true,則使用Scope類型構造函數創建一個child對象。如果isolate為false或者未指定,則創建一個child scope原型繼承于當前scope,這個過程由createChildScopeClass提供的構造函數實現:
function createChildScopeClass(parent) { function ChildScope() { this.$$watchers = null; this.$$listeners = {};//... } ChildScope.prototype = parent; return ChildScope; }
這里定義了ChildScope類型,包括其需要的屬性。然后將該類型的prototye屬性設置為傳入的scope實例(即前面的this),這就是前面闡述的原型繼承。之后通過ChildScope創建的scope對象都是原型繼承于parent的,即可以訪問parent scope的所有成員。結合$new的代碼,如果child非隔離,則child可以訪問當前scope對象中的所有成員(例如$digest,$apply等方法以及自定義成員)。這就解釋了在我們自己創建的controller對應的scope里,可以訪問$rootScope提供的成員,因為我們的scope最終原型繼承自root scope,因而可以通過原型鏈向上回溯到root scope的實例。
在前面一篇文章中,談到了Angular中Digest過程。當調用scope.$apply
方法時,實際上是從root scope開始,按照scope的層次結構,調用每個scope的$digest方法。這就是為什么在Scope的構造函數中會設置$root屬性:
function Scope() { this.$parent = null;//... this.$root = this; this.$$destroyed = false; this.$$listeners = {}; //... }
對于一般child scope,$root會通過原型繼承得到,在root scope構造以后,后續的scope都可以訪問$root對象,即是root scope對象。對于isolated scope,由于是通過Scope構造函數創建(非原型繼承),$root被child scope覆蓋,需要將$root屬性設置為parent的$root屬性,如前面$new的實現。這就保證了在任何一個scope中始終能拿到root scope的實例,也就可以完成自上而下的Digest過程,在$apply等方法的實現中,使用$rootScope代替$root,二者相同:
$apply: function(expr) { beginPhase('$apply'); try { return this.$eval(expr); } finally { clearPhase(); } finally { $rootScope.$digest(); } },//...
$rootScope是$RootScopeProvider提供的Scope類型實例,是最先初始化的scope對象。在開發中,我們可以這樣使用child scope:
.controller('smallCatCtrl', [ '$scope', function($scope){ var child = $scope.$new(); child.text = 'cat'; var child1 = $scope.$new(true); child1.value = 0; var child2 = $scope.$new(true, child); child2.value = 1; child2.$watch('value', function(oldValue, newValue){ console.log('child2.value changed'); }); child1.$watch('value', function(oldValue, newValue){ console.log('child1.value changed'); }) child.$watch('text', function(oldValue, newValue){ console.log('child.text changed'); }); console.log(child2.text); }]);
在這段代碼中,首先創建$scope的一個子scope----child,沒有給$new指定參數,意味著child原型繼承于$scope。同時定義了child的屬性text。接下來創建$scope的第二個子scope----child1,傳入$new的參數要求child1是isolated scope,并且在層次結構上是$scope的后代。同時定義了其value屬性。最后創建scope child2,它也是一個isolated scope,不同的是它以child為層次結構上的parent scope。
這段代碼的輸出如下:
首先,child2只是在層次結構上繼承于child,因此沒有把child實例作為原型,也就沒有text屬性,第一行輸出undefined。
由于Digest過程按scope層次結構自上而下進行,類似于樹的深度遍歷過程。在該例中scope的順序為$scope->child->child2->child1,因此三個watch listener函數的輸出也按照上面的順序。
以上是“Angular.JS中Scope繼承的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。