------------------------------------------------------------------------------ html
'this' vs $scope in AngularJS controllersangularjs
How does this and $scope work in AngularJS controllers?web
this
this
is the controller.(當控制器構造函數被「調用」時,this就是控制器函數)$scope
object is called, this
is the "scope in effect when the function was called". This may (or may not!)(this多是也可不能不是$scope) be the $scope
that the function is defined on. So, inside the function, this
and $scope
may not be the same.$scope
$scope
object(每一個控制器都有一個與其相關聯的$scope).$scope
.$scope
object (and parent scope objects, if prototypical inheritance is in play) are accessible from the HTML/view. E.g., from ng-click
, filters, etc.A controller function is a JavaScript constructor function.(控制器函數就是一個普普統統地構造器函數)app
When the constructor function executes (e.g., when a view loads), this
(i.e., the "function context") is set to the controller object. So in the "tabs" controller constructor function, when the addPane function is createdless
this.addPane = function(pane) { ... }
it is created on the controller object, not on $scope. Views cannot see the addPane function -- they only have access to functions defined on $scope. In other words, in the HTML, this won't work:ide
<a ng-click="addPane(newPane)">won't work</a>
After the "tabs" controller constructor function executes, we have the following:函數
The dashed black line indicates prototypal inheritance -- an isolate scope prototypically inherits from Scope. (It does not prototypically inherit from the scope in effect where the directive was encountered in the HTML.)性能
Now, the pane directive's link function wants to communicate with the tabs directive (which really means it needs to affect the tabs isolate $scope in some way). Events could be used, but another mechanism is to have the pane directive require
the tabs controller. (There appears to be no mechanism for the pane directive to require
the tabs $scope.)測試
So, this begs the question: if we only have access to the tabs controller, how do we get access to the tabs isolate $scope (which is what we really want)?ui
Well, the red dotted line is the answer. The addPane() function's "scope" (I'm referring to JavaScript's function scope/closures here) gives the function access to the tabs isolate $scope. I.e., addPane() has access to the "tabs IsolateScope" in the diagram above because of a closure that was created when addPane() was defined. (If we instead defined addPane() on the tabs $scope object, the pane directive would not have access to this function, and hence it would have no way to communicate with the tabs $scope.)
To answer the other part of your question: how does $scope work in controllers?
:
Within functions defined on $scope, this
is set to "the $scope in effect where/when the function was called". Suppose we have the following HTML:
<div ng-controller="ParentCtrl"> <a ng-click="logThisAndScope()">log "this" and $scope</a> - parent scope <div ng-controller="ChildCtrl"> <a ng-click="logThisAndScope()">log "this" and $scope</a> - child scope </div> </div>
And the ParentCtrl
(Solely) has
$scope.logThisAndScope = function() { console.log(this, $scope) }
Clicking the first link will show that this
and $scope
are the same, since "the scope in effect when the function was called" is the scope associated with the ParentCtrl
.
Clicking the second link will reveal this
and $scope
are not the same, since "the scope in effect when the function was called" is the scope associated with the ChildCtrl
. So here, this
is set to ChildCtrl
's $scope
. Inside the method, $scope
is still the ParentCtrl
's $scope(點擊子控制器時,this指向了子控制器(this的指向問題),$scope仍舊指向了父控制器).
I try to not use this
inside of a function defined on $scope, as it becomes confusing which $scope is being affected, especially considering that ng-repeat, ng-include, ng-switch, and directives can all create their own child scopes.
--------------------------------------------------------------------------
最近在Angular項目中遇到關於controller內使用$scope&this 暴露數據的問題,下面來分析一下:
「controller as」 是Angular在1.2版本後新增的語法,我將從引用方式,做用範圍,對象對比三個方面作二者的比較:
1) $scope 只須要在注入中聲明,後面就能夠直接在附加數據對象:
controller:
function ACtrl($scope) {
$scope.test = "一個例子"; //在$scope對象中加入test
}
html:
<div ng-controller="ACtrl">
{{test}}
</div>
2) this 則採用了controller as(須要版本爲ng 1.2+)寫法:
controller:
function BCtrl() {
var vm = this;
this.test = "一個例子"; //在this對象中加入test
}
html:
<!-- vm爲本身爲當前控制器做的一個簡略記號,也能夠寫做 BCtrl as b,
後面變量即可以在b中引出 如b.test -->
<div ng-controller="BCtrl as vm">
{{vm.test}}
</div>
1) $scope 中的變量或數據對象咱們能夠所有拿到,而且上級控制器中的變量也能夠在下級控制器中被獲取到:
controller:
function ParentCtrl($scope) {
$scope.test = "測試";
$scope.cover ="覆蓋測試";
}
function ChildCtrl($scope) {
$scope.cover ="子覆蓋測試";
var test = $scope.test; //「測試」
}
html:
<div ng-controller="ParentCtrl">
<p>Parent-test : {{test}}</p>
<p>Parent-cover : {{cover}}</p>
<div ng-controller="ChildCtrl">
<p>Child-test : {{test}}</p>
<p>Child-cover : {{cover}}</p>
</div>
</div>
我在父控制器ParentCtrl中聲明的test變量並未在子控制器ChildCtrl中作聲明,而在ChildCtrl做用範圍內的Child-test 中,test卻輸出了」測試」;基於此我再作了一次覆蓋測試,檢測結果顯示,當父子控制器同時存在相同的變量時, 父子控制器各自範圍內的值不會被覆蓋;
2) this 中的變量則只適用於當前控制器:
controller:
function ParentCtrl($scope) {
var vm = this;
vm.test = "測試";
vm.cover ="覆蓋測試";
}
function ChildCtrl($scope) {
var vm = this;
vm.cover ="子覆蓋測試";
}
html:
<div ng-controller="ParentCtrl as parent">
<p>Parent-test : {{parent.test}}</p>
<p>Parent-cover : {{parent.cover}}</p>
<div ng-controller="ChildCtrl as child">
<p>Child-test : {{child.test}}</p>
<p>Child-cover : {{child.cover}}</p>
</div>
<div ng-controller="ChildCtrl as parent">
<p>Child-test : {{parent.test}}</p>
<p>Child-cover : {{parent.cover}}</p>
</div>
</div>
在使用this的時候,各層級變量的命名空間是平行的狀態,模板html中只能夠拿到當前控制器下聲明的變量。
controller:
function CCtrl($scope) {
var vm = this;
$scope.logThisAndScope = function() {
console.log(vm === $scope)
}
}
vm與$scope其實是不相等的,在console中咱們能夠看到
vm: Constructor;
$scope: $get.Scope.$new.Child;
而在$scope中又包含了一個變量vm: Constructor
$scope: {
...,
vm: Constructor,
...
}
那麼咱們能夠整理以下:
$scope 當控制器在寫法上造成父子級關係時,子級沒有的變量或方法父級會自動強加在子級身上,子級能夠任意獲取到當前父級的變量或方法,該種形式是不可逆的,即父級沒法經過$scope獲取到子級的任意變量或方法。
this 則像一個獨立的個體,全部東西都是由本身管理,也就不存在父子級變量混淆關係了。
那數據共享該如何進行呢?數據業務邏輯我以爲仍是交給更專業的service來處理吧。
兩種方式其實在性能上並沒有優劣之分,只有代碼習慣的選擇。
這或許能夠取決於咱們觀察的角度,其實能夠理解爲私用跟公用的區別!
$event服務就是對原生事件的封裝,但在ng中,$event服務的意義更主要是在parent-child控制器中進行通訊。
In cases where the components of our web application are loosely connected, such as when we require user authentication and handle authorization, it’s not always feasible to handle the immediate communication without coupling our components together.
For example, if our back end responds to a request with a status code of 401 (indicative of an unauthorized request), we expect that our web app won’t allow our user to stay connected to the current view. In this case, we’d want our app to redirect the user to a login or signup page.
Given this logic, we cannot tell our controllers to set a new location from the outside. We also want this specific functionality to space across multiple scopes so we can protect multiple scopes using the same behavior. We need another way to communicate between them.
Angular’s scopes are hierarchical in nature: They can naturally communicate back and forth through parent-child relationships. Oftentimes, however, our scopes don’t share variables, and they often perform completely different functions from each other, regardless of their place in the parent tree.
For these cases, we have the ability to communicate between our scopes by propagating events up and down the chain(經過事件傳播在不一樣的做用域上進行通訊)。
Just as the browser responds to browser-level events, such as a mouse click or a page scroll, our Angular app can respond to Angular events(angualr應用能夠響應angualr事件). This fact gives us the advantage of being able to communicate across our application inside nested components(在不一樣的組件之間) that are not built with other
Note that the Angular event system does not share the browser event system, meaning that, by design, we can only listen for Angular events, not DOM events on scopes.
We can think of events as snippets of information propagated across an application that generally (optionally) contain information about what’s happening inside of that application.
能夠把事件當作一段信息,這段信息包含了「發生的事件」,這段信息在angualr應用之間進行傳播。
Since scopes are hierarchical, we can pass events up or down the scope chain.
A generally good rule of thumb for choosing the event passing method that we’ll use is to look at the scope from which we’re firing the event.
If we want to notify the entire event system (thus allowing any scope to handle the event), we’ll want to broadcast downwards.
On the other hand, if we want to alert a global module (so to speak), we’ll end up needing to alert our higher-level scopes ($rootScope, for instance), and we’ll need to pass an event upwards. It’s a good idea to limit the number of notifications sent to the global level, particularly because events, although very powerful, introduce complexity into our apps.
For example, when we’re routing, the ‘global’ app state needs to know at which page the app is currently set, while, on the other hand, if we’re communicating between a tab directive to its child pane directives, we’ll need to send the event downwards
To dispatch an event to travel up the scope chain (from child scopes to parent scopes), we’ll use the $emit() function.
To pass an event downwards (from parent scopes to child scopes), we use the $broadcast() function
On the $broadcast() method, every single child scope(採用broadcast方法,每一個子做用域都會收到) that registers a listener will receive this message. The event propagates to all directives and indirect scopes of the current scope and calls every single listener all the way down. We cannot cancel events sent using the $broadcast() method.
Listening(事件監聽)
To listen for an event, we can use the $on() method. This method registers a listener for the event bearing a particular name. The event name is simply the event type fired in Angular