这又是一篇历史遗留博文。
去年学习angularjs时controller用的都是$scope,在$scope下进行数据双向绑定,那时的angular版本号已记不清了,今年做项目时(angular v1.3.13),看到官网文档里的用法是ng-controller = "controllerName as XXX"
,然后controller.js中用了类似下面的代码:
var vm = this;
this.avalue = 'XXX';
觉得好奇为什么会引入这种用法,于是研究了一下。
$scope和this究竟是什么
v1.2.0之前的angular:
// <div ng-controller="MainCtrl"></div>
app.controller('MainCtrl', function ($scope) {
$scope.title = 'Some title';
});
v1.2.0之后的:
app.controller('MainCtrl', function () {
var vm = this;
vm.title = 'Some title';
});
让我们回忆一下js中的构造函数和实例,也就是“类”:
var myClass = function () {
this.title = 'Class title';
}
var myInstance = new myClass();
现在我们可以使用myInstance示例来访问myClass的属性和方法。
在angular中:
// we declare as usual, just using the `this` Object instead of `$scope`
app.controller('MainCtrl', function () {
var vm = this;
vm.title = 'Some title';
});
<div ng-controller="MainCtrl as main">
// MainCtrl doesn't exist, we get the `main` instance only
{{ main.title }}
</div>
app.controller('MainCtrl',function(){})
就是在定义一个名称为MainCtrl的构造函数,MainCtrl as main
就是在实例化,生成MainCtrl的实例main,然后就可以在main中访问MainCtrl里定义的变量和函数。
但是,Controller as的语法是将controller绑定了当前的$scope,而不是都绑向同一个$scope变量。
注意事项
一些scope的监听器和方法,例如:$watch, $broadcast, $on等,只能在$scope下调用。
controller as应用方式
除上面的方式外,还有:
-
directive:
app.directive('myDirective', function () { return { restrict: 'EA', replace: true, scope: true, template: [].join(''), controllerAs: '', // woohoo, nice and easy! controller: function () {}, // we'll instantiate this controller "as" the above name link: function () {} }; });
-
$routeProvide
app.config(function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'views/main.html', controllerAs: '', controller: '' }) .otherwise({ redirectTo: '/' }); });
-
ui-rooter
app.config(function ($routeProvider) { $stateProvider .state('tab.history', { url: '/history', views:{ 'personInfo':{ templateUrl: 'history.html', controller: 'historyCtrl as history' } } }) })
controller as让继承关系的变量之间更清晰可读
感受下:
-
老版引用同名的各自的变量:
<div ng-controller="MainCtrl"> {{ title }} <div ng-controller="AnotherCtrl"> {{ title }} <div ng-controller="YetAnotherCtrl"> {{ title }} </div> </div> </div>
-
新版引用同名的各自的变量:
<div ng-controller="MainCtrl as main"> {{ main.title }} <div ng-controller="AnotherCtrl as another"> {{ another.title }} <div ng-controller="YetAnotherCtrl as yet"> {{ yet.title }} </div> </div> </div>
在感受下
-
老版引用父变量
<div ng-controller="MainCtrl"> {{ title }} <div ng-controller="AnotherCtrl"> Scope title: {{ title }} Parent title: {{ $parent.title }} <div ng-controller="YetAnotherCtrl"> {{ title }} Parent title: {{ $parent.title }} Parent parent title: {{ $parent.$parent.title }} </div> </div> </div>
-
新版引用父变量
{{ main.title }}Scope title: {{ another.title }} Parent title: {{ main.title }}Scope title: {{ yet.title }} Parent title: {{ another.title }} Parent parent title: {{ main.title }}
controller as 方式解决了父子$scope带来的混乱
具体问题可以看AngularJS: “Controller as” or “$scope”?。
$scope方式下:
<div ng-controller="ParentController">
ParentController: <input type="text" ng-model="foo" />
<div ng-controller="ChildController">
ChildController: <input type="text" ng-model="foo" />
</div>
</div>
<script>
app
.controller('ParentController', function ($scope) {
$scope.foo = "bar";
})
.controller('ChildController', function ($scope) { /*empty*/ });
</script>
会产生父变字变,子边父不变的现象。这是由于原型链的缘故,当一个对象的原型链上有属性foo,你再给这个对象赋上一个属性foo,并不会它本来原型链上的属性foo。只是新建的一个foo,在原型链的更近端,访问这个属性时不会在去寻找更远处的foo属性。
这就相当于:
var obj1 = {
someProp: 'obj1 property!',
someMethod: function () {
alert('obj1 method!');
}
};
var obj2 = Object.create(obj1);
obj2.someProp = 'obj2 property!';
因此在父子继承关系下的属性必须明确指出对象已防止混乱。譬如我们要访问父对象的foo属性应该:
<div ng-controller="ParentController">
ParentController: <input type="text" ng-model="foo" />
<div ng-controller="ChildController">
ChildController: <input type="text" ng-model="$parent.foo" />
</div>
</div>
<script>
app
.controller('ParentController', function ($scope) {
$scope.foo = "bar";
})
.controller('ChildController', function ($scope) { /*empty*/ });
</script>
也可以借助对象传引用的方式来变态实现(个人觉得这个方法实在是曲线救国,但是controller as正是利用了这个原理):
<div ng-controller="ParentController">
ParentController: <input type="text" ng-model="obj.foo" />
<div ng-controller="ChildController">
ChildController: <input type="text" ng-model="obj.foo" />
</div>
</div>
<script>
app
.controller('ParentController', function ($scope) {
$scope.obj = {
foo: "bar"
};
})
.controller('ChildController', function ($scope) { /*empty*/ });
</script>
使用controller as方式可以简化代码,让关系更清晰:
<div ng-controller="ParentController as parent">
ParentController: <input type="text" ng-model="parent.foo" />
parent.foo: {{ parent.foo }}
<div ng-controller="ChildController as child">
ChildController: <input type="text" ng-model="parent.foo" />
parent.foo: {{ parent.foo }}
</div>
</div>
<script>
app
.controller('ParentController', function () {
this.foo = "bar";
})
.controller('ChildController', function () { /*empty*/ });
</script>
controller as的实质
上面的代码已经可以推测实质了,controller as只是语法糖,
app.controller('MyController', function () {
this.someValue = "Hello!";
});
等价于:
app.controller('MyController', function ($scope) {
$scope.myController = this;
this.someValue = "Hello!";
}
除了在父子contrllor之间,controllerAs的写法能比较方便的切换父子对象,其他的只要遵守不把变量直接挂在$scope上,而是挂在$scope的对象上,那么继续用$scope也未尝不可,就是代码量上多一些 objCtrl as obj和this.namej和this.namej和this.name = ‘lili’也是angularjs自己内部创建了obj的对象挂在$scope上,即:$scope.obj = { name=’lili’ }
以上是我初次接触controller as时的想法,当初并不当回事。但是后来我将所有业务代码都用了controller as方式,语法糖也是很必要的,当代码量大的时候,语法糖节省的是你的时间,提高的是你的效率,理清的是你的思路。