angular中常见问答题

一、angularjs的几大特性是什么?

双向数据绑定、依赖注入、模板、指令、MVC/MVVMjavascript

二、列举几种常见的设计模式,写出没个表明的含义?

MVC :model view controller
MVVM :model view viewModelcss

三、请描述angularjs的运行过程?

angularjs编译全部的HTML元素标签,而后在里面查找angular程序的入口 ng-app 每一个元素上的指令是把全部指令收集起来根据优先级依次编译html

四、ng-bind和ng-model的区别是什么?

ng-bind只能展现数据 ng-model能够操做数据前端

五、请描述$scope的特色还有其最大的父类?

随建立做用域建立的一个变量,就表明controller所表明的做用域,其持有的对象和方法可在当前及其子做用域生效java

六、原生js的延迟或回调在angularjs里能完美运行吗?怎么解决?能够用例子?

不能 须要用$apply来进行传播git

七、{{ array | filter:{‘age’:23}:true }} 这个过滤里的true是什么意思?

是否用angular.equals进行比较后为真才返回程序员

八、自定义过滤建立后返回的是一个什么对象?

返回一个函数对象 而且函数内要返回最后返回的对象angularjs

九、ng-repeat循环[1,3,2,4,3,4]数组会报错吗?若是会怎么解决?

会由于有重复的内容 track by $indexgithub

十、angular经常使用的服务中value和constant最大的区别是什么?

constant的建立要早于value 而且其能够在config配置中使用 value不行ajax

十一、经常使用服务中factory和service的最大区别是什么?

factory返回的对象当咱们使用它的时候手动初始化并返回,而service是当咱们第一次使用的时候angular帮咱们初始化一次,而后之后使用的时候返回的都是这个对象,factory建立的服务是表明的是其后面函数的返回值,这个返回值能够是任意类型,service不用返回,直接操做的就是本身

十二、怎么拦截服务?

在config配置里注入须要拦截的服务的名字+Provider来拦截

1三、decorator的做用是什么?和拦截服务的区别是什么?

装饰器不只能够应用在咱们本身的服务上,也能够对angularjs核心服务进行拦截、中断甚至替换功能的操做,事实上angularjs的不少测试就是借助$provide.decorator()创建的、请写一个配置路由的代码段(只须要写怎么声明一个路由和其经常使用属性的代码段)

1四、resolve的做用是什么?

若是设置了resolve属性,angularjs会将列表中的元素都注入到控制器中,列表对象能够是键(键值是会被注入到控制器中依赖的名字),也能够是工厂(便可以是一个服务的名字)

1五、ngRoute默认查找的路由是什么?$routeProvider.otherwise(’/index’)是什么做用?

是/ 设置路由的意外指向到/index

1六、$location.path(‘/home’)和$location.url(‘/home’)均可以进行路由跳转,可是.path方法和.url方法最大的区别是什么?

.url方法:能够在跳转的同时设置查询串,返回url的整个路径; 而.path方法:返回的路径不包括?后面的部分;

1七、什么是跨域,请简要描述跨域的场景?

协议 域名 端口号有一个不同就是跨域,也就是不一样域名之间的访问;

1八、常使用的跨域方案就哪两种?分别描述其利用的原理?

jsonp; post请求设置请求头 ; jsonp利用的是script能够访问外部信息的原理发送请求而且利用jsonp协议进行数据交互 post设置请求头跳过预请求来实现跨域

1九、请写出$http网络请求的几种写法,最少两种

$http.**(url).success(function(data){}).error(function(error){}) $http({ method:’**’, url:url }).success(function(data){
}).error(function(error){
}) $http({ method:’***’, url:url }).then(function success(data){
},function error(error){
})
var promise = $http({ method:’get’, url:url }); promise.then(function(data){
},function(error){
}) 或者 promise.success(function(data){
}); promise.error(function(error){
});

20、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 绑定给另外一个显示区域,内层改变时,外层不会同步改变,由于此时已是两个变量了。

{{name}}

<div ng-if="true"> <input type="text" ng-model="name"></div>

2一、ng-show

不存在此问题,由于它不自带一级做用域。

避免这类问题出现的办法是,始终将页面中的元素绑定到对象的属性(data.x)而不是直接绑定到基本变量(x)上。

详见 

AngularJS 中的做用域

2二、ng-repeat迭代数组的时候,若是数组中有相同值,会有什么问题,如何解决?

会提示 Duplicates in a repeater are not allowed.

加 track by $index

可解决。固然,也能够 trace by 任何一个普通的值,只要能惟一性标识数组中的每一项便可(创建 dom 和数据之间的关联)。

ng-click 中写的表达式,能使用 JS 原生对象上的方法吗?

不止是 ng-click 中的表达式,只要是在页面中,都不能直接调用原生的 JS 方法,由于这些并不存在于与页面对应的 Controller 的 $scope 中。

举个栗子:

{{parseInt(55.66)}}

会发现,什么也没有显示。
但若是在 $scope 中添加了这个函数:
$scope.parseInt = function(x){ return parseInt(x);}
这样天然是没什么问题了。

2三、对于这种需求,使用一个 filter 或许是不错的选择:

{{13.14 | parseIntFilter}}

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 有两种使用方法,一种是直接在页面里:

{{now | date : 'yyyy-MM-dd'}}

另外一种是在 js 里面用:
// $filter('过滤器名称')(须要过滤的对象, 参数1, 参数2,...)$filter('date')(now, 'yyyy-MM-dd hh:mm:ss');

2四、自定义 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; }})

2五、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 的 providerapp.config(function(FooServiceProvider){ 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

2六、$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
)一个事件,而后在父 controller 中监听($on
)事件,再广播($broadcast
)给子 controller ,这样经过事件携带的参数,实现了数据通过父 controller,在同级 controller 之间传播。
第二种是借助 $rootScope
。每一个 Angular 应用默认有一个根做用域 $rootScope
, 根做用域位于最顶层,从它往下挂着各级做用域。因此,若是子控制器直接使用 $rootScope
广播和接收事件,那么就可实现同级之间的通讯。
详见 AngularJS 中 Controller 之间的通讯
一个 angular 应用应当如何良好地分层?
目录结构的划分
对于小型项目,能够按照文件类型组织,好比:
cssjs controllers models services filterstemplates

可是对于规模较大的项目,最好按业务模块划分,好比:
cssmodules 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 层更薄了)。
参考 AngularJS在苏宁云中心的深刻实践

2七、angular 应用经常使用哪些路由库,各自的区别是什么?

Angular1.x 中经常使用 ngRoute 和 ui.router,还有一种为 Angular2 设计的 new router(面向组件)。后面那个没在实际项目中用过,就不讲了。
不管是 ngRoute 仍是 ui.router,做为框架额外的附加功能,都必须以 模块依赖 的形式被引入。
区别
ngRoute 模块是 Angular 自带的路由模块,而 ui.router 模块是基于 ngRoute模块开发的第三方模块。
ui.router 是基于 state (状态)的, ngRoute 是基于 url 的,ui.router模块具备更强大的功能,主要体如今视图的嵌套方面。
使用 ui.router 可以定义有明确父子关系的路由,并经过 ui-view 指令将子路由模版插入到父路由模板的

中去,从而实现视图嵌套。而在 ngRoute 中不能这样定义,若是同时在父子视图中 使用了

会陷入死循环。

示例

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' })

2八、若是经过angular的directive规划一套全组件化体系,可能遇到哪些挑战?

没有本身用 directive 作过一全套组件,讲不出。
能想到的一点是,组件如何与外界进行数据的交互,以及如何经过简单的配置就能使用吧。
分属不一样团队进行开发的 angular 应用,若是要作整合,可能会遇到哪些问题,如何解决?
可能会遇到不一样模块之间的冲突。
好比一个团队全部的开发在 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 中并无很好的解决办法,因此最好在前期进行统一规划,作好约定,严格按照约定开发,每一个开发人员只写特定区块代码。
angular 的缺点有哪些?
强约束
致使学习成本较高,对前端不友好。
但遵照 AngularJS 的约定时,生产力会很高,对 Java 程序员友好。
不利于 SEO
由于全部内容都是动态获取并渲染生成的,搜索引擎无法爬取。
一种解决办法是,对于正经常使用户的访问,服务器响应 AngularJS 应用的内容;对于搜索引擎的访问,则响应专门针对 SEO 的HTML页面。
性能问题
做为 MVVM 框架,由于实现了数据的双向绑定,对于大数组、复杂对象会存在性能问题。
能够用来 优化 Angular 应用的性能 的办法:
减小监控项(好比对不会变化的数据采用单向绑定)

主动设置索引(指定 track by
,简单类型默认用自身当索引,对象默认使用 $$hashKey
,好比改成 track by item.id

下降渲染数据量(好比分页,或者每次取一小部分数据,根据须要再取)

数据扁平化(好比对于树状结构,使用扁平化结构,构建一个 map 和树状数据,对树操做时,因为跟扁平数据同一引用,树状数据变动会同步到原始的扁平数据)

另外,对于Angular1.x ,存在 脏检查 和 模块机制 的问题。
移动端
可尝试 Ionic,但并不完善。
参考 如何看2015年1月Peter-Paul Koch对Angular的见解?
如何看待 angular 1.2 中引入的 controller as 语法?
最根本的好处
在 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';}

原理
从源码实现上来看,controllerAs 语法只是把 controller 这个对象的实例用 as 别名在 $scope 上建立了一个属性。
if (directive.controllerAs) { locals.$scope[directive.controllerAs] = controllerInstance;}

可是这样作,除了上面提到的使 controller 更加 POJO 外,还能够避免遇到 AngularJS 做用域相关的一个坑(就是上文中 ng-if 产生一级做用域的坑,其实也是 javascript 原型链继承中值类型继承的坑。由于使用 controllerAs 的话 view 上全部字段都绑定在一个引用的属性上,好比 vm.xx,因此坑再也不存在)。

<div ng-controller="TestCtrl as vm">

{{name}}

<div ng-if="vm.name"> <input type="text" ng-model="vm.name"> </div></div>

问题
使用 controllerAs 会遇到的一个问题是,由于没有注入 $scope
,致使 $emit
、 $broadcast
、 $on
、 $watch
等 $scope
下的方法没法使用。这些跟事件相关的操做能够封装起来统一处理,或者在单个 controller 中引入 $scope
,特殊对待。
参考 angular controller as syntax vs scope
详述 angular 的 “依赖注入”
栗子
依赖注入是一种软件设计模式,目的是处理代码之间的依赖关系,减小组件间的耦合。
举个栗子,若是没有使用 AngularJS,想从后台查询数据并在前端显示,可能须要这样作:

var animalBox = document.querySelector('.animal-box');var httpRequest = { get: function(url, callback){ console.log(url + ' requested'); var animals = ['cat', 'dog', 'rabbit']; callback(animals); }}var render = function(el, http){ http.get('/api/animals', function(animals){ el.innerHTML = animals; })}render(httpRequest, animalBox);

可是,若是在调用 render 的时候不传参数,像下面这样,会报错,由于找不到 el 和 http(定义的时候依赖了,运行的时候不会自动查找依赖项)
render();// TypeError: Cannot read property 'get' of undefined

而使用 AngularJS,能够直接这样
function myCtrl = ($scope, $http){ $http.get('/api/animals').success(function(data){ $scope.animals = data; })}

也就是说,在 Angular App 运行的时候,调用 myCtrl,自动作了 $scope
和 $http
两个依赖性的注入。
原理
AngularJS 是经过构造函数的参数名字来推断依赖服务名称的,经过 toString()
来找到这个定义的 function 对应的字符串,而后用正则解析出其中的参数(依赖项),再去依赖映射中取到对应的依赖,实例化以后传入。
简化一下,大概是这样:

var inject = { // 存储依赖映射关系 storage: {}, // 注册依赖 register: function(name, resource){ this.storage[name] = resource; }, // 解析出依赖并调用 resolve: function(target){ var self = this; var FN_ARGS = /^function\s*[^=XXFN}*(\s*({FNXX=]*))/m; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?*\/))/mg; fnText = target.toString().replace(STRIP_COMMENTS, ''); argDecl = fnText.match(FN_ARGS)[1].split(/, ?/g); var args = []; argDecl.forEach(function(arg){ if(self.storage[arg]){ args.push(self.storage[arg]); } }) return function(){ target.apply({}, args); } }}

使用这个 injector,前面那个不用 AngularJS 的栗子这样改造一下就能够调用了

inject.register('el', animalBox);inject.register('ajax', httpRequest);render = inject.resolve(render);render();

问题
由于 AngularJS 的 injector 是假设函数的参数名就是依赖的名字,而后去查找依赖项,那若是按前面栗子中那样注入依赖,代码压缩后(参数被重命名了),就没法查找到依赖项了。
// 压缩前function myCtrl = ($scope, $http){ ...}// 压缩后function myCtrl = (a, b){ ...}

因此,一般会使用下面两种方式注入依赖(对依赖添加的顺序有要求)。
数组注释法
myApp.controller('myCtrl', ['$scope', '$http', function($scope, $http){ ...}])

显式 $inject

myApp.controller('myCtrl', myCtrl);function myCtrl = ($scope, $http){ ...}myCtrl.$inject = ['$scope', '$http'];

2九、补充

对于一个 DI 容器,必须具有三个要素:依赖项的注册,依赖关系的声明和对象的获取。
在 AngularJS 中,module 和 $provide 均可以提供依赖项的注册;内置的 injector 能够获取对象(自动完成依赖注入);依赖关系的声明,就是前面问题中提到的那样。
下面是个栗子
// 对于 module,传递参数不止一个,表明新建模块,空数组表明不依赖其余模块// 只有一个参数(模块名),表明获取模块// 定义 myApp,添加 myApp.services 为其依赖项angular.module('myApp', ['myApp.services']);// 定义一个 services module,将 services 都注册在这个 module 下面angular.module('myApp.services', [])// $provider 有 factory, service, provider, value, constant// 定义一个 HttpServiceangular.module('myApp.services').service('HttpService', ['$http', function($http){ ...}])

30、参考

[AngularJS] 本身实现一个简单的依赖注入

理解angular中的module和injector,即依赖注入

AngularJS中的依赖注入实际应用场景

3一、如何看待angular2

相比 Angular1.x,Angular2的改动很大,几乎算是一个全新的框架。
基于 TypeScript(可使用 TypeScript 进行开发),在大型项目团队协做时,强语言类型更有利。
组件化,提高开发和维护的效率。
还有 module 支持动态加载,new router,promise的原生支持等等。
迎合将来标准,吸纳其余框架的优势,值得期待,不过同时要学习的东西也更多了(ES next、TS、Rx等)。
参考
浴火重生的Angular

有关Angular 2.0的一切

相关文章
相关标签/搜索