angularjs $scope與this的區別,controller as vm有何含義?

 壹 ❀ 引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

Angular路由中的controller經常有as vm, 有何做用?

controller as引入的意義和方法?

相關文章
相關標籤/搜索