1、angularjs的幾大特性是什么瞳浦?
雙向數(shù)據(jù)綁定担映、依賴注入、模板叫潦、指令蝇完、MVC/MVVM
2、列舉幾種常見的設(shè)計(jì)模式矗蕊,寫出沒個代表的含義短蜕?
MVC :model view controller
MVVM :model view viewModel
3、請描述angularjs的運(yùn)行過程傻咖?
angularjs編譯所有的HTML元素標(biāo)簽朋魔,然后在里面查找angular程序的入口 ng-app 每個元素上的指令是把所有指令收集起來根據(jù)優(yōu)先級依次編譯
4、ng-bind和ng-model的區(qū)別是什么没龙?
ng-bind只能展示數(shù)據(jù) ng-model可以操作數(shù)據(jù)
5铺厨、請描述$scope的特點(diǎn)還有其最大的父類缎玫?
隨創(chuàng)建作用域創(chuàng)建的一個變量,就代表controller所代表的作用域解滓,其持有的對象和方法可在當(dāng)前及其子作用域生效
6赃磨、原生js的延遲或回調(diào)在angularjs里能完美運(yùn)行嗎?怎么解決洼裤?可以用例子邻辉?
不能 需要用$apply來進(jìn)行傳播
7、{{ array | filter:{‘a(chǎn)ge’:23}:true }} 這個過濾里的true是什么意思腮鞍?
是否用angular.equals進(jìn)行比較后為真才返回
8值骇、自定義過濾創(chuàng)建后返回的是一個什么對象?
返回一個函數(shù)對象 并且函數(shù)內(nèi)要返回最后返回的對象
9移国、ng-repeat循環(huán)[1,3,2,4,3,4]數(shù)組會報錯嗎吱瘩?如果會怎么解決?
會因?yàn)橛兄貜?fù)的內(nèi)容 track by $index
10迹缀、angular常用的服務(wù)中value和constant最大的區(qū)別是什么使碾?
constant的創(chuàng)建要早于value 并且其可以在config配置中使用 value不行
11、常用服務(wù)中factory和service的最大區(qū)別是什么祝懂?
factory返回的對象當(dāng)我們使用它的時候手動初始化并返回票摇,而service是當(dāng)我們第一次使用的時候angular幫我們初始化一次,然后以后使用的時候返回的都是這個對象砚蓬,factory創(chuàng)建的服務(wù)是代表的是其后面函數(shù)的返回值矢门,這個返回值可以是任意類型,service不用返回灰蛙,直接操作的就是自己
12祟剔、怎么攔截服務(wù)?
在config配置里注入需要攔截的服務(wù)的名字+Provider來攔截
13摩梧、decorator的作用是什么峡扩?和攔截服務(wù)的區(qū)別是什么?
裝飾器不僅可以應(yīng)用在我們自己的服務(wù)上障本,也可以對angularjs核心服務(wù)進(jìn)行攔截、中斷甚至替換功能的操作响鹃,事實(shí)上angularjs的很多測試就是借助$provide.decorator()建立的驾霜、請寫一個配置路由的代碼段(只需要寫怎么聲明一個路由和其常用屬性的代碼段)
14、resolve的作用是什么买置?
如果設(shè)置了resolve屬性粪糙,angularjs會將列表中的元素都注入到控制器中,列表對象可以是鍵(鍵值是會被注入到控制器中依賴的名字)忿项,也可以是工廠(即可以是一個服務(wù)的名字)
15蓉冈、ngRoute默認(rèn)查找的路由是什么城舞?$routeProvider.otherwise(’/index’)是什么作用?
是/ 設(shè)置路由的意外指向到/index
16寞酿、$location.path(‘/home’)和$location.url(‘/home’)都可以進(jìn)行路由跳轉(zhuǎn)家夺,但是.path方法和.url方法最大的區(qū)別是什么?
.url方法:可以在跳轉(zhuǎn)的同時設(shè)置查詢串伐弹,返回url的整個路徑拉馋; 而.path方法:返回的路徑不包括?后面的部分惨好;
17煌茴、什么是跨域,請簡要描述跨域的場景日川?
協(xié)議 域名 端口號有一個不一樣就是跨域蔓腐,也就是不同域名之間的訪問;
18龄句、常使用的跨域方案就哪兩種回论?分別描述其利用的原理?
jsonp; post請求設(shè)置請求頭 撒璧; jsonp利用的是script可以訪問外部信息的原理發(fā)送請求并且利用jsonp協(xié)議進(jìn)行數(shù)據(jù)交互 post設(shè)置請求頭跳過預(yù)請求來實(shí)現(xiàn)跨域
19透葛、請寫出$http網(wǎng)絡(luò)請求的幾種寫法,最少兩種
$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 的區(qū)別有哪些僚害?
第一點(diǎn)區(qū)別是,ng-if
在后面表達(dá)式為 true 的時候才創(chuàng)建這個 dom 節(jié)點(diǎn)繁调,ng-show
是初始時就創(chuàng)建了萨蚕,用 display:block
和 display:none
來控制顯示和不顯示。
第二點(diǎn)區(qū)別是蹄胰,ng-if
會(隱式地)產(chǎn)生新作用域岳遥,ng-switch
、 ng-include
等會動態(tài)創(chuàng)建一塊界面的也是如此裕寨。
這樣會導(dǎo)致浩蓉,在 ng-if
中用基本變量綁定 ng-model
,并在外層 div 中把此 model 綁定給另一個顯示區(qū)域宾袜,內(nèi)層改變時捻艳,外層不會同步改變,因?yàn)榇藭r已經(jīng)是兩個變量了庆猫。
<p>{{name}}</p><div ng-if="true"> <input type="text" ng-model="name"></div>
21认轨、ng-show
不存在此問題,因?yàn)樗蛔詭б患壸饔糜颉?br>
避免這類問題出現(xiàn)的辦法是月培,始終將頁面中的元素綁定到對象的屬性(data.x)而不是直接綁定到基本變量(x)上嘁字。
詳見 AngularJS 中的作用域
22恩急、ng-repeat迭代數(shù)組的時候,如果數(shù)組中有相同值纪蜒,會有什么問題衷恭,如何解決?
會提示 Duplicates in a repeater are not allowed.
加 track by $index
可解決霍掺。當(dāng)然匾荆,也可以 trace by 任何一個普通的值,只要能唯一性標(biāo)識數(shù)組中的每一項(xiàng)即可(建立 dom 和數(shù)據(jù)之間的關(guān)聯(lián))杆烁。
ng-click 中寫的表達(dá)式牙丽,能使用 JS 原生對象上的方法嗎?
不止是 ng-click 中的表達(dá)式兔魂,只要是在頁面中烤芦,都不能直接調(diào)用原生的 JS 方法,因?yàn)檫@些并不存在于與頁面對應(yīng)的 Controller 的 $scope 中析校。
舉個栗子:
<p>{{parseInt(55.66)}}<p>
會發(fā)現(xiàn)构罗,什么也沒有顯示。
但如果在 $scope 中添加了這個函數(shù):
$scope.parseInt = function(x){ return parseInt(x);}
這樣自然是沒什么問題了智玻。
23遂唧、對于這種需求,使用一個 filter 或許是不錯的選擇:
<p>{{13.14 | parseIntFilter}}</p>app.filter('parseIntFilter', function(){ return function(item){ return parseInt(item); }})
{{now | 'yyyy-MM-dd'}}
這種表達(dá)式里面吊奢,豎線和后面的參數(shù)通過什么方式可以自定義盖彭?
filter,格式化數(shù)據(jù)页滚,接收一個輸入召边,按某規(guī)則處理,返回處理結(jié)果裹驰。
內(nèi)置 filter
ng 內(nèi)置的 filter 有九種:
date(日期)
currency(貨幣)
limitTo(限制數(shù)組或字符串長度)
orderBy(排序)
lowercase(小寫)
uppercase(大寫)
number(格式化數(shù)字隧熙,加上千位分隔符,并接收參數(shù)限定小數(shù)點(diǎn)位數(shù))
filter(處理一個數(shù)組幻林,過濾出含有某個子串的元素)
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');
24、自定義 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; }})
25沪饺、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,返回一個實(shí)例化對象
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 的 providerapp.config(function(FooServiceProvider){ FooServiceProvider.setConfigData('config data');});
從底層實(shí)現(xiàn)上來看蛾扇,service 調(diào)用了 factory,返回其實(shí)例魏滚;factory 調(diào)用了 provider镀首,返回其 $get
中定義的內(nèi)容。factory 和 service 功能類似鼠次,只不過 factory 是普通 function更哄,可以返回任何東西(return 的都可以被訪問,所以那些私有變量怎么寫腥寇,你懂的)成翩;service 是構(gòu)造器,可以不返回(綁定到 this 的都可以被訪問)赦役;provider 是加強(qiáng)版 factory麻敌,返回一個可配置的 factory。
詳見 AngularJS 之 Factory vs Service vs Provider
angular 的數(shù)據(jù)綁定采用什么機(jī)制掂摔?詳述原理
臟檢查機(jī)制术羔。
雙向數(shù)據(jù)綁定是 AngularJS 的核心機(jī)制之一。當(dāng) view 中有任何數(shù)據(jù)變化時乙漓,會更新到 model 级历,當(dāng) model 中數(shù)據(jù)有變化時,view 也會同步更新叭披,顯然寥殖,這需要一個監(jiān)控。
原理就是趋观,Angular 在 scope 模型上設(shè)置了一個 監(jiān)聽隊(duì)列扛禽,用來監(jiān)聽數(shù)據(jù)變化并更新 view 。每次綁定一個東西到 view 上時 AngularJS 就會往 $watch
隊(duì)列里插入一條 $watch
皱坛,用來檢測它監(jiān)視的 model 里是否有變化的東西编曼。當(dāng)瀏覽器接收到可以被 angular context 處理的事件時,$digest
循環(huán)就會觸發(fā)剩辟,遍歷所有的 $watch
掐场,最后更新 dom。
舉個栗子
<button ng-click="val=val+1">increase 1</button>
click 時會產(chǎn)生一次更新的操作(至少觸發(fā)兩次 $digest
循環(huán))
按下按鈕
瀏覽器接收到一個事件贩猎,進(jìn)入到 angular context
26熊户、$digest
循環(huán)開始執(zhí)行,查詢每個 $watch
是否變化
由于監(jiān)視 $scope
.val 的 $watch
報告了變化吭服,因此強(qiáng)制再執(zhí)行一次 $digest
循環(huán)
新的 $digest
循環(huán)未檢測到變化
瀏覽器拿回控制器嚷堡,更新 $scope
.val 新值對應(yīng)的 dom
$digest
循環(huán)的上限是 10 次(超過 10次后拋出一個異常,防止無限循環(huán))艇棕。
詳見 關(guān)于 AngularJS 的數(shù)據(jù)綁定
兩個平級界面塊 a 和 b蝌戒,如果 a 中觸發(fā)一個事件串塑,有哪些方式能讓 b 知道?詳述原理
這個問題換一種說法就是北苟,如何在平級界面模塊間進(jìn)行通信桩匪。有兩種方法,一種是共用服務(wù)友鼻,一種是基于事件傻昙。
共用服務(wù)
在 Angular 中,通過 factory 可以生成一個單例對象彩扔,在需要通信的模塊 a 和 b 中注入這個對象即可妆档。
基于事件
這個又分兩種方式
第一種是借助父 controller。在子 controller 中向父 controller 觸發(fā)($emit
)一個事件借杰,然后在父 controller 中監(jiān)聽($on
)事件过吻,再廣播($broadcast
)給子 controller ,這樣通過事件攜帶的參數(shù)蔗衡,實(shí)現(xiàn)了數(shù)據(jù)經(jīng)過父 controller纤虽,在同級 controller 之間傳播。
第二種是借助 $rootScope
绞惦。每個 Angular 應(yīng)用默認(rèn)有一個根作用域 $rootScope
逼纸, 根作用域位于最頂層,從它往下掛著各級作用域济蝉。所以杰刽,如果子控制器直接使用 $rootScope
廣播和接收事件,那么就可實(shí)現(xiàn)同級之間的通信王滤。
詳見 AngularJS 中 Controller 之間的通信
一個 angular 應(yīng)用應(yīng)當(dāng)如何良好地分層贺嫂?
目錄結(jié)構(gòu)的劃分
對于小型項(xiàng)目,可以按照文件類型組織雁乡,比如:
cssjs controllers models services filterstemplates
但是對于規(guī)模較大的項(xiàng)目第喳,最好按業(yè)務(wù)模塊劃分,比如:
cssmodules account controllers models services filters templates disk controllers models services filters templates
modules 下最好再有一個 common 目錄來存放公共的東西踱稍。
邏輯代碼的拆分
作為一個 MVVM 框架曲饱,Angular 應(yīng)用本身就應(yīng)該按照 模型,視圖模型(控制器)珠月,視圖來劃分扩淀。
這里邏輯代碼的拆分,主要是指盡量讓 controller 這一層很薄啤挎。提取共用的邏輯到 service 中 (比如后臺數(shù)據(jù)的請求驻谆,數(shù)據(jù)的共享和緩存,基于事件的模塊間通信等),提取共用的界面操作到 directive 中(比如將日期選擇旺韭、分頁等封裝成組件等)氛谜,提取共用的格式化操作到 filter 中等等。
在復(fù)雜的應(yīng)用中区端,也可以為實(shí)體建立對應(yīng)的構(gòu)造函數(shù),比如硬盤(Disk)模塊澳腹,可能有列表织盼、新建、詳情這樣幾個視圖酱塔,并分別對應(yīng)的有 controller沥邻,那么可以建一個 Disk 構(gòu)造函數(shù),里面完成數(shù)據(jù)的增刪改查和驗(yàn)證操作羊娃,有跟 Disk 相關(guān)的 controller唐全,就注入 Disk 構(gòu)造器并生成一個實(shí)例,這個實(shí)例就具備了增刪改查和驗(yàn)證方法蕊玷。這樣既層次分明邮利,又實(shí)現(xiàn)了復(fù)用(讓 controller 層更薄了)。
參考 AngularJS在蘇寧云中心的深入實(shí)踐
27垃帅、angular 應(yīng)用常用哪些路由庫延届,各自的區(qū)別是什么?
Angular1.x 中常用 ngRoute 和 ui.router贸诚,還有一種為 Angular2 設(shè)計(jì)的 new router(面向組件)方庭。后面那個沒在實(shí)際項(xiàng)目中用過辕万,就不講了咧纠。
無論是 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)系的路由鳖敷,并通過 ui-view 指令將子路由模版插入到父路由模板的 <div ui-view></div>
中去脖苏,從而實(shí)現(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' })
28、如果通過angular的directive規(guī)劃一套全組件化體系,可能遇到哪些挑戰(zhàn)亦歉?
沒有自己用 directive 做過一全套組件恤浪,講不出。
能想到的一點(diǎn)是肴楷,組件如何與外界進(jìn)行數(shù)據(jù)的交互水由,以及如何通過簡單的配置就能使用吧。
分屬不同團(tuán)隊(duì)進(jìn)行開發(fā)的 angular 應(yīng)用赛蔫,如果要做整合砂客,可能會遇到哪些問題,如何解決呵恢?
可能會遇到不同模塊之間的沖突鞠值。
比如一個團(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'])
會導(dǎo)致兩個 module 下面的 serviceA 發(fā)生了覆蓋渗钉。
貌似在 Angular1.x 中并沒有很好的解決辦法彤恶,所以最好在前期進(jìn)行統(tǒng)一規(guī)劃,做好約定鳄橘,嚴(yán)格按照約定開發(fā)声离,每個開發(fā)人員只寫特定區(qū)塊代碼。
angular 的缺點(diǎn)有哪些挥唠?
強(qiáng)約束
導(dǎo)致學(xué)習(xí)成本較高抵恋,對前端不友好。
但遵守 AngularJS 的約定時宝磨,生產(chǎn)力會很高弧关,對 Java 程序員友好。
不利于 SEO
因?yàn)樗袃?nèi)容都是動態(tài)獲取并渲染生成的唤锉,搜索引擎沒法爬取世囊。
一種解決辦法是,對于正常用戶的訪問窿祥,服務(wù)器響應(yīng) AngularJS 應(yīng)用的內(nèi)容株憾;對于搜索引擎的訪問,則響應(yīng)專門針對 SEO 的HTML頁面晒衩。
性能問題
作為 MVVM 框架嗤瞎,因?yàn)閷?shí)現(xiàn)了數(shù)據(jù)的雙向綁定,對于大數(shù)組听系、復(fù)雜對象會存在性能問題贝奇。
可以用來 優(yōu)化 Angular 應(yīng)用的性能 的辦法:
減少監(jiān)控項(xiàng)(比如對不會變化的數(shù)據(jù)采用單向綁定)
主動設(shè)置索引(指定 track by
,簡單類型默認(rèn)用自身當(dāng)索引靠胜,對象默認(rèn)使用 $$hashKey
掉瞳,比如改為 track by item.id
)
降低渲染數(shù)據(jù)量(比如分頁毕源,或者每次取一小部分?jǐn)?shù)據(jù),根據(jù)需要再壬孪啊)
數(shù)據(jù)扁平化(比如對于樹狀結(jié)構(gòu)霎褐,使用扁平化結(jié)構(gòu),構(gòu)建一個 map 和樹狀數(shù)據(jù)该镣,對樹操作時冻璃,由于跟扁平數(shù)據(jù)同一引用,樹狀數(shù)據(jù)變更會同步到原始的扁平數(shù)據(jù))
另外损合,對于Angular1.x 俱饿,存在 臟檢查 和 模塊機(jī)制 的問題。
移動端
可嘗試 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 可避免內(nèi)部的函數(shù)在使用 this 時導(dǎo)致上下文改變 var vm = this; vm.a = 'aaa';}
原理
從源碼實(shí)現(xiàn)上來看眷蜓,controllerAs 語法只是把 controller 這個對象的實(shí)例用 as 別名在 $scope 上創(chuàng)建了一個屬性。
if (directive.controllerAs) { locals.$scope[directive.controllerAs] = controllerInstance;}
但是這樣做胎围,除了上面提到的使 controller 更加 POJO 外吁系,還可以避免遇到 AngularJS 作用域相關(guān)的一個坑(就是上文中 ng-if 產(chǎn)生一級作用域的坑,其實(shí)也是 javascript 原型鏈繼承中值類型繼承的坑白魂。因?yàn)槭褂?controllerAs 的話 view 上所有字段都綁定在一個引用的屬性上汽纤,比如 vm.xx,所以坑不再存在)福荸。
<div ng-controller="TestCtrl as vm"> <p>{{name}}</p> <div ng-if="vm.name"> <input type="text" ng-model="vm.name"> </div></div>
問題
使用 controllerAs 會遇到的一個問題是蕴坪,因?yàn)闆]有注入 $scope
,導(dǎo)致 $emit
敬锐、 $broadcast
背传、 $on
、 $watch
等 $scope
下的方法無法使用台夺。這些跟事件相關(guān)的操作可以封裝起來統(tǒng)一處理径玖,或者在單個 controller 中引入 $scope
,特殊對待谒养。
參考 angular controller as syntax vs scope
詳述 angular 的 “依賴注入”
栗子
依賴注入是一種軟件設(shè)計(jì)模式挺狰,目的是處理代碼之間的依賴關(guān)系明郭,減少組件間的耦合。
舉個栗子丰泊,如果沒有使用 AngularJS薯定,想從后臺查詢數(shù)據(jù)并在前端顯示,可能需要這樣做:
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);
但是瞳购,如果在調(diào)用 render 的時候不傳參數(shù)话侄,像下面這樣,會報錯学赛,因?yàn)檎也坏?el 和 http(定義的時候依賴了年堆,運(yùn)行的時候不會自動查找依賴項(xiàng))
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 運(yùn)行的時候变丧,調(diào)用 myCtrl,自動做了 $scope
和 $http
兩個依賴性的注入绢掰。
原理
AngularJS 是通過構(gòu)造函數(shù)的參數(shù)名字來推斷依賴服務(wù)名稱的痒蓬,通過 toString()
來找到這個定義的 function 對應(yīng)的字符串,然后用正則解析出其中的參數(shù)(依賴項(xiàng))滴劲,再去依賴映射中取到對應(yīng)的依賴攻晒,實(shí)例化之后傳入。
簡化一下班挖,大概是這樣:
var inject = { // 存儲依賴映射關(guān)系 storage: {}, // 注冊依賴 register: function(name, resource){ this.storage[name] = resource; }, // 解析出依賴并調(diào)用 resolve: function(target){ var self = this; var FN_ARGS = /function\s*[(](\s([^)]))/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 的栗子這樣改造一下就可以調(diào)用了
inject.register('el', animalBox);inject.register('ajax', httpRequest);render = inject.resolve(render);render();
問題
因?yàn)?AngularJS 的 injector 是假設(shè)函數(shù)的參數(shù)名就是依賴的名字,然后去查找依賴項(xiàng)萧芙,那如果按前面栗子中那樣注入依賴给梅,代碼壓縮后(參數(shù)被重命名了),就無法查找到依賴項(xiàng)了末购。
// 壓縮前function myCtrl = ($scope, $http){ ...}// 壓縮后function myCtrl = (a, b){ ...}
所以破喻,通常會使用下面兩種方式注入依賴(對依賴添加的順序有要求)。
數(shù)組注釋法
myApp.controller('myCtrl', ['$scope', '$http', function($scope, $http){ ...}])
顯式 $inject
myApp.controller('myCtrl', myCtrl);function myCtrl = ($scope, $http){ ...}myCtrl.$inject = ['$scope', '$http'];
29盟榴、補(bǔ)充
對于一個 DI 容器曹质,必須具備三個要素:依賴項(xiàng)的注冊,依賴關(guān)系的聲明和對象的獲取擎场。
在 AngularJS 中羽德,module 和 $provide 都可以提供依賴項(xiàng)的注冊;內(nèi)置的 injector 可以獲取對象(自動完成依賴注入)迅办;依賴關(guān)系的聲明宅静,就是前面問題中提到的那樣。
下面是個栗子
// 對于 module站欺,傳遞參數(shù)不止一個姨夹,代表新建模塊纤垂,空數(shù)組代表不依賴其他模塊// 只有一個參數(shù)(模塊名),代表獲取模塊// 定義 myApp磷账,添加 myApp.services 為其依賴項(xiàng)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] 自己實(shí)現(xiàn)一個簡單的依賴注入
理解angular中的module和injector逃糟,即依賴注入
AngularJS中的依賴注入實(shí)際應(yīng)用場景
31吼鱼、如何看待angular2
相比 Angular1.x,Angular2的改動很大绰咽,幾乎算是一個全新的框架菇肃。
基于 TypeScript(可以使用 TypeScript 進(jìn)行開發(fā)),在大型項(xiàng)目團(tuán)隊(duì)協(xié)作時取募,強(qiáng)語言類型更有利琐谤。
組件化,提升開發(fā)和維護(hù)的效率玩敏。
還有 module 支持動態(tài)加載笑跛,new router,promise的原生支持等等聊品。
迎合未來標(biāo)準(zhǔn),吸納其他框架的優(yōu)點(diǎn)几苍,值得期待翻屈,不過同時要學(xué)習(xí)的東西也更多了(ES next、TS妻坝、Rx等)伸眶。
參考
浴火重生的Angular