CacheFactoryProvider 簡介
原始碼裡是這麼描述的:
Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
them.
意思就是通過cacheFactory可以構造一個Cache物件來給予訪問和執行許可權。
這個Cache物件官方文件是這麼說的:
A cache object used to store and retrieve data, primarily used by $http and the script directive to cache templates and other data.
用我自己的話來說就是 提供儲存和訪問快取物件的服務,angular內部主要被$http,script指令用於
快取template和其他資料。我們自己可以在Controller內部使用。
CacheFactoryProvider 用法
<!doctype html>
<html lang="en" ng-app="myapp">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
<script src="js/angular.js"></script>
</head>
<body>
<div ng-controller="MyController">
</div>
<script >
var app=angular.module('myapp',[]);
app.controller('MyController',function($scope,$cacheFactory){
var myCache = $cacheFactory("myCache",{capacity: 6});
//var myCache1 = $cacheFactory("myCache",{capacity: 6}); //會報錯
myCache.put("name","john");
myCache.put("name1","wonder");
myCache.put("name","john");
});
app.controller('getCacheController',['$scope','$cacheFactory',
function($scope,$cacheFactory){
var cache = $cacheFactory.get('myCache');
var name = cache.get('name');
console.log(name); //列印john
}]);
</script>
</body>
</html>
看了上面這個一個簡單的例子,讀者可能會產生如下疑惑:
- 不是名為CacheFactoryProvider嗎,怎麼在程式碼裡只看到cacheFactory呢?
- cacheFactory是怎麼儲存物件的?
下面我們來依次解答這兩個問題
$cacheFactory的注入
我們首先來看第一個問題,這個問題要牽涉到angular裡面的依賴注入機制,我們前面的分析也講過,
angular會在啟動之前通過呼叫publishExternalAPI
函式先發布一些擴充套件API,同時定義ng
模組,在定義ng模組的時候就傳入了注入provider的方法
angularModule('ng', ['ngLocale'], ['$provide',
//通過引數注入$provide
function ngModule($provide) {
///部分程式碼省略
$provide.provider({
$cacheFactory: $CacheFactoryProvider,
});
}])
$cacheFactory出現了,它是通過javascript的鍵值物件作為鍵傳給provider方法。那麼它是如何儲存
物件的呢?首先我們看它的定義:
CacheFactoryProvider的定義
內部定義了依賴注入核心的$get
方法,$get
方法返回cacheFactory方法(也就是上面例項程式碼裡的
$cacheFactory
引數)。
function $CacheFactoryProvider() {
//定義$get方法供依賴呼叫
//controller中獲取cacheFactory時會呼叫此方法
//這個$get方法也是獲取provider的關鍵方法
this.$get = function() {
var caches = {};//閉包的一個運用
function cacheFactory(cacheId, options) {
//部分程式碼省略
//可以用if來判斷
if (cacheId in caches) {//如果caches中已經存在cacheId
//例項程式碼裡丟擲的錯誤就在此處、
//統一呼叫minErr函式
throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!"
, cacheId);
}
var size = 0,
//把options 和{id:cacheId} 放入{} 中 不是深拷貝
stats = extend({}, options, {id: cacheId}),
data = createMap(),//通過Object.create(null) 建立個空物件
capacity = (options && options.capacity) || Number.MAX_VALUE,
lruHash = createMap(),
freshEnd = null,
staleEnd = null;
//返回caches中的一個物件
return caches[cacheId] = {
//省略部分程式碼
//儲存裡講解
put:function(key,value){
},
get: function(key) {
},
remove: function(key) {
},
removeAll: function() {
},
destroy: function() {
},
info: function() {
}
}
//重新整理節點次序
function refresh(entry) {
}
//
function link(nextEntry, prevEntry) {
}
}
//所有的快取
cacheFactory.info = function() {
var info = {};
forEach(caches, function(cache, cacheId) {
info[cacheId] = cache.info();
});
return info;
};
cacheFactory.get = function(cacheId) {
return caches[cacheId];
};
return cacheFactory;
}
}
CacheFactoryProvider的儲存
儲存分為這幾個核心方法:put
,refresh
,remove
,link
put函式
value會放入data物件中,key會放入lruHash連結串列
put: function(key, value) {
if (isUndefined(value)) return;
//如果設定的capcity小於maxvalue
if (capacity < Number.MAX_VALUE) {
//lruHash 存了當前的key 還有可能是 p 和n (previous和next)
var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
//重新整理各節點的次序
refresh(lruEntry);//把當前entry放入連結串列末尾
}
//如果key 在data裡不存在 那麼增加size
if (!(key in data)) size++;
data[key] = value;
//當大於capacity時 會清除最早加入的那個
if (size > capacity) {
this.remove(staleEnd.key);//移除淘汰節點stableEnd
}
return value;
}
get函式
Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object
獲取儲存在cache物件中的指定資料
get: function(key) {
if (capacity < Number.MAX_VALUE) {
var lruEntry = lruHash[key];
if (!lruEntry) return;
// 獲取first的時候 因為staleEnd為first 所以會讓staleEnd指向 second
// 內部會執行link 使得 second.p = null
// first.p = third third.n = first
//stableEnd為 second freshEnd為first
refresh(lruEntry);
}
return data[key];
}
remove函式
Removes an entry from the {@link $cacheFactory.Cache Cache} object.
從cache物件刪除一個entry
remove: function(key) {
//如果capacity小於maxvalue
if (capacity < Number.MAX_VALUE) {
//先取出當前key的entry
var lruEntry = lruHash[key];
if (!lruEntry) return;
//第一次超過時 freshEnd 為third lryEntry為first
if (lruEntry == freshEnd) freshEnd = lruEntry.p;
//第一次超過時 staleEnd 為first lryEntry為first
//所以 會讓 stalEnd 指向second 以便於下次移除時
if (lruEntry == staleEnd) staleEnd = lruEntry.n;
//把淘汰節點的一個節點選中
//第一次超過時 lryEntry.n為 second lryEntry.p 為null
//執行結果為 second.p = null
link(lruEntry.n,lruEntry.p);
//把當前key從lruHash中刪除
delete lruHash[key];
}
if (!(key in data)) return;
delete data[key];
size--;
}
refresh函式
makes the entry
the freshEnd of the LRU linked list。
把entry 放入連結串列的末尾
function refresh(entry) {
if (entry != freshEnd) {
if (!staleEnd) { //staleEnd為空那麼就讓他指向當前entry
staleEnd = entry;
} else if (staleEnd == entry) {
//如果淘汰節點等於當前節點
staleEnd = entry.n; //用於把 當前的下一個節點 用作淘汰節點
}
//放入第一個元素時 entry.n,entry.p都為undefined
link(entry.n, entry.p); //當前的上一個節點 和當前的下一個節點
link(entry, freshEnd); // 當前的節點 和 最新的末尾節點
freshEnd = entry;
freshEnd.n = null;
//第一次執行完 結果為: freshEnd = first staleEnd為first
//first.p=null first.n=null
//第二次執行完 結果為:freshEnd = second staleEnd為first
// first.p=null first.n= second
// scecond.p = first scecond.n = null
//第三次執行完 freshEnd = third staleEnd為first first.p=null
//first.n= second
// second.p = first second.n = null
// third.p = second third.n = null
}
}
link函式
bidirectionally(雙向連結串列) links two entries of the LRU linked list
雙向連結連結串列裡的兩個元素。
function link(nextEntry, prevEntry) {
//undefined 不等於undefined
if (nextEntry != prevEntry) {
//
if (nextEntry) nextEntry.p = prevEntry;
//p stands for previous, 'prev' didn't minify
if (prevEntry) prevEntry.n = nextEntry;
//n stands for next, 'next' didn't minify
}
}
歡迎關注我的公眾號,獲取最新原始碼解析文章!