壹 ❀ 引html
初學angularjs的同窗對於$scope必定不會陌生,scope(做用域)是將view(視圖)與model(模板)關聯起來的橋樑,經過controller(控制器)對於model的數據操做,咱們能輕易實現雙向綁定,這是一個簡單的例子:angularjs
<body ng-controller="myCtrl"> <input type="text" ng-model="name"> <div>{{name}}</div> </body>
angular.module('myApp', []) .controller('myCtrl', function ($scope) { $scope.name = '聽風是風'; });
隨着對於angularjs的深刻學習,咱們知道原來在angularjs版本1.2以後,數據除了綁定scope還能綁定this上,像這樣:segmentfault
<body ng-controller="myCtrl as vm"> <input type="text" ng-model="vm.name"> <div>{{vm.name}}</div> </body>
angular.module('myApp', []) .controller('myCtrl', function ($scope) { this.name = '你們好,我是聽風是風'; });
從視覺角度來看,this好像也達到了scope的做用,那它兩真的就等同嗎?兩者有什麼區別呢?在第二個例子中,myCtrl as vm又是什麼意思?本文就此展開探討。函數
貳 ❀ controller as作了什麼性能
若是隻是將數據綁定在this上,ng-controller不使用ctrl as的寫法,你會發現this上的數據在視圖中是沒法被識別的。咱們都知道控制器controller是一個構造函數,這裏的controller as vm其實就是實例化了一個叫vm的實例而已,相似於這樣:學習
class myCtrl { constructor() { this.name = '聽風是風'; }; sayName() { console.log(this.name); } } let vm = new myCtrl(); vm.sayName() //聽風是風
這也是爲何非得經過vm才能訪問controller控制器中this屬性的緣由;另外,as vm的vm也只是一個實例名而已,隨便你取什麼名字都是OK的,並非硬性要求。ui
叄 ❀ $scope與this有何區別this
1.含義不一樣:spa
每一個控制器controller都有一個關聯的$scope對象,控制器(構造函數)負責在其關聯的做用域($scope)上設置模型(model)屬性和行爲。而視圖只能訪問在此對象和父做用域對象($scope)上定義的屬性方法。3d
而this就有點不一樣,瞭解Javascript的同窗都知道,this指向實際上是一個不太肯定的東西,在你不知道this直接調用者是誰,你也沒法判斷this指向誰,在angular中也是如此。
在angular中當你調用控制器的構造函數時,this就會指向控制器,好比前面咱們提到ctrl as vm。而當你調用$scope上的方法時,this指向當前控制器的有效做用域。
<body ng-controller="myCtrl as vm"> <button ng-click="vm.demo1()">ctrl的this</button> <button ng-click="demo2()">scope的this</button> <button ng-click="demo3()">$scope中的this就是當前做用域</button> </body>
angular.module('myApp', []) .controller('myCtrl', function ($scope) { this.demo1 = function () { console.log(this); }; $scope.demo2 = function () { console.log(this); }; $scope.demo3 = function () { console.log(this === $scope); }; });
上述例子中,咱們分別將方法綁定在this上與$scope上分別輸出this,以及判斷綁定在$scope時this是否等同於當前控制器的做用域,根據結果咱們也驗證了前面的結論,this可能等於當前scope,也可能不等於。
固然通常狀況下,咱們會認爲this和$scope不是同一個東西,在咱們使用ctrl as vm時,其實只是在$scope中添加了一個key名爲vm的對象屬性:
<body ng-controller="myCtrl as vm"> </body>
angular.module('myApp', []) .controller('myCtrl', function ($scope) { this.name = '聽風是風'; this.sayName = function () { console.log(1); }; console.log($scope); });
因此雖然this可能會等於$scope,但實例vm始終不會等於當前$scope,這點須要注意。另一提的是在controller中常有使用let vm = this的作法,vm與this的關係也跟this指向有關,以下:
angular.module('myApp', []) .controller('myCtrl', function ($scope) { let vm = this vm.demo1 = function () { console.log(this === vm); // true }; $scope.demo3 = function () { console.log(this === $scope); // true console.log(vm === $scope); // false console.log(vm, this); // {demo1: ƒ} ChildScope{...} }; });
2.做用範圍不一樣
若是說$scope已經能解決平常開發需求,那爲什麼還要推出新的ctrl as vm的寫法呢,其主要的一點,就是爲了解決scope繼承致使做用域混亂的問題。在下面的例子中,即便子做用域沒有聲明name屬性,同樣能繼承來自父做用域的name:
<body ng-controller="parentCtrl"> <span>{{name}}</span> <div ng-controller="childCtrl"> <span>{{name}}</span> <span>{{age}}</span> </div> </body>
angular.module('myApp', []) .controller('parentCtrl', function ($scope) { $scope.name = '聽風是風'; }) .controller('childCtrl', function ($scope) { $scope.age = 26; });
在代碼結構比較複雜的狀況下,你每每很難區分這個name屬性來自於哪裏,而ctrl as正好解決了這個問題,下面的例子相較上方是否是看着更清晰呢:
<body ng-controller="parentCtrl as parent"> <span>{{parent.name}}</span> <div ng-controller="childCtrl as child"> <span>{{parent.name}}</span> <span>{{child.age}}</span> </div> </body>
angular.module('myApp', []) .controller('parentCtrl', function ($scope) { this.name = '聽風是風'; }) .controller('childCtrl', function ($scope) { this.age = 26; });
肆 ❀ 使用this與$scope的坑
在介紹完this與$scope的區別後,在平常開發中什麼時候使用$scope與this也有些注意的地方,這裏我列舉兩處你們可能會忽略的點:
1.directive中scope屬性爲true時$scope與this表現不一樣
咱們都知道在自定義指令directive開發中,提供了一個scope屬性,值分爲false(不建立做用域),true與(建立做用域但不隔離)一個對象{}(建立隔離做用域)。
值爲false的表現爲,子會繼承父做用域的屬性,不管父子誰修改此屬性,雙方都會同步;
true的表現爲,子會繼承父做用域的屬性,但只有修改父時子會同步,經過子修改此屬性,父並不會改變。{}表示子建立隔離做用域,即子不會繼承父任何屬性。
上面三種狀況的描述其實都是值綁定在$scope上的狀況,若是值綁定在this上,scope值爲false或{}時,$scope.name與this.name表現一致,惟獨scope值爲true時,若是你將值綁定在this上,修改子也會影響到父,直接看個例子:
<body ng-controller="myCtrl as vm"> scope: <input type="text" ng-model="name1"><br> this: <input type="text" ng-model="vm.name2"> <div echo></div> </body>
angular.module('myApp', []) .controller('myCtrl', function ($scope) { let vm = this $scope.name1 = '時間跳躍'; vm.name2 = '聽風是風'; }) .directive('echo', function () { return { restrict: 'EACM', scope: true, replace: true, template: '<div>scope:<input type="text" ng-model="name1"></br>this:<input type="text" ng-model="vm.name2"></div>', } })
其實仔細一想,咱們原本就是在設置directive的scope繼承方式,this不符合這個規則也是情理之中,那爲何咱們還能在子做用域使用父做用域中this的值呢,在子中輸出一下$scope就明白了:
在子做用域中經過$parent訪問父做用域,能夠看到vm對象做爲父做用域中的一條屬性存在,子修改父屬性,相似淺拷貝的道理,讓父也發生了改變。
2.directive中require只能訪問controller中綁在this上的屬性方法
咱們知道自定義指令的require屬性能將其餘指令的controller注入到自身,這樣就能夠直接使用其它指令controller中定義過的方法屬性,但前提是這些方法屬性是定義在this上,而非$scope上:
<body ng-controller="myCtrl as vm"> <div echo></div> </body>
angular.module('myApp', []) .controller('myCtrl', function ($scope) {}) .directive('echo', function () { return { restrict: 'EACM', template: '<span><echo1></echo1></span>', controller: function ($scope) { $scope.name = '聽風是風'; this.sayName = function (name) { console.log('個人名字是' + name); } } } }) .directive('echo1', function () { return { restrict: 'EACM', require: '^echo', link: function (scope, ele, attr, ctrl) { console.log(ctrl); } } })
能夠看到在指令echo1中link函數的第四個參數ctrl只能訪問到sayName方法,致使這個狀況的緣由是在angularjs源碼中,require所作的操做也是實例化了一個控制器實例,非this屬性都沒法添加到實例上,這一點也是在自定義指令開發中須要注意的。
伍 ❀ 總
那麼到這裏,咱們瞭解了ctrl as這種寫法的含義,而且知道了angualrjs中$scope與this的區別,this可能與$scope相等,當使用ctrl as vm時,vm只是成爲了$scope中的一條屬性,因此vm與$scope永遠不相等。
咱們還了解了在自定義指令開發中,爲scope與this添加值時會帶來不一樣的影響,若是你對於angular 自定義指令開發有興趣,歡迎閱讀博主 angularjs 一篇文章看懂自定義指令directive 這篇文章。
另外我看了一眼文章配圖中標誌性的摩托車,才反應過來這是電影阿基拉的同人做品,裏面的兩我的物分別是男主金田正太郎與男二女朋友香織(男二帽子戴好..),這輛摩托在電影頭號玩家中也有做爲彩蛋出現,那麼到這裏,本文結束。
參考
'this' vs $scope in AngularJS controllers