AngularJS常見面試題及答案

2022-04-15 14:40 更新

Angular是當下非常流行的前端框架,受到了廣大前端開發(fā)者的喜愛。下面,w3cschool將為大家列出一些經(jīng)典的Angular面試題以及答案,供大家參考。

timg (1)

Q1、ng-if跟ng-show/hide的區(qū)別有哪些?

A:區(qū)別主要有兩點:

1、ng-if 在后面表達式為 true 的時候才創(chuàng)建dom 節(jié)點,而ng-show 是在初始時就創(chuàng)建了,可以用 display:block 和 display:none 來控制顯示和不顯示。

2、ng-if 會(隱式地)產(chǎn)生新作用域,ng-switch 、ng-include 等會動態(tài)創(chuàng)建一塊界面的也是如此。

這樣會導致,在 ng-if 中用基本變量綁定 ng-model,同事在外層的 div 中將 model 綁定給另一個顯示區(qū)域后,在內(nèi)層改變時,外層并不會隨著內(nèi)層改變,因為這已經(jīng)兩個不同的變量了。

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

而在 ng-show 中,卻不存在此問題,因為它不自帶一級作用域。

為了避免這類問題的出現(xiàn),我們可以始終將頁面中的元素綁定到對象的屬性(data.x),而不是直接綁定到基本變量(x)上。


Q2、ng-repeat迭代數(shù)組的時候,如果數(shù)組中有相同值,會有什么問題,如何解決?

A:會提示 Duplicates in a repeater are not allowed. 出現(xiàn)這種情況的時候,我們可以通過加 track by $index 來解決。當然,也可以 trace by 任何一個普通的值,只要能唯一性標識數(shù)組中的每一項即可(建立 dom 和數(shù)據(jù)之間的關(guān)聯(lián))。


Q3、ng-click 中寫的表達式,能使用 JS 原生對象上的方法嗎?

A:不止是 ng-click 中,只要是在頁面中,都是無法直接調(diào)用原生的 JS 方法的,因為這些并不存在于與頁面對應的 Controller 的 $scope 中。

舉個例子:

<p>{{parseInt(55.66)}}<p>

我們會發(fā)現(xiàn),什么也沒有顯示。

但如果在 $scope 中添加了下面這個函數(shù):

$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'}} 這種表達式里面,豎線和后面的參數(shù)通過什么方式可以自定義?

A:filter,格式化數(shù)據(jù),接收一個輸入,按某規(guī)則處理,返回處理結(jié)果。


ng 內(nèi)置的filter主要有九種:

1:date(日期)

2:currency(貨幣)

3:limitTo(限制數(shù)組或字符串長度)

4:orderBy(排序)

5:lowercase(小寫)

6:uppercase(大寫)

7:number(格式化數(shù)字,加上千位分隔符,并接收參數(shù)限定小數(shù)點位數(shù))

8:filter(處理一個數(shù)組,過濾出含有某個子串的元素)

9:json(格式化 json 對象)


filter 有兩種使用方法:

一種是直接在頁面里:

<p>{{now | date : 'yyyy-MM-dd'}}</p>

另一種是在 js 里面用:

// $filter('過濾器名稱')(需要過濾的對象, 參數(shù)1, 參數(shù)2,...)
$filter('date')(now, 'yyyy-MM-dd hh:mm:ss');

自定義 filter

// 形式
app.filter('過濾器名稱',function(){
    return function(需要過濾的對象,過濾器參數(shù)1,過濾器參數(shù)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 是什么關(guān)系?

factory

把 service 的方法和數(shù)據(jù)放在一個對象里,并返回這個對象。

app.factory('FooService', function(){
  return {
    target: 'factory',
    sayHello: function(){
      return 'hello ' + this.target;
    }
  }
});

service

通過構(gòu)造函數(shù)的方式創(chuàng)建 service,然后返回一個實例化對象。

app.service('FooService', function(){
  var self = this;
  this.target = 'service';
  this.sayHello = function(){
    return 'hello ' + self.target;
  }
});

provider

創(chuàng)建一個可通過 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');
});

從底層實現(xiàn)上來看,三者的關(guān)系是:service 調(diào)用了 factory,返回其實例;factory 調(diào)用了 provider,返回其 $get 中定義的內(nèi)容。factory 和 service 的功能類似,但是 factory 是普通 function,可以返回任何東西;service 是構(gòu)造器,可以不返回(綁定到 this 的都可以被訪問);provider 是加強版 factory,返回一個可配置的 factory。


Q6、angular 的數(shù)據(jù)綁定采用什么機制?詳述原理

A:采用了臟檢查機制。

雙向數(shù)據(jù)綁定是 AngularJS 的核心機制之一。當 view 中有任何一個數(shù)據(jù)變化時,都會更新到 model 中。如果 model 中的數(shù)據(jù)有變化時,view 也會同步更新,顯然,這需要一個監(jiān)控。

原理

Angular 在 scope 模型上設置了一個監(jiān)聽隊列,這個監(jiān)聽隊列可以用來監(jiān)聽數(shù)據(jù)變化并更新 view 。每次綁定一個東西到 view 上時, AngularJS 就會往 $watch 隊列里插入一條 $watch ,用來檢測它監(jiān)視的 model 里是否有變化的東西。當瀏覽器接收到可以被 angular context 處理的事件時, $digest 循環(huán)就會觸發(fā),遍歷所有的 $watch ,最后更新 dom。


Q7、兩個平級界面塊 a 和 b,如果 a 中觸發(fā)一個事件,有哪些方式能讓 b 知道?詳述原理

A:這個問題換一種說法就是,如何在平級界面模塊間進行通信。有兩種方法,一種是共用服務,一種是基于事件。

共用服務

在 Angular 中,通過 factory 可以生成一個單例對象,在需要通信的模塊 a 和 b 中注入這個對象即可。

基于事件

這個又分兩種方式

第一種是借助父 controller。在子 controller 中向父 controller 觸發(fā)( $emit )一個事件,然后在父 controller 中監(jiān)聽( $on )事件,再廣播( $broadcast )給子 controller ,這樣通過事件攜帶的參數(shù),實現(xiàn)了數(shù)據(jù)經(jīng)過父 controller,在同級 controller 之間傳播。

第二種是借助 $rootScope 。每個 Angular 應用默認有一個根作用域 $rootScope , 根作用域位于最頂層,從它往下掛著各級作用域。所以,如果子控制器直接使用 $rootScope 廣播和接收事件,那么就可實現(xiàn)同級之間的通信。


Q8、一個 angular 應用應當如何良好地分層?

目錄結(jié)構(gòu)的劃分

對于小型項目,可以按照文件類型組織,比如:

css
js
 controllers
 models
 services
 filters
templates 

但是對于規(guī)模較大的項目,最好按業(yè)務模塊劃分,比如:

css
modules
 account
  controllers
  models
  services
  filters
  templates
 disk
  controllers
  models
  services
  filters
  templates

modules 下最好再有一個 common 目錄來存放公共的東西。

邏輯代碼的拆分

作為一個 MVVM 框架,Angular 應用本身就應該按照 模型,視圖模型(控制器),視圖來劃分。

這里邏輯代碼的拆分,主要是指盡量讓 controller 這一層很薄。提取共用的邏輯到 service 中 (比如后臺數(shù)據(jù)的請求,數(shù)據(jù)的共享和緩存,基于事件的模塊間通信等),提取共用的界面操作到 directive 中(比如將日期選擇、分頁等封裝成組件等),提取共用的格式化操作到 filter 中等等。

在復雜的應用中,也可以為實體建立對應的構(gòu)造函數(shù),比如硬盤(Disk)模塊,可能有列表、新建、詳情這樣幾個視圖,并分別對應的有 controller,那么可以建一個 Disk 構(gòu)造函數(shù),里面完成數(shù)據(jù)的增刪改查和驗證操作,有跟 Disk 相關(guān)的 controller,就注入 Disk 構(gòu)造器并生成一個實例,這個實例就具備了增刪改查和驗證方法。這樣既層次分明,又實現(xiàn)了復用(讓 controller 層更薄了)。


Q9、angular 應用常用哪些路由庫,各自的區(qū)別是什么?

A:Angular1.x 中常用 ngRoute 和 ui.router。

區(qū)別

ngRoute 模塊是 Angular 自帶的路由模塊,而 ui.router 模塊是基于 ngRoute模塊開發(fā)的第三方模塊。

ui.router 是基于 state (狀態(tài))的, ngRoute 是基于 url 的,ui.router模塊具有更強大的功能,主要體現(xiàn)在視圖的嵌套方面。

使用 ui.router 能夠定義有明確父子關(guān)系的路由,并通過 ui-view 指令將子路由模版插入到父路由模板的 <div ui-view></div> 中去,從而實現(xiàn)視圖嵌套。而在 ngRoute 中不能這樣定義,如果同時在父子視圖中 使用了 <div ng-view></div> 會陷入死循環(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、分屬不同團隊進行開發(fā)的 angular 應用,如果要做整合,可能會遇到哪些問題,如何解決?

A:可能會遇到不同模塊之間的沖突。

比如一個團隊所有的開發(fā)在 moduleA 下進行,另一團隊開發(fā)的代碼在 moduleB 下

angular.module('myApp.moduleA', [])
  .factory('serviceA', function(){
    ...
  })
   
angular.module('myApp.moduleB', [])
  .factory('serviceA', function(){
    ...
  })  
   
angular.module('myApp', ['myApp.moduleA', 'myApp.moduleB'])  

會導致兩個 module 下面的 serviceA 發(fā)生了覆蓋。

貌似在 Angular1.x 中并沒有很好的解決辦法,所以最好在前期進行統(tǒng)一規(guī)劃,做好約定,嚴格按照約定開發(fā),每個開發(fā)人員只寫特定區(qū)塊代碼。


Q11、angular 的缺點有哪些?

強約束

導致學習成本較高,對前端不友好。

但遵守 AngularJS 的約定時,生產(chǎn)力會很高,對 Java 程序員友好。

不利于SEO

因為所有內(nèi)容都是動態(tài)獲取并渲染生成的,搜索引擎沒法爬取。

一種解決辦法是,對于正常用戶的訪問,服務器響應 AngularJS 應用的內(nèi)容;對于搜索引擎的訪問,則響應專門針對 SEO 的HTML頁面。

性能問題

作為 MVVM 框架,因為實現(xiàn)了數(shù)據(jù)的雙向綁定,對于大數(shù)組、復雜對象會存在性能問題。

可以用來 優(yōu)化 Angular 應用的性能 的辦法:

減少監(jiān)控項(比如對不會變化的數(shù)據(jù)采用單向綁定)

主動設置索引(指定 track by ,簡單類型默認用自身當索引,對象默認使用 $$hashKey ,比如改為 track by item.id )

降低渲染數(shù)據(jù)量(比如分頁,或者每次取一小部分數(shù)據(jù),根據(jù)需要再?。?

數(shù)據(jù)扁平化(比如對于樹狀結(jié)構(gòu),使用扁平化結(jié)構(gòu),構(gòu)建一個 map 和樹狀數(shù)據(jù),對樹操作時,由于跟扁平數(shù)據(jù)同一引用,樹狀數(shù)據(jù)變更會同步到原始的扁平數(shù)據(jù))

另外,對于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 可避免內(nèi)部的函數(shù)在使用 this 時導致上下文改變
  var vm = this;
  vm.a = 'aaa';
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號