Angular是當(dāng)下非常流行的前端框架,受到了廣大前端開發(fā)者的喜愛(ài)。下面,w3cschool將為大家列出一些經(jīng)典的Angular面試題以及答案,供大家參考。
Q1、ng-if跟ng-show/hide的區(qū)別有哪些?
A:區(qū)別主要有兩點(diǎn):
1、ng-if 在后面表達(dá)式為 true 的時(shí)候才創(chuàng)建dom 節(jié)點(diǎn),而ng-show 是在初始時(shí)就創(chuàng)建了,可以用 display:block 和 display:none 來(lái)控制顯示和不顯示。
2、ng-if 會(huì)(隱式地)產(chǎn)生新作用域,ng-switch 、ng-include 等會(huì)動(dòng)態(tài)創(chuàng)建一塊界面的也是如此。
這樣會(huì)導(dǎo)致,在 ng-if 中用基本變量綁定 ng-model,同事在外層的 div 中將 model 綁定給另一個(gè)顯示區(qū)域后,在內(nèi)層改變時(shí),外層并不會(huì)隨著內(nèi)層改變,因?yàn)檫@已經(jīng)兩個(gè)不同的變量了。
<p>{{name}}</p>
<div ng-if="true">
<input type="text" ng-model="name">
</div>
而在 ng-show 中,卻不存在此問(wèn)題,因?yàn)樗蛔詭б患?jí)作用域。
為了避免這類問(wèn)題的出現(xiàn),我們可以始終將頁(yè)面中的元素綁定到對(duì)象的屬性(data.x),而不是直接綁定到基本變量(x)上。
Q2、ng-repeat迭代數(shù)組的時(shí)候,如果數(shù)組中有相同值,會(huì)有什么問(wèn)題,如何解決?
A:會(huì)提示 Duplicates in a repeater are not allowed. 出現(xiàn)這種情況的時(shí)候,我們可以通過(guò)加 track by $index 來(lái)解決。當(dāng)然,也可以 trace by 任何一個(gè)普通的值,只要能唯一性標(biāo)識(shí)數(shù)組中的每一項(xiàng)即可(建立 dom 和數(shù)據(jù)之間的關(guān)聯(lián))。
Q3、ng-click 中寫的表達(dá)式,能使用 JS 原生對(duì)象上的方法嗎?
A:不止是 ng-click 中,只要是在頁(yè)面中,都是無(wú)法直接調(diào)用原生的 JS 方法的,因?yàn)檫@些并不存在于與頁(yè)面對(duì)應(yīng)的 Controller 的 $scope 中。
舉個(gè)例子:
<p>{{parseInt(55.66)}}<p>
我們會(huì)發(fā)現(xiàn),什么也沒(méi)有顯示。
但如果在 $scope 中添加了下面這個(gè)函數(shù):
$scope.parseInt = function(x){
return parseInt(x);
}
運(yùn)行就自然是沒(méi)什么問(wèn)題了。
對(duì)于這種需求,使用一個(gè) filter 或許是不錯(cuò)的選擇:
<p>{{13.14 | parseIntFilter}}</p>
app.filter('parseIntFilter', function(){
return function(item){
return parseInt(item);
}
})
Q4、{{now | 'yyyy-MM-dd'}} 這種表達(dá)式里面,豎線和后面的參數(shù)通過(guò)什么方式可以自定義?
A:filter,格式化數(shù)據(jù),接收一個(gè)輸入,按某規(guī)則處理,返回處理結(jié)果。
ng 內(nèi)置的filter主要有九種:
1:date(日期)
2:currency(貨幣)
3:limitTo(限制數(shù)組或字符串長(zhǎng)度)
4:orderBy(排序)
5:lowercase(小寫)
6:uppercase(大寫)
7:number(格式化數(shù)字,加上千位分隔符,并接收參數(shù)限定小數(shù)點(diǎn)位數(shù))
8:filter(處理一個(gè)數(shù)組,過(guò)濾出含有某個(gè)子串的元素)
9:json(格式化 json 對(duì)象)
filter 有兩種使用方法:
一種是直接在頁(yè)面里:
<p>{{now | date : 'yyyy-MM-dd'}}</p>
另一種是在 js 里面用:
// $filter('過(guò)濾器名稱')(需要過(guò)濾的對(duì)象, 參數(shù)1, 參數(shù)2,...)
$filter('date')(now, 'yyyy-MM-dd hh:mm:ss');
自定義 filter
// 形式
app.filter('過(guò)濾器名稱',function(){
return function(需要過(guò)濾的對(duì)象,過(guò)濾器參數(shù)1,過(guò)濾器參數(shù)2,...){
//...做一些事情
return 處理后的對(duì)象;
}
});
// 栗子
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 是什么關(guān)系?
factory
把 service 的方法和數(shù)據(jù)放在一個(gè)對(duì)象里,并返回這個(gè)對(duì)象。
app.factory('FooService', function(){
return {
target: 'factory',
sayHello: function(){
return 'hello ' + this.target;
}
}
});
service
通過(guò)構(gòu)造函數(shù)的方式創(chuàng)建 service,然后返回一個(gè)實(shí)例化對(duì)象。
app.service('FooService', function(){
var self = this;
this.target = 'service';
this.sayHello = function(){
return 'hello ' + self.target;
}
});
provider
創(chuàng)建一個(gè)可通過(guò) config 配置的 service,$get 中返回的,就是用 factory 創(chuàng)建 service 的內(nèi)容。
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');
});
從底層實(shí)現(xiàn)上來(lái)看,三者的關(guān)系是:service 調(diào)用了 factory,返回其實(shí)例;factory 調(diào)用了 provider,返回其 $get 中定義的內(nèi)容。factory 和 service 的功能類似,但是 factory 是普通 function,可以返回任何東西;service 是構(gòu)造器,可以不返回(綁定到 this 的都可以被訪問(wèn));provider 是加強(qiáng)版 factory,返回一個(gè)可配置的 factory。
Q6、angular 的數(shù)據(jù)綁定采用什么機(jī)制?詳述原理
A:采用了臟檢查機(jī)制。
雙向數(shù)據(jù)綁定是 AngularJS 的核心機(jī)制之一。當(dāng) view 中有任何一個(gè)數(shù)據(jù)變化時(shí),都會(huì)更新到 model 中。如果 model 中的數(shù)據(jù)有變化時(shí),view 也會(huì)同步更新,顯然,這需要一個(gè)監(jiān)控。
原理
Angular 在 scope 模型上設(shè)置了一個(gè)監(jiān)聽(tīng)隊(duì)列,這個(gè)監(jiān)聽(tīng)隊(duì)列可以用來(lái)監(jiān)聽(tīng)數(shù)據(jù)變化并更新 view 。每次綁定一個(gè)東西到 view 上時(shí), AngularJS 就會(huì)往 $watch 隊(duì)列里插入一條 $watch ,用來(lái)檢測(cè)它監(jiān)視的 model 里是否有變化的東西。當(dāng)瀏覽器接收到可以被 angular context 處理的事件時(shí), $digest 循環(huán)就會(huì)觸發(fā),遍歷所有的 $watch ,最后更新 dom。
Q7、兩個(gè)平級(jí)界面塊 a 和 b,如果 a 中觸發(fā)一個(gè)事件,有哪些方式能讓 b 知道?詳述原理
A:這個(gè)問(wèn)題換一種說(shuō)法就是,如何在平級(jí)界面模塊間進(jìn)行通信。有兩種方法,一種是共用服務(wù),一種是基于事件。
共用服務(wù)
在 Angular 中,通過(guò) factory 可以生成一個(gè)單例對(duì)象,在需要通信的模塊 a 和 b 中注入這個(gè)對(duì)象即可。
基于事件
這個(gè)又分兩種方式
第一種是借助父 controller。在子 controller 中向父 controller 觸發(fā)( $emit )一個(gè)事件,然后在父 controller 中監(jiān)聽(tīng)( $on )事件,再?gòu)V播( $broadcast )給子 controller ,這樣通過(guò)事件攜帶的參數(shù),實(shí)現(xiàn)了數(shù)據(jù)經(jīng)過(guò)父 controller,在同級(jí) controller 之間傳播。
第二種是借助 $rootScope 。每個(gè) Angular 應(yīng)用默認(rèn)有一個(gè)根作用域 $rootScope , 根作用域位于最頂層,從它往下掛著各級(jí)作用域。所以,如果子控制器直接使用 $rootScope 廣播和接收事件,那么就可實(shí)現(xiàn)同級(jí)之間的通信。
Q8、一個(gè) angular 應(yīng)用應(yīng)當(dāng)如何良好地分層?
目錄結(jié)構(gòu)的劃分
對(duì)于小型項(xiàng)目,可以按照文件類型組織,比如:
css
js
controllers
models
services
filters
templates
但是對(duì)于規(guī)模較大的項(xiàng)目,最好按業(yè)務(wù)模塊劃分,比如:
css
modules
account
controllers
models
services
filters
templates
disk
controllers
models
services
filters
templates
modules 下最好再有一個(gè) common 目錄來(lái)存放公共的東西。
邏輯代碼的拆分
作為一個(gè) MVVM 框架,Angular 應(yīng)用本身就應(yīng)該按照 模型,視圖模型(控制器),視圖來(lái)劃分。
這里邏輯代碼的拆分,主要是指盡量讓 controller 這一層很薄。提取共用的邏輯到 service 中 (比如后臺(tái)數(shù)據(jù)的請(qǐng)求,數(shù)據(jù)的共享和緩存,基于事件的模塊間通信等),提取共用的界面操作到 directive 中(比如將日期選擇、分頁(yè)等封裝成組件等),提取共用的格式化操作到 filter 中等等。
在復(fù)雜的應(yīng)用中,也可以為實(shí)體建立對(duì)應(yīng)的構(gòu)造函數(shù),比如硬盤(Disk)模塊,可能有列表、新建、詳情這樣幾個(gè)視圖,并分別對(duì)應(yīng)的有 controller,那么可以建一個(gè) Disk 構(gòu)造函數(shù),里面完成數(shù)據(jù)的增刪改查和驗(yàn)證操作,有跟 Disk 相關(guān)的 controller,就注入 Disk 構(gòu)造器并生成一個(gè)實(shí)例,這個(gè)實(shí)例就具備了增刪改查和驗(yàn)證方法。這樣既層次分明,又實(shí)現(xiàn)了復(fù)用(讓 controller 層更薄了)。
Q9、angular 應(yīng)用常用哪些路由庫(kù),各自的區(qū)別是什么?
A:Angular1.x 中常用 ngRoute 和 ui.router。
區(qū)別
ngRoute 模塊是 Angular 自帶的路由模塊,而 ui.router 模塊是基于 ngRoute模塊開發(fā)的第三方模塊。
ui.router 是基于 state (狀態(tài))的, ngRoute 是基于 url 的,ui.router模塊具有更強(qiáng)大的功能,主要體現(xiàn)在視圖的嵌套方面。
使用 ui.router 能夠定義有明確父子關(guān)系的路由,并通過(guò) ui-view 指令將子路由模版插入到父路由模板的 <div ui-view></div> 中去,從而實(shí)現(xiàn)視圖嵌套。而在 ngRoute 中不能這樣定義,如果同時(shí)在父子視圖中 使用了 <div ng-view></div> 會(huì)陷入死循環(huán)。
示例
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、分屬不同團(tuán)隊(duì)進(jìn)行開發(fā)的 angular 應(yīng)用,如果要做整合,可能會(huì)遇到哪些問(wèn)題,如何解決?
A:可能會(huì)遇到不同模塊之間的沖突。
比如一個(gè)團(tuán)隊(duì)所有的開發(fā)在 moduleA 下進(jìn)行,另一團(tuán)隊(duì)開發(fā)的代碼在 moduleB 下
angular.module('myApp.moduleA', [])
.factory('serviceA', function(){
...
})
angular.module('myApp.moduleB', [])
.factory('serviceA', function(){
...
})
angular.module('myApp', ['myApp.moduleA', 'myApp.moduleB'])
會(huì)導(dǎo)致兩個(gè) module 下面的 serviceA 發(fā)生了覆蓋。
貌似在 Angular1.x 中并沒(méi)有很好的解決辦法,所以最好在前期進(jìn)行統(tǒng)一規(guī)劃,做好約定,嚴(yán)格按照約定開發(fā),每個(gè)開發(fā)人員只寫特定區(qū)塊代碼。
Q11、angular 的缺點(diǎn)有哪些?
強(qiáng)約束
導(dǎo)致學(xué)習(xí)成本較高,對(duì)前端不友好。
但遵守 AngularJS 的約定時(shí),生產(chǎn)力會(huì)很高,對(duì) Java 程序員友好。
不利于SEO
因?yàn)樗袃?nèi)容都是動(dòng)態(tài)獲取并渲染生成的,搜索引擎沒(méi)法爬取。
一種解決辦法是,對(duì)于正常用戶的訪問(wèn),服務(wù)器響應(yīng) AngularJS 應(yīng)用的內(nèi)容;對(duì)于搜索引擎的訪問(wèn),則響應(yīng)專門針對(duì) SEO 的HTML頁(yè)面。
性能問(wèn)題
作為 MVVM 框架,因?yàn)閷?shí)現(xiàn)了數(shù)據(jù)的雙向綁定,對(duì)于大數(shù)組、復(fù)雜對(duì)象會(huì)存在性能問(wèn)題。
可以用來(lái) 優(yōu)化 Angular 應(yīng)用的性能 的辦法:
減少監(jiān)控項(xiàng)(比如對(duì)不會(huì)變化的數(shù)據(jù)采用單向綁定)
主動(dòng)設(shè)置索引(指定 track by ,簡(jiǎn)單類型默認(rèn)用自身當(dāng)索引,對(duì)象默認(rèn)使用 $$hashKey ,比如改為 track by item.id )
降低渲染數(shù)據(jù)量(比如分頁(yè),或者每次取一小部分?jǐn)?shù)據(jù),根據(jù)需要再?。?
數(shù)據(jù)扁平化(比如對(duì)于樹狀結(jié)構(gòu),使用扁平化結(jié)構(gòu),構(gòu)建一個(gè) map 和樹狀數(shù)據(jù),對(duì)樹操作時(shí),由于跟扁平數(shù)據(jù)同一引用,樹狀數(shù)據(jù)變更會(huì)同步到原始的扁平數(shù)據(jù))
另外,對(duì)于Angular1.x ,存在 臟檢查 和 模塊機(jī)制 的問(wèn)題。
Q12、如何看待 angular 1.2 中引入的 controller as 語(yǔ)法?
A:在 angular 1.2 以前,在 view 上的任何綁定都是直接綁定在 $scope 上的
function myCtrl($scope){
$scope.a = 'aaa';
$scope.foo = function(){
...
}
}
使用 controllerAs,不需要再注入 $scope ,controller 變成了一個(gè)很簡(jiǎn)單的 javascript 對(duì)象(POJO),一個(gè)更純粹的 ViewModel。
function myCtrl(){
// 使用 vm 捕獲 this 可避免內(nèi)部的函數(shù)在使用 this 時(shí)導(dǎo)致上下文改變
var vm = this;
vm.a = 'aaa';
}
更多建議: