AngularJS面试题
1.与jQuery的比较
jQuery
js函数库 封装简化dom操作
使用jquery的思想是:我拥有一个DOM元素并且想让它去做某件事。也就是命令式编程思想。
angular
JS结构化框架
主体不再是DOM,而是页面中的动态数据
使用angular的思想是:我需要完成什么任务,然后接着设计你的应用,最后再去设计你的视图view层。也就是声明式编程思想。
为什么要用jQuery?
jQuery是一个非常优秀的JS框架。其宗旨是写更少的代码,做更多的事情。
它是轻量级的JS库(压缩后不到30kb),这是其他js库所不能及的;它还有出色的兼容性,能兼容CSS3,能兼容各种浏览器;能使用户更方便的操作dom,进行事件处理,实现动画效果和Ajax交互;jQuery还有个很大的优势是开源,发展好,文档很齐全,各种应用说的很详细,并且还有很多成熟的插件可供选择。最主要的是使得html页面行为层和结构层分离,便于维护和调试。
为什么要用AngularJS?
优点:
- 模板功能强大丰富,并且是声明式的,自带了丰富的Ag指令;
- 是一个比较完善的MVC框架,包括模板,数据双向绑定,路由,模块化,服务,过滤器等
- 自定义指令,比jQuery插件还灵活,但需要深入了解指令的特性
- Ng模块化引入了依赖注入,能很容易写出可服用的代码,敏捷开发比较方便。
缺点 - 验证功能比较薄弱
- Ng-view只能有一个,不能嵌套多个视图,然有 angular-ui/ui-router · GitHub 解决,但是貌似ui-router 对于URL的控制不是很灵活,必须是嵌套式的;
- 对于特别复杂的应用场景,貌似性能有点问题
- Angular 太笨重了,没有让用户选择一个轻量级的版本
- 不利于SEO。因为所有内容都是动态获取并渲染生成的,搜索引擎没法爬取
AngularJS这种框架的使用场景:
1、DOM不是第一优先级的时候。
2、考虑到效率的问题,在框架内它就支持单元测试,简化了测试上的麻烦。另外,降低模块间的耦合度也有利于程序员梳理项目逻辑,便于维护和调试。
3、考虑到声明式语言的优势。
4、高度复杂对象模型的单页面。适合CURD类型的应用。
综上所述
两者是出于不同的目的被创建的,解决的也是不同的问题。当一个项目的重点是数据展示和执行,而不是分析,此时可能AngularJS就会更胜一筹。对于框架的选择,你要考虑到很多因素,需要整体的构思。AngularJS总体上还是一个不错的选择,可以提高程序员的效率,相对减少Web开发中的维护成本。
扩展,什么是命令式编程,什么是声明式编程
jQuery的一个常见问题就是它是命令式编程,就意味着你要告诉计算机如何达成某项目的,其实你想要的就是程序运行的结果。而声明式编程则转移了重点,它只是告诉机器你想要的结果,让机器自己实现这个结果。javascript就是一种命令式语言,但HTML,和它指导的AngularJS就是声明式的,只需要告诉计算机你需要的展现形式,至于细节就由机器处理了。这样,完成一个任务的代码就大幅缩减了,代码质量也更高了。
2.双向数据绑定-Ag中DOM变化如何传播给模型,模型的变化如何触发DOM重绘?
什么是双向数据绑定?
当view中有数据变化时会更新到model,当model中数据有变化时,view也会同步更新。
工作原理
Ag通过不同指令注册的DOM事件监视器来将DOM树的变化传播给模型,事件监视器的代码通过修改$scope暴露的变量来更新模型。
Ag该在何时以何种方式监视模型变化?
Ag只会在被明确告知的情况下才会启动它的模型监控机制。也就是执行scope对象上的$apply方法。
检测模型变化的过程称为$digest循环。$digest从根作用域检查,只有有一个监视器的一个变化都能使$digest循环变脏,迫使Ag进入又一轮循环。每一个被监视的表达式在每个$digest循环中都会被运算两次。(因为两个模型值间可能存在一个依赖关系,一个模型变化时可能会让另一个已被认为稳定的模型值也发生变化)
双向数据更新的过程
以input元素如何将模型变化传播给DOM的例子说明:
1.DOM中的input事件被触发。浏览器进入JS执行环境。
2.由input指令注册的DOM事件处理函数执行。该处理函数会更新模型值,然后在作用域实例上调用scope.$apply方法。
3.js执行进入ag世界,$digest循环启动。第一次$digest循环中发现有个监视表达式是“脏”的,此时需要再来一次循环。
4.一旦检查出一个模型变化,就会触发一个$watch的回调函数。该回调函数会更新插值表达式所在的text属性。
5.第二次$digest循环重新运算了所有监视表达式,但这次没发现任何变化。Ag宣布该模型“稳定”,然后退出$digest循环。
6.Js执行上下文处理其他任何非Ag代码。大多数没有这类代码,之后浏览器退出JS执行环境。
7.UI线程切换到DOM渲染环境,浏览器重绘那个text属性发生变化的DOM节点。
8.在重绘完成之后,浏览器回到守株待兔的状态。
Ag中触发模型变化的情况
- DOM事件
- XHR相应触发回调
- 浏览器的地址变化
- 计时器触发回调
2.依赖注入
原理
AngularJS 是通过构造函数的参数名字来推断依赖服务名称的。具体来说:将函数体通过 toString()转换为一个字符串之后,通过正则表达式匹配来解析出参数的名称,进而找出依赖服务的名称。
通过构造函数的参数名字来推断依赖服务名称的,通过 toString() 来找到这个定义的 function 对应的字符串,然后用正则解析出其中的参数(依赖项),再去依赖映射中取到对应的依赖,实例化之后传入。
**
问题
**
因为 AngularJS 的 injector 是假设函数的参数名就是依赖的名字,然后去查找依赖项,那如果按前面栗子中那样注入依赖,代码压缩后(参数被重命名了),就无法查找到依赖项了。
所以,通常会使用下面两种方式注入依赖(对依赖添加的顺序有要求)。
数组风格依赖注入
myApp.controller('myCtrl',['$scope','$http',function($scope,$http){
...
}])
缺点:重构有点棘手。因为函数参数,在函数定义时写一遍,在数组中再写一遍。把参数弄错了就是问题。
显式调用 $inject
myApp.controller('myCtrl',myCtrl);
function myCtrl = ($scope,$http){
...
}
myCtrl.$inject = ['$scope','$http'];
3.Ag和异步模块定义
异步模块定义(AMD),是由Require.js和类似的JS库推行一种规范,其目标是为异步加载的可复用JS模块制定的一套通用规则。AMD模块可以按需加载,并能实现各模块之间的相互依赖。另外,AMD模块定义还可以被离线构建脚本用来合并特定应用功能所需的所有模块。
AMD模块的功能:
- 异步加载脚本文件,这样浏览器不会被阻塞,可以同时并行加载若干其他资源文件。
- 根据用户使用应用的场景,按需加载JS代码。这点有效地消除了提前加载应用程序代码的需求。
- 用模块定义来指定模块之间的依赖关系,还可设定生产环境中打包所需的模块。
Ag和AMD定义都使用了同一个单词“module”,但是他们不一样,具体如下:
- ag模块用于定义不同的JS类和对象在运行时应如何组合在一起。Ag模块不会执行任何脚本加载操作。相反,Ag期望在应用启动之前所有的模块已经全部加载到浏览器中。
- AMD模块主要关系脚本加载问题。AMD定义允许将应用拆分成若干个更小的文件,然后在需要是仅一步加载你所需的那部分文件。
4.Ag2和Ag1的区别
https://wenku.baidu.com/view/...
使用Ag2的原因
现在出到Ag4了
http://www.jb51.net/article/1...
5.Ag的生命周期
主要分为编译、链接、注册监视、模型变化、观察、摧毁6个阶段。
编译:ag会遍历浏览器提供的dom树,尝试参照已注册的指令集来匹配每个元素、属性、注释和css类。每当匹配一个指令时,ag就会调用该指令的编译函数,该函数返回一个连接函数,ag会收集所有的连接函数。
链接:一旦所有指令被编译完成,ag就会创建作用域,然后通过调用每个指令对应的链接函数将指令和作用域连接起来。同时注册监视:作用域一旦生成,指令就会在它身上注册一个监视,就是我们平时用到的$scope.$watch(),顾名思义监视数据有没有变化
模型变化:这个时候一旦模型发生了变化,会执行用户自己定义的回调函数。其中关键的是,在模型发生变化时,如何从浏览器的js环境进入到angular的环境中操作在ag模型上的数据,此时,ag会调用一个内置指令$scope.$apply,这样就能进去ag的环境。
观察:在这个阶段会启动脏检测机制,先检测根scope,然后传播到所有的子作用域上,这个时候检测到变化就会执行监听函数$watch的回调函数。
摧毁:当我们不需要一个作用域,需要将它移除掉。原则是谁创建的谁摧毁,使用的方法是$scope.$destroy()。
---------持续更新中--------------
原文链接:https://www.f2er.com/angularjs/146242.html