AngularJS 是一种开源的 JavaScript 框架,用于创建动态的 Web 应用程序。它可以帮助开发人员快速构建客户端应用程序,并且可以轻松地扩展到服务器端。AngularJS 面试题是一个重要的考察项目,因为它能够帮助雇主了解求职者是否具备使用 AngularJS 的能力。
AngularJS 面试题通常包括对 AngularJS 的基本理解、如何使用 AngularJS 构建 Web 应用程序、如何使用 AngularJS 进行单元测试、如何使用 AngularJS 进行数据绑定以及如何使用 AngularJS 进行依赖注入等内容。
1. 什么是 AngularJS? 2. 什么是数据绑定? 3. 什么是依赖注入? 4. 如何使用 AngularJS 构建 Web 应用程序? 5. 如何使用 AngularJS 进行单元测试? 6. 什么是 MVC 架构? 7. 什么是 $scope ? 8. 什么是 $rootScope ? 9. 什么是 $http 服务? 10. 什么是 ng-model ?
Angular是当下非常流行的前端框架,受到了广大前端开发者的喜爱。下面,将为大家列出一些经典的Angular面试题以及答案,供大家参考。
Q1、ng-if跟ng-show/hide的区别有哪些?
A:区别主要有两点:
1、ng-if 在后面表达式为 true 的时候才创建dom 节点,而ng-show 是在初始时就创建了,可以用 display:block 和 display:none 来控制显示和不显示。
2、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)上。
Q2、ng-repeat迭代数组的时候,如果数组中有相同值,会有什么问题,如何解决?
A:会提示 Duplicates in a repeater are not allowed. 出现这种情况的时候,我们可以通过加 track by $index 来解决。当然,也可以 trace by 任何一个普通的值,只要能唯一性标识数组中的每一项即可(建立 dom 和数据之间的关联)。
Q3、ng-click 中写的表达式,能使用 JS 原生对象上的方法吗?
A:不止是 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);
}
})
Q4、{{now | "yyyy-MM-dd"}} 这种表达式里面,竖线和后面的参数通过什么方式可以自定义?
A:filter,格式化数据,接收一个输入,按某规则处理,返回处理结果。
ng 内置的filter主要有九种:
1:date(日期)
2:currency(货币)
3:limitTo(限制数组或字符串长度)
4:orderBy(排序)
5:lowercase(小写)
6:uppercase(大写)
7:number(格式化数字,加上千位分隔符,并接收参数限定小数点位数)
8:filter(处理一个数组,过滤出含有某个子串的元素)
9: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;
}
})
Q5、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.configData = "init data";
this.setConfigData = function(data){
if(data){
this.configData = data;
}
}
this.$get = function(){
var self = this;
return {
target: "provider",
sayHello: function(){
return self.configData + " hello " + this.target;
}
}
}
});
// 此处注入的是 FooService 的 provider
app.config(function(FooServiceProvider){
FooServiceProvider.setConfigData("config data");
});
从底层实现上来看,三者的关系是:service 调用了 factory,返回其实例;factory 调用了 provider,返回其 $get 中定义的内容。factory 和 service 的功能类似,但是 factory 是普通 function,可以返回任何东西;service 是构造器,可以不返回(绑定到 this 的都可以被访问);provider 是加强版 factory,返回一个可配置的 factory。
Q6、angular 的数据绑定采用什么机制?详述原理
A:采用了脏检查机制。
双向数据绑定是 AngularJS 的核心机制之一。当 view 中有任何一个数据变化时,都会更新到 model 中。如果 model 中的数据有变化时,view 也会同步更新,显然,这需要一个监控。
原理
Angular 在 scope 模型上设置了一个监听队列,这个监听队列可以用来监听数据变化并更新 view 。每次绑定一个东西到 view 上时, AngularJS 就会往 $watch 队列里插入一条 $watch ,用来检测它监视的 model 里是否有变化的东西。当浏览器接收到可以被 angular context 处理的事件时, $digest 循环就会触发,遍历所有的 $watch ,最后更新 dom。
Q7、两个平级界面块 a 和 b,如果 a 中触发一个事件,有哪些方式能让 b 知道?详述原理
A:这个问题换一种说法就是,如何在平级界面模块间进行通信。有两种方法,一种是共用服务,一种是基于事件。
共用服务
在 Angular 中,通过 factory 可以生成一个单例对象,在需要通信的模块 a 和 b 中注入这个对象即可。
基于事件
这个又分两种方式
第一种是借助父 controller。在子 controller 中向父 controller 触发( $emit )一个事件,然后在父 controller 中监听( $on )事件,再广播( $broadcast )给子 controller ,这样通过事件携带的参数,实现了数据经过父 controller,在同级 controller 之间传播。
第二种是借助 $rootScope 。每个 Angular 应用默认有一个根作用域 $rootScope , 根作用域位于最顶层,从它往下挂着各级作用域。所以,如果子控制器直接使用 $rootScope 广播和接收事件,那么就可实现同级之间的通信。
Q8、一个 angular 应用应当如何良好地分层?
目录结构的划分
对于小型项目,可以按照文件类型组织,比如:
css
js
controllers
models
services
filters
templates
但是对于规模较大的项目,最好按业务模块划分,比如:
css
modules
account
controllers
models
services
filters
templates
disk
controllers
models
services
filters
templates
modules 下最好再有一个 common 目录来存放公共的东西。
逻辑代码的拆分
作为一个 MVVM 框架,Angular 应用本身就应该按照 模型,视图模型(控制器),视图来划分。
这里逻辑代码的拆分,主要是指尽量让 controller 这一层很薄。提取共用的逻辑到 service 中 (比如后台数据的请求,数据的共享和缓存,基于事件的模块间通信等),提取共用的界面操作到 directive 中(比如将日期选择、分页等封装成组件等),提取共用的格式化操作到 filter 中等等。
在复杂的应用中,也可以为实体建立对应的构造函数,比如硬盘(Disk)模块,可能有列表、新建、详情这样几个视图,并分别对应的有 controller,那么可以建一个 Disk 构造函数,里面完成数据的增删改查和验证操作,有跟 Disk 相关的 controller,就注入 Disk 构造器并生成一个实例,这个实例就具备了增删改查和验证方法。这样既层次分明,又实现了复用(让 controller 层更薄了)。
Q9、angular 应用常用哪些路由库,各自的区别是什么?
A:Angular1.x 中常用 ngRoute 和 ui.router。
区别
ngRoute 模块是 Angular 自带的路由模块,而 ui.router 模块是基于 ngRoute模块开发的第三方模块。
ui.router 是基于 state (状态)的, ngRoute 是基于 url 的,ui.router模块具有更强大的功能,主要体现在视图的嵌套方面。
使用 ui.router 能够定义有明确父子关系的路由,并通过 ui-view 指令将子路由模版插入到父路由模板的 <div ui-view></div> 中去,从而实现视图嵌套。而在 ngRoute 中不能这样定义,如果同时在父子视图中 使用了 <div ng-view></div> 会陷入死循环。
示例
ngRoute
var app = angular.module("ngRouteApp", ["ngRoute"]);
app.config(function($routeProvider){
$routeProvider
.when("/main", {
templateUrl: "main.html",
controller: "MainCtrl"
})
.otherwise({ redirectTo: "/tabs" });
ui.router
var app = angular.module("uiRouteApp", ["ui.router"]);
app.config(function($urlRouterProvider, $stateProvider){
$urlRouterProvider.otherwise("/index");
$stateProvider
.state("Main", {
url: "/main",
templateUrl: "main.html",
controller: "MainCtrl"
})
Q10、分属不同团队进行开发的 angular 应用,如果要做整合,可能会遇到哪些问题,如何解决?
A:可能会遇到不同模块之间的冲突。
比如一个团队所有的开发在 moduleA 下进行,另一团队开发的代码在 moduleB 下
angular.module("myApp.moduleA", [])
.factory("serviceA", function(){
...
})
angular.module("myApp.moduleB", [])
.factory("serviceA", function(){
...
})
angular.module("myApp", ["myApp.moduleA", "myApp.moduleB"])
会导致两个 module 下面的 serviceA 发生了覆盖。
貌似在 Angular1.x 中并没有很好的解决办法,所以最好在前期进行统一规划,做好约定,严格按照约定开发,每个开发人员只写特定区块代码。
Q11、angular 的缺点有哪些?
强约束
导致学习成本较高,对前端不友好。
但遵守 AngularJS 的约定时,生产力会很高,对 Java 程序员友好。
不利于SEO
因为所有内容都是动态获取并渲染生成的,搜索引擎没法爬取。
一种解决办法是,对于正常用户的访问,服务器响应 AngularJS 应用的内容;对于搜索引擎的访问,则响应专门针对 SEO 的HTML页面。
性能问题
作为 MVVM 框架,因为实现了数据的双向绑定,对于大数组、复杂对象会存在性能问题。
可以用来 优化 Angular 应用的性能 的办法:
减少监控项(比如对不会变化的数据采用单向绑定)
主动设置索引(指定 track by ,简单类型默认用自身当索引,对象默认使用 $$hashKey ,比如改为 track by item.id )
降低渲染数据量(比如分页,或者每次取一小部分数据,根据需要再取)
数据扁平化(比如对于树状结构,使用扁平化结构,构建一个 map 和树状数据,对树操作时,由于跟扁平数据同一引用,树状数据变更会同步到原始的扁平数据)
另外,对于Angular1.x ,存在 脏检查 和 模块机制 的问题。
Q12、如何看待 angular 1.2 中引入的 controller as 语法?
A:在 angular 1.2 以前,在 view 上的任何绑定都是直接绑定在 $scope 上的
function myCtrl($scope){
$scope.a = "aaa";
$scope.foo = function(){
...
}
}
使用 controllerAs,不需要再注入 $scope ,controller 变成了一个很简单的 javascript 对象(POJO),一个更纯粹的 ViewModel。
function myCtrl(){
// 使用 vm 捕获 this 可避免内部的函数在使用 this 时导致上下文改变
var vm = this;
vm.a = "aaa";
}
HTMLIndex 文件public/index.html文件是一个会被html-webpack-plugin处理的模板。在构建过程中,资源链接会被自动注入。另外,Vu...
核心概念系统里有两个主要的部分:@vue/cli:全局安装的,暴露vue create app命令;@vue/cli-service:局部安装,暴露vue-cli-se...
自定义指令简介除了默认设置的核心指令(v-model和v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 里面,代码复用的主要形式...
需要服务端渲染(SSR)吗?在开始服务端渲染前,我们先看看它能给我们带来什么,以及什么时候需要用它。SEO(搜索引擎优化)谷歌...
监听事件可以用v-on指令监听 DOM 事件来触发一些 JavaScript 代码。示例:div id="example-1"button v-on:click="counter += 1"...
稳定性: 2 - 不稳定流用于处理Node.js中的流数据的抽象接口,在Node里被不同的对象实现。例如,对HTTP服务器的请求是流,process...
稳定性: 2 - 不稳定Node.js域包含了能把不同的IO操作看成单独组的方法。如果任何一个注册到域的事件或者回调触发error事件,或者...
MongoDB是一种文档导向数据库管理系统,由C++撰写而成。本章节我们将为大家介绍如何使用 Node.js 来连接 MongoDB,并对数据库进...