AngularJS⾯试题集锦
ng-if 跟 ng-show/hide 的区别有哪些?
第⼀点区别是, ng-if 在后⾯表达式为 true 的时候才创建这个 dom 节点, ng-show 是初始时就创建了,⽤ display:block 和display:none 来控制显⽰和不显⽰。
第⼆点区别是, ng-if 会(隐式地)产⽣新作⽤域, ng-switch 、 ng-include 等会动态创建⼀块界⾯的也是如此。
这样会导致,在 ng-if 中⽤基本变量绑定 ng-model ,并在外层 div 中把此 model 绑定给另⼀个显⽰区域,内层改变时,外层不会同步改变,因为此时已经是两个变量了。
<p>{{name}}</p>
<div ng-if="true">
<input type="text" ng-model="name">
</div>
ng-show 不存在此问题,因为它不⾃带⼀级作⽤域。
避免这类问题出现的办法是,始终将页⾯中的元素绑定到对象的属性(data.x)⽽不是直接绑定到基本变量(x)上。
详见 AngularJS 中的作⽤域
ng-repeat迭代数组的时候,如果数组中有相同值,会有什么问题,如何解决?
会提⽰ Duplicates in a repeater are not allowed. 加 track by $index 可解决。当然,也可以 trace by 任何⼀个普通的值,只要能唯⼀性标识数组中的每⼀项即可(建⽴ dom 和数据之间的关联)。
ng-click 中写的表达式,能使⽤ JS 原⽣对象上的⽅法吗?
不⽌是 ng-click 中的表达式,只要是在页⾯中,都不能直接调⽤原⽣的 JS ⽅法,因为这些并不存在于与页⾯对应的Controller 的 $scope 中。
举个栗⼦:
<p>{{parseInt(55.66)}}<p>
会发现,什么也没有显⽰。
但如果在 $scope 中添加了这个函数:
$scope.parseInt = function(x){
return parseInt(x);
}
这样⾃然是没什么问题了。
对于这种需求,使⽤⼀个 filter 或许是不错的选择:
<p>{{13.14 | parseIntFilter}}</p>
app.filter('parseIntFilter', function(){
return function(item){
return parseInt(item);
}
})
{{now | 'yyyy-MM-dd'}} 这种表达式⾥⾯,竖线和后⾯的参数通过什么⽅式可以⾃定义?
filter,格式化数据,接收⼀个输⼊,按某规则处理,返回处理结果。
内置 filter
ng 内置的 filter 有九种:
date(⽇期)
currency(货币)
limitTo(限制数组或字符串长度)
orderBy(排序)
lowercase(⼩写)
uppercase(⼤写)
number(格式化数字,加上千位分隔符,并接收参数限定⼩数点位数)filter(处理⼀个数组,过滤出含有某个⼦串的元素)
json(格式化 json 对象)
filter 有两种使⽤⽅法,⼀种是直接在页⾯⾥:
<p>{{now | date : 'yyyy-MM-dd'}}</p>
另⼀种是在 js ⾥⾯⽤:
// $filter('过滤器名称')(需要过滤的对象, 参数1, 参数2,...)
$filter('date')(now, 'yyyy-MM-dd hh:mm:ss');
⾃定义 filter
// 形式
app.filter('过滤器名称',function(){
return function(需要过滤的对象,过滤器参数1,过滤器参数2,...){
//...做⼀些事情
return 处理后的对象;
}
});
// 栗⼦
app.filter('timesFilter', function(){
return function(item, times){
var result = '';
for(var i = 0; i < times; i++){
result += item;
}
return result;
}
})
factory、service 和 provider 是什么关系?
factory
把 service 的⽅法和数据放在⼀个对象⾥,并返回这个对象
app.factory('FooService', function(){
return {
target: 'factory',
sayHello: function(){
return 'hello ' + this.target;
}
}
});
service
通过构造函数⽅式创建 service,返回⼀个实例化对象
app.service('FooService', function(){
var self = this;
this.target = 'service';
this.sayHello = function(){
return 'hello ' + self.target;
}
});
provider
创建⼀个可通过 config 配置的 service,$get 中返回的,就是⽤ factory 创建 service 的内容
app.provider('FooService', function(){
this.setConfigData = function(data){
if(data){
}
}
this.$get = function(){
var self = this;
return {
target: 'provider',
sayHello: function(){
figData + ' hello ' + this.target;
}
}
}
});
// 此处注⼊的是 FooService 的 provider
FooServiceProvider.setConfigData('config data');
});
从底层实现上来看,service 调⽤了 factory,返回其实例;factory 调⽤了 provider,返回其 $get 中定义的内容。factory 和service 功能类似,只不过 factory 是普通 function,可以返回任何东西(return 的都可以被访问,所以那些私有变量怎么写,你懂的);service 是构造器,可以不返回(绑定到 this 的都可以被访问);provider 是加强版 factory,返回⼀个可配置的factory。
详见 AngularJS 之 Factory vs Service vs Provider
angular 的数据绑定采⽤什么机制?详述原理
脏检查机制。
双向数据绑定是 AngularJS 的核⼼机制之⼀。当 view 中有任何数据变化时,会更新到 model ,当 model 中数据有变化
时,view 也会同步更新,显然,这需要⼀个监控。
原理就是,Angular 在 scope 模型上设置了⼀个监听队列,⽤来监听数据变化并更新 view 。每次绑定⼀个东西到 view 上时AngularJS 就会往 $watch 队列⾥插⼊⼀条 $watch ,⽤来检测它监视的 model ⾥是否有变化的东西。当浏览器接收到可以被angular context 处理的事件时, $digest 循环就会触发,遍历所有的 $watch ,最后更新 dom。
举个栗⼦
<button ng-click="val=val+1">increase 1</button>
click 时会产⽣⼀次更新的操作(⾄少触发两次 $digest 循环)
按下按钮
浏览器接收到⼀个事件,进⼊到 angular context
$digest 循环开始执⾏,查询每个 $watch 是否变化
由于监视 $scope .val 的 $watch 报告了变化,因此强制再执⾏⼀次 $digest 循环
新的 $digest 循环未检测到变化
浏览器拿回控制器,更新 $scope .val 新值对应的 dom
$digest 循环的上限是 10 次(超过 10次后抛出⼀个异常,防⽌⽆限循环)。
详见关于 AngularJS 的数据绑定
两个平级界⾯块 a 和 b,如果 a 中触发⼀个事件,有哪些⽅式能让 b 知道?详述原理
这个问题换⼀种说法就是,如何在平级界⾯模块间进⾏通信。有两种⽅法,⼀种是共⽤服务,⼀种是基于事件。
共⽤服务
在 Angular 中,通过 factory 可以⽣成⼀个单例对象,在需要通信的模块 a 和 b 中注⼊这个对象即可。
基于事件
这个⼜分两种⽅式
第⼀种是借助⽗ controller。在⼦ controller 中向⽗ controller 触发( $emit )⼀个事件,然后在⽗ con
troller 中监听( $on )事件,再⼴播( $broadcast )给⼦ controller ,这样通过事件携带的参数,实现了数据经过⽗ controller,在同级 controller
之间传播。
第⼆种是借助 $rootScope 。每个 Angular 应⽤默认有⼀个根作⽤域 $rootScope ,根作⽤域位于最顶层,从它往下挂着各级作⽤域。所以,如果⼦控制器直接使⽤ $rootScope ⼴播和接收事件,那么就可实现同级之间的通信。
详见 AngularJS 中 Controller 之间的通信
⼀个 angular 应⽤应当如何良好地分层?
⽬录结构的划分
对于⼩型项⽬,可以按照⽂件类型组织,⽐如:
css
js
controllers
models
services
filters
templates
但是对于规模较⼤的项⽬,最好按业务模块划分,⽐如:
css
modules
account
controllers
models
原生js和js的区别
services
filters
templates
disk
controllers
models
services
filters
templates
modules 下最好再有⼀个 common ⽬录来存放公共的东西。
逻辑代码的拆分
作为⼀个 MVVM 框架,Angular 应⽤本⾝就应该按照模型,视图模型(控制器),视图来划分。
这⾥逻辑代码的拆分,主要是指尽量让 controller 这⼀层很薄。提取共⽤的逻辑到 service 中(⽐如后台数据的请求,数据的共享和缓存,基于事件的模块间通信等),提取共⽤的界⾯操作到 directive 中(⽐如将⽇期选择、分页等封装成组件等),提取共⽤的格式化操作到 filter 中等等。
在复杂的应⽤中,也可以为实体建⽴对应的构造函数,⽐如硬盘(Disk)模块,可能有列表、新建、详情这样⼏个视图,并分别对应的有 controller,那么可以建⼀个 Disk 构造函数,⾥⾯完成数据的增删改查和验证操作,有跟 Disk 相关的 controller,就注⼊ Disk 构造器并⽣成⼀个实例,这个实例就具备了增删改查和验证⽅法。这样既层次分明,⼜实现了复⽤(让 controller 层更薄了)。
参考 AngularJS在苏宁云中⼼的深⼊实践
angular 应⽤常⽤哪些路由库,各⾃的区别是什么?
Angular1.x 中常⽤ ngRoute 和 ui.router,还有⼀种为 Angular2 设计的 new router (⾯向组件)。后⾯那个没在实际项⽬中⽤过,就不讲了。
⽆论是 ngRoute 还是 ui.router,作为框架额外的附加功能,都必须以模块依赖的形式被引⼊。
区别
ngRoute 模块是 Angular ⾃带的路由模块,⽽ ui.router 模块是基于 ngRoute模块开发的第三⽅模块。
使⽤ ui.router 能够定义有明确⽗⼦关系的路由,并通过 ui-view 指令将⼦路由模版插⼊到⽗路由模板的 <div ui-view></div> 中去,从⽽实现视图嵌套。⽽在 ngRoute 中不能这样定义,如果同时在⽗⼦视图中使⽤了 <div ng-view></div> 会陷⼊死循环。
⽰例
ngRoute
var app = dule('ngRouteApp', ['ngRoute']);
$routeProvider
.when('/main', {
templateUrl: "main.html",
controller: 'MainCtrl'
})
.otherwise({ redirectTo: '/tabs' });
var app = dule("uiRouteApp", ["ui.router"]);
$herwise("/index");
$stateProvider
.state("Main", {
url: "/main",
templateUrl: "main.html",
controller: 'MainCtrl'
})
如果通过angular的directive规划⼀套全组件化体系,可能遇到哪些挑战?
没有⾃⼰⽤ directive 做过⼀全套组件,讲不出。
能想到的⼀点是,组件如何与外界进⾏数据的交互,以及如何通过简单的配置就能使⽤吧。
分属不同团队进⾏开发的 angular 应⽤,如果要做整合,可能会遇到哪些问题,如何解决?
可能会遇到不同模块之间的冲突。
⽐如⼀个团队所有的开发在 moduleA 下进⾏,另⼀团队开发的代码在 moduleB 下
.factory('serviceA', function(){
.
..
})
.factory('serviceA', function(){
...
})
会导致两个 module 下⾯的 serviceA 发⽣了覆盖。
貌似在 Angular1.x 中并没有很好的解决办法,所以最好在前期进⾏统⼀规划,做好约定,严格按照约定开发,每个开发⼈员只写特定区块代码。
angular 的缺点有哪些?
强约束
导致学习成本较⾼,对前端不友好。
但遵守 AngularJS 的约定时,⽣产⼒会很⾼,对 Java 程序员友好。
不利于 SEO
因为所有内容都是动态获取并渲染⽣成的,搜索引擎没法爬取。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论