深入理解AngularJs-scope(二)
深入理解AngularJs-scope(一)中,我们对AngularJs的脏检测及其触发、异步任务队列进行了学习。紧接上一篇文章 深入理解AngularJs-scope(一),我们来看看scope对以下两个特性的实现。
- scope的继承机制和 isolated scope;
- 依赖于scope的事件系统: $on, $broadcast, $emit;
scope的继承机制
在上一篇文章中,我们创建了scope类,并在scope.prototype上实现了脏检测和异步任务相关的各个方法。
现在,我们来看看AngularJs中,scope之间是如何通过继承联系在一起的,如何从parentScope上获取properties。实际上,得益于javascript的原型继承机制,要实现scope的继承相当的简单,代码如下:
Scope.prototype.$new = function(isolated, parent) {
var child;
parent = parent || this;
if(isolated) {
child = new Scope();
child.$root = parent.$root;
child.$$asyncQueue = parent.$$asyncQueue;
child.$$postDigestQueue = parent.$$postDigestQueue;
child.$$applyAsyncQueue = parent.$$applyAsyncQueue;
} else {
var ChildScope = function() {};
ChildScope.prototype = this;
child = new ChildScope();
}
parent.$$children.push(child);
child.$$watchers = []; // shadow这个prop,使成为每个scope独立拥有这个prop
child.$$listeners = {}; // shadow这个prop, 存储包含自定义事件键值对的对象
child.$$children = []; // shadow这个prop,使成为每个scope独立拥有这个prop
child.$parent = parent; // 缓存parentScope, 以便让scope上的其他method能够使用它,比如$destroy
return child;
};
在我们使用AngularJs进行开发时,$new方法的调用无处不在。大部分情况下并不需要我们手动调用,只是指令自己做了创建scope的工作。
$new方法有两个参数:
isolated-布尔值,表示新建的scope是不是 isolated scope(孤立的scope)。
parent-支持传入一个其他scope来作为 AngularJs scope机制中的parentScope。
AngularJs中的scope分为两种,一种是普通scope,如上面代码13行所示,普通scope的prototype指向parentScope, 故能够通过原型链获取到parentScope上的properties。
另一种是isolated(孤立) scope, isolated是通过 Scope构造函数创建,它的protorype是指向scope构造函数的,并不是parentScope,所以不能从原型链访问parentScope的properties。
代码19行至22对新scope的$$watchers、$$listeners、$$children、$parent进行了初始化,因为这些属性是每个scope实例自己拥有、自己维护的。
scope的事件系统:订阅/发布
AngularJs 也为开发者提供了一套事件系统供开发者进行事件的绑定和触发,基于 publish/subscribe 设计模式。其中包含3个核心方法:$on, $emit, $broadcast。
$on: 在scope上绑定自定义事件,即向scope的$$listeners数组中插入listener回调函数。返回事件销毁函数。
Scope.prototype.$on = function(eventName, listener) {
var listeners = this.$$listeners[eventName];
if(!listeners) {
this.$$listeners[eventName] = listeners = [];
}
listeners.push(listener);
return function(eventName) {
var index = listeners.indexOf(listener);
if(index >= 0) {
listeners[index] = null;
}
};
};
$emit: 沿着scope -> parentScope 向上发射事件,执行对应的回调函数。
Scope.prototype.$emit = function(eventName) {
var propagationStopped = false;
var event = {
name: eventName,
targetScope: this,
stopPropagation: function() {
propagationStopped = true;
},
preventDefault: function() {
event.defaultPrevented = true;
}
};
// 把event和additionalArgs拼接成新数组,通过apply方法传入listener, 使参数获取方式正常
var listenerArgs = [event].concat(_.tail(arguments));
var scope = this;
do {
event.currentScope = scope;
scope.$$fireEventOnScope(eventName, listenerArgs);
scope = scope.$parent; // 通过改变scope引用 实现向上传播的关键代码
} while (scope && !propagationStopped);
event.currentScope = null;
return event;
};
$broadcast: 向下广播事件,并触发对应的回调函数。$broadcast有一点特殊,一旦开始向下广播,就不能中断。
Scope.prototype.$broadcast = function(eventName) {
var event = {
name: eventName,
targetScope: this,
preventDefault: function() {
event.defaultPrevented = true;
}
};
// 把event和additionalArgs拼接成新数组,通过apply方法传入listener, 使参数获取方式正常
var listenerArgs = [event].concat(_.tail(arguments));
this.$$everyScope(function(scope) {
event.currentScope = scope;
scope.$$fireEventOnScope(eventName, listenerArgs);
return true;
});
event.currentScope = null;
return event;
};
这两篇用到的工具函数我都放在后面,由于事件系统的代码比较简单,就不再做过多说明。
工具方法1: $$fileEventOnScope
/* $emit 和 $broadcast中 提取出的 fire event listener函数
angularjs源码没有这个方法,其中只是重复了这些代码, 本书作者提出了重复代码
*/
Scope.prototype.$$fireEventOnScope = function(eventName, listenerArgs) { var listeners = this.$$listeners[eventName] || [];
var i = 0; while(i < listeners.length) {
if(listeners[i] === null) {
listeners.splice(i, 1);
} else {
try {
listeners[i].apply(null, listenerArgs);
} catch(e) {
console.error(e);
}
i++;
}
} return event;
};
工具方法2: $$everyScope
/* 为使$digest循环能够递归child scope上的watchers的工具方法
这个方法还用于实现$broadcast
*/
Scope.prototype.$$everyScope = function(fn) {
if(fn(this)) {
return this.$$children.every(function(child) {
return child.$$everyScope(fn);
});
} else {
return false;
}
};
总结:
这两篇文章提供了与scope相关的脏检测,$watch, 异步任务,继承机制,事件系统的代码及一点补充分析。弄明白了这些机制是如何实现的,当你在开发工作中用到这些东西时,一定会多一份自信,多一份游刃有余。希望这两篇文章能够帮助到正在使用angular1.x开发的朋友。如有错误,请不吝指出~!谢谢~
深入理解AngularJs-scope(二)的更多相关文章
- 深入理解AngularJs-scope(一)
进入正文前的说明:本文中的示例代码并非AngularJs源码,而是来自书籍<<Build Your Own AngularJs>>, 这本书的作者仅依赖jquery和lodas ...
- 理解AngularJS生命周期:利用ng-repeat动态解析自定义directive
ng-repeat是AngularJS中一个非常重要和有意思的directive,常见的用法之一是将某种自定义directive和ng-repeat一起使用,循环地来渲染开发者所需要的组件.比如现在有 ...
- 转: 理解AngularJS中的依赖注入
理解AngularJS中的依赖注入 AngularJS中的依赖注入非常的有用,它同时也是我们能够轻松对组件进行测试的关键所在.在本文中我们将会解释AngularJS依赖注入系统是如何运行的. Prov ...
- 理解AngularJS中的依赖注入
点击查看AngularJS系列目录 理解AngularJS中的依赖注入 AngularJS中的依赖注入非常的有用,它同时也是我们能够轻松对组件进行测试的关键所在.在本文中我们将会解释AngularJS ...
- AngularJS Scope(作用域)
1. AngularJS Scope(作用域) Scope(作用域) 是应用在 HTML (视图) 和 JavaScript (控制器)之间的纽带. Scope 是一个对象,有可用的方法和属性. Sc ...
- 理解ThreadLocal(之二)
想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码 ...
- AngularJS进阶(二十七)实现二维码信息的集成思路
AngularJS实现二维码信息的集成思路 赠人玫瑰,手留余香.若您感觉此篇博文对您有用,请花费2秒时间点个赞,您的鼓励是我不断前进的动力,与君共勉! 注:点击此处进行知识充电 ...
- AngularJS学习之旅—AngularJS Scope作用域(五)
1.AngularJS Scope(作用域) Scope(作用域) 是应用在 HTML (视图) 和 JavaScript (控制器)之间的纽带. Scope 是一个对象,有可用的方法和属性. Sco ...
- 深入理解C/C++二维数组
深入理解C/C++二维数组 前言 本来以为自己对二维数组的理解还可以,没感觉有什么,但是今天小伙伴问了一个问题感觉迷惑了好久,于是决定细致的记录一下,一步一步的探究各种关于二维数组的问题,巩固基础. ...
随机推荐
- textview-显示行数限制
在代码中直接添加 android:maxLines="2" android:ellipsize="end" 跟ellipsize搭配使用,超过两行的时候,第二行 ...
- amazeui学习笔记--css(基本样式4)--打印样式Print
amazeui学习笔记--css(基本样式3)--打印样式Print 一.总结 1.打印显示url方法: 利用 CSS3 content 属性,将 <a> 和 <abbr> 的 ...
- SQl 事务增加数据
连SQl简单地事务处理多表的情况 begin transaction declare @error int declare @QID int set @error = 0 insert into HW ...
- 关于jsonp跨域的问题以及解决方法(跨域、同源与非同源)
什么是跨域? 想要了解跨域,首先需要了解下浏览器的同源机制: JSONP和AJAX相同,都是客户端向服务器端发送请求:给服务器端传递数据 或者 从服务器端获取数据 的方式 JSONP属于非同源策略(跨 ...
- python分解质因数
将一个正整数分解质因数.例如:输入90,打印出90=2*3*3*5. # !/usr/bin/env python # -*- coding:utf-8 -*- # Author:Hiuhung Wa ...
- 模拟登录QQ推断是否须要验证码
老生常谈的问题了,在模拟登录之前,推断是否须要验证码: https://ssl.ptlogin2.qq.com/check? uin=QQ号码&appid=1003903&js_ver ...
- Dynamics CRM 2015/2016 Web API:Unbound Function 和 Bound Function
今天我们来看看Dynamics CRM Web API Function 吧, 这是一个新概念,刚接触的时候我也是比較的迷糊.这种命名确实是和之前的那套基于SOAP协议的API全然联系不上.好了,不说 ...
- 【例题5-8 UVA - 400】Unix ls
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 设n个字符串中出现的最长的为len; 最后一列能容纳len个字符,然后前面的列能容纳len+2个字符. 每行最多60个字符. 按照这 ...
- 【POJ 1226】Substrings
[链接]h在这里写链接 [题意] 给你n个字符串. 让你找一个字符串s. 设s'为这个字符串的逆序. 要求s或者s'在每个字符串里都能够找得到. 并且要求s的长度最长. 求出这个最长的串的长度. [题 ...
- PDO中获取结果集
fetch()方法 fetch()方法用于获取结果集的下一行.语法例如以下: mixed PDOStatement::fetch([int fetch_style][,int cursor_orien ...