1.scope的幾種繼承
說到scope,我們可能會想到rootScope,因爲所有的scope都是由rootScope產生出來的,我們也知道在angular裏面 scope有幾種繼承形式:
1.scope爲false,完成繼承父的scope,子scope,父scope修改值都互相影響(其實同一scope下修改的是用一個值)
2.scope爲true,子創建一個scope(注意此時和父是兩個不用scope),但是還是繼承父scope,怎麼理解呢,拿個構造函數來說吧,我實例化這個constructor,那麼實例化出來的對象應該是同時擁有constructor和prototype上的屬性和方法,假如有個屬性a在constructor和prototype都有,那麼首先肯定先取constructor.a,constructor上沒有,再取prototype.a,那麼constructor可以比作子scope,prototype可以比作父scope,當子scope沒有,再去父scope去取。
3.scope爲{},那麼此時創建一個獨立的scope,和父scope是沒有關係,兩個scope都是互不想幹的(此時有幾種父傳值給子的方式)
有了這些概念之後我們來看個例子:
html
<div ng-app="App">
<parent-scope>
</parent-scope>
</div>
js
angular.module("App", [])
.directive("parentScope", function () {
return {
restrict: "ECAM",
template: '<input type="text" ng-model="username"><child-scope></child-scope>',
scope: false,
controller: ['$scope', function ($scope) {
$scope.username = '張三'
}]
}
})
.directive("childScope", function () {
return {
restrict: "ECAM",
template: '<input type="text" ng-model="username">',
replace: true,
scope: false,
}
})
結果:子scope繼承父scope,並且互相影響!
然後把子的scope改成true,結果是當子scope無此屬性,父可以影響到子,當子scope有此屬性,父影響不到子,子也影響不到父!
最後把子的scope改成{},那就毫無疑問,兩者互不影響!
2.controller繼承
可能看到controller繼承有點懵?沒聽過controller繼承?還是拿個上面例子來說,稍微改了下:
angular.module("App", [])
.directive("parentScope", function () {
return {
restrict: "ECAM",
template: '<input type="text" ng-model="ctrl.username"><child-scope></child-scope>',
scope: false,
controllerAs:'ctrl',
controller: ['$scope', function ($scope) {
var ctrl = this;
ctrl.username = '張三'
}]
}
})
.directive("childScope", function () {
return {
restrict: "ECAM",
template: '<input type="text" ng-model="ctrl.username">',
replace: true,
scope: true,
}
})
我把username綁到controller上,然後把子scope改成true,想下結果是什麼?很多人認爲可能和scope一樣的吧,ctrl是在scope裏面的,應該和scope繼承一樣,但是結果不是一樣,是子scope爲false結果一樣,互相影響!無論改變哪個scope值,都會一起變化!
爲什麼會是這樣的?
還記得scope是怎麼創建的和怎麼繼承的嗎?(不瞭解的可以點擊這裏)
這裏我大概說下,首先創建的是rootScope(根作用域),然後創建子scope,創建子scope時候會判斷此scope是否是隔離的,如果是不是隔離就會繼承父scope,ChildScope.prototype = parent,如果是隔離的就new scope ,此時和原型繼承毫無關係了。上段源代碼:
$new: function (isolate, parent) {
var child;
parent = parent || this;
//是否是隔離scope
if (isolate) {
//此時和原型繼無關
child = new Scope();
child.$root = this.$root;
} else {
//判斷是否有
if (!this.$$ChildScope) {
//子構造函數的創建並繼承父
this.$$ChildScope = createChildScopeClass(this);
}
//子作用域的創建
child = new this.$$ChildScope();
}
//把自己的父,兄弟姐妹,兒子整明白(要不然亂套了)
child.$parent = parent;
// 孩子的前一個兄弟節點爲父親的最後一個孩子
child.$$prevSibling = parent.$$childTail;
if (parent.$$childHead) {
parent.$$childTail.$$nextSibling = child;
parent.$$childTail = child;
} else {
parent.$$childHead = parent.$$childTail = child;
}
if (isolate || parent !== this) child.$on('$destroy', destroyChildScope);
return child;
}
function createChildScopeClass(parent) {
function ChildScope() {
this.$$watchers = this.$$nextSibling =
this.$$childHead = this.$$childTail = null;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$id = nextUid();
this.$$ChildScope = null;
this.$$suspended = false;
}
ChildScope.prototype = parent;
return ChildScope;
}
上面有註釋,已經原理很清楚了,那麼再來看上面的例子,首先創建rootScope,parentScope指令創建的scope繼承rootScope,也是就用一scope了,所以id都爲1。而childScope創建的scope雖然也繼承rootScope,但是它還是個創建了一個新的scope(因爲scope=true)。
此圖爲parentScope(id爲1):
此圖爲childScope(id爲2):
因爲parentScope裏面創建了一個controller,所以你在parentScope圖中可以看到有個ctrl屬性,根據原型繼承ChildScope.prototype = parentScope,那麼ChildScope原型上必定有ctrl屬性,我們來看看ChildScope的__proto__屬性:
所以不管輸入那邊的,它們都互相都受影響!這是一種繼承方式,下面我們再來說下另外一種獲取controller方式(用的比較少,嚴格的說不能是繼承,只是拿到這個controller)
這裏我們需要用到$controller service(angualr內置服務),下面看下怎麼實現的:
html:
<div ng-app="App">
<div ng-controller="firstController">
<inherit-test></inherit-test>
</div>
</div>
js:
angular.module("App", [])
.controller("firstController", [function () {
var ctrl = this;
ctrl.name = "張三";
ctrl.age = 18;
ctrl.sex = '男';
}])
.directive("inheritTest", function () {
return {
restrict: "ECAM",
controllerAs: 'ctrl',
controller: ['$controller', '$scope', function ($controller, $scope) {
var ctrl = this;
ctrl.test = 'inherit';
var test = $controller('firstController', {
$scope: $scope
})
angular.extend(this, test);
console.log(test); //age: 18 name: "張三" sex: "男" test: "inherit"
}],
}
})
這裏就和scope沒有關係,其實大概原理是angular會把註冊的controller放到一個invokeQueue數組裏面,此時並沒有執行,等到 angular初始化時 angularInit(window.document, bootstrap),然後$ControllerProvider.register.apply($ControllerProvider,firstController);在register方法裏面把所有註冊的controller放到一個controllers對象裏面,我們看下這個register源碼(無關的代碼去掉了):
function $ControllerProvider() {
//裝所有註冊的controller的對象
var controllers = {};
//兩個參數相當是('firstController',firstControllerConstructor)
this.register = function (name, constructor) {
//判斷名稱是hasOwnProperty拋出錯誤
assertNotHasOwnProperty(name, 'controller');
//是對象的話就讓controllers繼承它
if (isObject(name)) {
extend(controllers, name);
} else {
//與firstController爲key,firstControllerConstructor爲value形式放到controllers裏面
controllers[name] = constructor;
}
};
所以我們可以根據controller註冊名稱可以去這個controllers對象裏面找它。另外還有一種組件之間controller獲取(用的是require,如果不懂的可以點擊這裏)。
3.this的指向
Controller是一個的構造函數,必須要new(實例化後)才能進行調用,所以必定會生成一個Controller實例,此實例也必然會繼承構造函數中this綁定的所有屬性與方法,此時的this指向的是Controller實例。再看下面代碼:
angular.module("App", [])
.controller("firstController", ['$scope',function ($scope) {
var ctrl = this;
ctrl.name = "張三";
ctrl.age = 18;
ctrl.sex = '男';
$scope.method =function(){
console.log(this);//scope
}
}])
從上面的代碼可以看出,它們其實沒什麼聯繫,如果非要說有的話,那麼它們都可以在同一個構造函數中互相訪問,$scope是這個函數的形參,this是這個函數的上下文,所以它們絕對不是同一個對象。Controller中的this與$scope都可以存儲數據,最好不要數據分散存儲兩個對象中。好了說到這裏都已經說的差不多了,有不正的地方請指正,不勝感激!!歡樂的時光總是過得特別快,又到時候和大家講拜拜!!