今天终于拿到了《精通angular js》这本书,学长们力推的一本书,看了一天,豁然开朗,以下是对angular js中作用域的一些理解。
作用域1.0
控制器与$scope
了解作用域,首先要理解angular js的控制器。控制器的主要职责是初始化作用域实例,那么问题来了,什么是作用域实例呢?学过Java的人都知道,Java中可以将很多方法和需要的属性封装到一个类中,方便其他类调用,在我个人看来,angular js里面就定义了一个scope类,而我们在html里面使用ng-controller标签(这个标签是由指令(Directives)定义的)时,会自动创建一个scope类的实例。这里就从angular js的运行来谈,由于我之前的学习时针对的Java和c语言,所以就拿Java来做对比,在dom加载完后,浏览器发现ng-app标签后,会先创建一个根作用域$root(这个root可以看成是Java中的类继承的根类),然后看到了ng-controller,于是又创建一个$scope(此时的$scope完全继承自$root,也就是说可以用$root的所有属性),也就是说在一个标签内的另一个标签如果创建作用域,如果没指明父作用域,那么它们之间就有个继承关系,说到这里,再回到控制器来,它的职责有个亮点“初始化”,也就是说控制器其实就是用来给$scope的属性赋值(这里的值可为数组,对象等,这个是js特有的特点)和方法。
ng-repeat怎么创建作用域
ng-repeat我在第一次接触时就喜欢上了它,因为有了它,我可以很简单的实现很多想法,它的作用是重复创建所在的标签,这个时候问题又来了,它这个语法"XXX in XXXs"(后面那个就表示复数啊)按照我上面说的原则,对应每个XXX,都有个新变量要暴露给$scope,而又不能覆盖之前的变量值,这个时候同名(XXX)的属性怎么创建作用域(属性也有自己的作用域,因为js的特点,属性也可以为对象等),angular js为了解决这个问题,使用了一个叫“模型值”(model values)的东东,用以辨别同名的属性作用域(也就是3个数字,如001)。
尝试
对于以上的学习,我做了如下的尝试来验证:
test.html
<span style="font-size:18px;"><html >
<head>
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="test.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body ng-app ng-init="appName='app'">
$root.appName={{appName}}
<div ng-controller="aCtrl">
<hr>
控制器作用域<br>
使用父作用域的appName:<input ng-model="appName">
$root.appName:{{appName}}
<br/>对names进行ng-repeat,给集合每个元素创建一个叫a的属性作用域
<div ng-repeat="a in names">
<br/>
使用a获取:{{a}}<br/>
使用a.name获取:{{a.name}}<br>
</div>
</div>
</body>
</html></span>
test.js
<span style="font-size:18px;">var aCtrl = function($scope){
$scope.names = [
{name:"1"},
{name:"2"},
];
};</span>
现在来看看浏览器的效果
下面是ng-app($root)的作用域, 可以看到我定义的属性appName。
再来看ng-controller($scope)的作用域,它里面也可以看到$root的appName,而且多了个属性names(这是我通过js给$scope创建的属性)
下面再来看看ng-controller中通过ng-repeat创建的属性作用域
第一个属性作用域,在里面有父作用域中的appName和names,而且又多了个a的属性,注意names集合中对象的值里多了个我在js里没有定义的$$hashKey,这个东西应该就是model values。
第二个属性作用域也就大同小异了,可以看到唯一的区别就是$$hashKey的值
既然子作用域可以用父作用域的值,那我就不免想到用子作用域更改值,会产生什么影响,以下是改变后浏览器的变化
因为我的input标签与appName实现了双向数据绑定,可以看到我更改appName后,分割线上的{{appName}}无改变,而ng-controller($scope)中的{{appName}}改变了。
来看看这个时候ng-app($root)的作用域,很明显appName没有改变,那么ng-controller($scope)里的appName为什么变了呢?
带着疑问,我又看了ng-controller($scope)的作用域,没错,里面的appName改变了,再做了相关的查阅后,总结出一个原因:ng-controller生成的作用域创建了新的appName变量,而不是去修改$root中设定的appName的值,而如果想要修改,可以利用<input ng-model="$parent.appName">明确的引用父作用域来实现。
以上是这篇笔记中的所有尝试。
TIP
anguler js中官方定义的属性前都有个$符号,所以呢,我们创建自己的变量时就最好不要加$了,这样也方便查看,然后Firefox有个查看angular js作用域的工具,叫做Angscope,很有用,我就拿它来验证自己对作用域的猜想。
未完待续(以上纯属个人感悟,仅供参考)......