AngularJS(七)作用域

当HTML页面中出现ng-app和ng-controller指令时,AngularJS会自动创建作用域对象,我们只需将其注入即可。

1. 根作用域

$rootScope:AngularJS应用启动时会自动创建

2. AngularJS作用域继承

2.1 JavaScript对象继承机制

· JavaScript有两种方式构造对象:一是通过字面量创建

var obj = { name: "bob"};

· 二是通过构造方法,JS提供了几个内置的构造方法:Object、 Array、 String等

function Person(name) {

this.name = name;

}

var person = new Person("bob");

每个JS构造方法都有一个名称为prototype的属性,可以指向另一个对象。当我们访问对象属性时,JS引擎会从对象的所有属性中查找该属性,如果找到就返回属性值,如果没有找到就继续从prototype属性指向的对象属性中查找,如果仍然没有找到,则会沿着prototype链一直查找下去,直到prototype链结束或找到对象位置。


除了使用prototype实现继承外,还可以使用apply、call方法实现继承。由于JavaScript构造方法的apply()、call()方法可以改变对象构造中“this”的上下文环境,使特定的对象实例具有对象构造中所定义的属性、方法,因此我们可以使用apply()、call()方法实现JavaScript对象的继承。

function Person(name){

    this.name = name;

}

function Student(name, love) {

    //Person.apply(this,name);

    Person.call(this,name);

    this.love = love;

}

var student = new Student('bob','pingpong');

上例中,Student对象继承了Person对象的name属性。apply()和call()方法的不同之处在于apply()方法只接受两个参数,第二参数为数组,call()方法可以接收多个参数。

此外,JavaScript中,一个对象可以继承另一个对象的属性。

function Person(name) { this.name = name; }

var person = new Person('bob'); var student = Object.create(person); student.love = "pingpong";

Object.getPrototypeOf(student);  // Person

总结一下,JavaScript中有三种对象继承方式:

· prototype     Cat.prototype = new Animal();

· apply()/call()方法    在构造方法中调用这两个方法: Person.call(this, name);

· Object.create(person);

2.2 AngularJS作用域对象原型继承

AngularJS作用域对象继承为JavaScript的第一种方式。AngularJS作用域构造方法中提供了一个$new()成员方法,用于创建子作用域。

var parent = $rootScope;

var child = parent.$new();

<div ng-app>

    <div ng-controller="OuterController">

        <div ng-controller="InnerController">

        </div>

    </div>

</div>

AngularJS框架遍历DOM元素,查找到ng-app指令时,创建$rootScope作用域。然后AngularJS框架查找到第一个ng-controller指令,指向名称为OuterController的控制器,并调用$rootScope.$new()方法,以原型继承的方式创建$rootScope作用域的子作用域对象($scope1)。当OuterController构造方法接收一个名为$scope的参数时,AngularJS实例化控制器对象时会把$scope1对象注入控制器对象中。

接下来继续遍历,以同样的方式创建$scope1的子作用域,然后将其注入控制器对象中。


3 作用域高级特性

3.1. $watch方法监视作用域

<script type="text/javascript">

    angular.module("wacthModule",[])

        .run(["$rootScope", function($rootScope) {

            $rootScope.count = 0;

            $rootScope.name = "bob";

            $rootScope.$watch("name", function() {

                $rootScope.count++;

            })

        }]);

</script>

使用作用域对象的$watch()方法对$rootScope中的name属性进行监视。在AngularJS内部,每当我们对ng-model绑定的name属性进行一次修改时,AngularJS内部的$digest循环就会运行一次,并在运行结束之后检查我们使用$watch()方法来监视的内容,如果和上一次进行$digest之前相比有了变化,就执行$watch()方法绑定的处理函数。

在AngularJS作用域对象的$watch()方法中,对基本类型和引用类型的操作有所不同。基本类型的监视与上面的例子一样,引用类型有所不同。

<script type="text/javascript">

angular.module("wacthModule",[])

    .run(["$rootScope", function($rootScope) {

        $rootScope.count = 0;

        $rootScope.items = [{ "value" : 1},{ "value" : 2}];

        $rootScope.$watch("items", function() {

            $rootScope.count++;

         })

    }]);

</script>

$watch()方法在对待基本类型和引用类型时会有不同的处理方式,这时需要介绍$watch()方法的第三个参数,第三个参数默认情况下为false。在默认情况下,即不显示指明第三个参数或者将其指明为false时,我们进行的监视叫作引用监视(reference watch),也就是说只要监视对象的引用没有发生变化,就不算它发生变化。在上述例子中,将一个新的数组newItems赋值给items,此时才会认为监视的属性发生了变化,进行回调方法的调用。相反,只要将第三个参数设置为true,此时进行的监视就叫作“全等监视”,只要属性发生变化,就会执行相应的回调方法。

补充:

$watchCollection() :针对数组(集合)进行监视,它的性能介于全等监视和引用监视之间,即它并不会对集合中的每一项的属性进行监视,但是可以对数组的项目增减作出反应。

3.2 解除作用域监视

需要关注$watch()方法的返回值,该方法调用完毕后返回另一个方法,这时只需调用返回的方法即可解除作用域监视。

var unbindWatcher = $rootScope.$watch("num", function(newValue, oldValue) {

    if (newValue == 2){ unbindWatcher(); }

    $rootScope.count++;

});

3.3 $apply方法和$digest循环

实际上AngularJS中的双向绑定也是通过类似于$watch方法的机制实现的。

$digest循环:周期性地运行一个函数来检查scope模型中的数据是否发生了变化。在该循环中,watchers会被触发,当一个watcher被触发时,AngularJS会检测scope模型,如果它发生了变化,那么关联到该watcher的回调方法就会被调用。那么,$digest循环是在什么时候以各种方式开始的呢?

在调用了$scope.$digest()后,$digest循环就开始了。假设你在一个ng-click指令对应的事件处理方法中更改了scope的一条数据,此时AngularJS会自动地通过调用$digest()来触发一轮$digest循环。当$digest循环开始后,它会触发每个watcher,由每一个watcher对属性进行检查,判断是否执行回调函数。

当AngularJS作用域中的模型数据发生变化时,AngularJS会自动触发$digest循环,从而达到自动更新视图的目的。但在有些情况下,模型数据修改后需要我们手动调用$apply()方法来触发$digest循环。例如使用JavaScript的setTimeOut()方法来更新一个模型数据,AngularJS框架就没有办法知道我们修改了什么,也就无法触发$digest循环。这个时候,只要将setTimeOut()方法中的代码移到一个匿名方法中,然后把该匿名方法作为$apply()方法的参数。

setTimeOut(function() {

    $scope.$apply(function() {

        $scope.message = "信息内容";

    });

});

3.4 $timeout 与 $interval服务介绍

$timeout 与$interval服务分别是为解决JavaScript中的setTimeOut() 与 setInterval()不能自动触发watcher的问题的方案。使用这两个服务就能够达到JavaScript中的效果,同时还能自动触发$digest循环。

$timeout(function(){}, 3000);


4 作用域事件路由与广播

AngularJS作用域支持两种事件传播方式:

· 事件从子作用域路由到父作用域中

· 事件从父作用域广播到所有子作用域

相关的方法有$on()、$emit()、$broadcast()

4.1 $emit方法实现事件路由

该方法用于实现事件从子作用域路由到父作用域中,$emit()方法的第一个参数为事件名称,后面可以传入一个或多个参数,这些参数能够被传递到父作用域注册的事件监听器中,$emit()方法使用如下:

$scope.$emit("infoEvent", {name:"bob", age: 12});

消息发送出去后,我们可以在父作用域中调用AngularJS作用域对象的$on()方法,注册一个事件监听器监听子作用域路由的事件:



2. $broadcast方法实现事件广播

所有注册了实践监听器的子作用域就能接收到父作用域的广播事件。

$scope.$broadcast("infoEvent", {name:"bob"});

3. 作用域对象$on方法详解

$on方法用于注册一个事件监听器,该方法接收两个参数,第一个参数是要监听的事件名称,第二个参数是事件处理方法。

$scope.$on("infoEvent", function(event, data) {});

event 参数为事件对象,第二个参数data为调用$emit()或$broadcast()方法传递的数据。需要注意的是,event事件对象具有一些使用的属性和方法。

· event.name: 事件的名称

· event.targetScope: 事件源作用域对象

· event.currentScope: 当前作用域对象

· event.stopPropagation(): 停止事件的进一步传播。该方法只对向父作用域路由事件起作用,当在某个事件监听处理方法中调用事件对象的stopPropagation()方法后,事件不会再向上级父作用域路由。

· event.preventDefault(): 将defaultPrevented属性设置为true,直到事件监听器的实现者采取行动之前才会检查defaultPrevented的值。

· event.defaultPrevented: 如果调用了event.preventDefault()方法,那么该属性将被设置为true。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352