接着第一篇《動手DIY一個underscorejs庫及underscorejs源碼分析1》
所有代碼掛在我的github上。
1.兼容requirejs和seajs模塊化
- requirejs
在代碼的尾部加上
if (typeof define == 'function' && define.amd) {
//定義一個模塊並且起個名字
define('_underscore', [], function() {
return _;
});
}
使用測試:代碼請點我
demo3.html
<body>
<script data-main="demo3" src="lib/require.js"></script>
</body>
demo3.js
define(function(require) {
require(['../DIY/2/_underscore'], function() {
console.log(_);
});
});
- 加上支持seajs的代碼
if (typeof define == 'function' && define.amd) {
define('_underscore', [], function() {
return _;
});
} else if (typeof define == 'function' && define.cmd) { //seajs
define(function(require, exports, module) {
module.exports = _;
});
}
使用:
demo2.html
<script src="lib/sea-debug.js"></script>
<script>
seajs.use('./demo2');
</script>
demo2.js
define(function(require, exports, module) {
var _ = require('../DIY/2/_underscore');
console.info(_);
});
2.支持nodejs
root._ = _;
修改爲:
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
3._.extend
使用:
console.log(_.extend({name: 'moe'}, {age: 50}));
//結果Object {name: "moe", age: 50}
//類似與_.keys
_.allKeys = function(obj) {
if (!_.isObject(obj)) return [];
var keys = [];
for (var key in obj) keys.push(key);
// Ahem, IE < 9.
// if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
};
主函數:
var createAssigner = function(keysFunc, defaults) {
return function(obj) {
var length = arguments.length;
if (defaults) obj = Object(obj);
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!defaults || obj[key] === void 0) obj[key] = source[key];
//將參數(對象)放入到obj組合到一起
}
}
return obj;
};
};
_.extend = createAssigner(_.allKeys);
_.extendOwn = _.assign = createAssigner(_.keys);
4.重要內部函數cb
var builtinIteratee;
//如果是函數則返回上面說到的回調函數;
//如果是對象則返回一個能判斷對象是否相等的函數;
//默認返回一個獲取對象屬性的函數
var cb = function(value, context, argCount) {
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
if (value == null) return _.identity; //默認的迭代器
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
if (_.isObject(value)) return _.matcher(value);
return _.property(value);
};
_.iteratee = builtinIteratee = function(value, context) {
return cb(value, context, Infinity);
};
4.1 _.identity
很簡單但是是默認的迭代器
_.identity = function(value) {
return value;
};
測試很簡單
var obj1 = {name:'zry'};
console.log(obj1 === _.identity(obj1));//true
4.2 _.matcher
_.matcher = _.matches = function(attrs) {
attrs = _.extendOwn({}, attrs);
return function(obj) {
return _.isMatch(obj, attrs);
};
};
//兩個對象是不是全等於。給定的對象是否匹配attrs指定鍵/值屬性
_.isMatch = function(object, attrs) {
var keys = _.keys(attrs),
length = keys.length;
if (object == null) return !length;
var obj = Object(object);
for (var i = 0; i < length; i++) {
var key = keys[i];
if (attrs[key] !== obj[key] || !(key in obj)) return false;
}
return true;
};
測試:
var obj2 = {selected: true, visible: true};
var ready = _.isMatch(obj2,{selected: true, visible: true});
//返回一個斷言函數,這個函數會給你一個斷言 可以用來辨別
//給定的對象是否匹配attrs指定鍵/值屬性
console.log(ready);//true
4.3 _.property
property函數在第一篇博客中已經實現
_.property = property;
5._.map
_.map = _.collect = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
results = Array(length);
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
//返回的是(value,key,obj)
}
return results;
};
6._.filter
_.filter = _.select = function(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
_.each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
};
測試:
var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });//[2,4,6]
7.兩個常用的工具函數_.escape
,_.unescape`
7.1 _.escape
要過濾的字符串
var escapeMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'`': '`'
};
主函數
var createEscaper = function(map) {
var escaper = function(match) {//match 匹配的子串
return map[match];
};
var source = '(?:' + _.keys(map).join('|') + ')';
var testRegexp = RegExp(source);
var replaceRegexp = RegExp(source, 'g');
return function(string) {
string = string == null ? '' : '' + string;
return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
};
};
注意值了的string.replace
函數第二個參數是個函數,那麼返回的數據第一個是match(匹配的子串)
變量名 | 代表的值 |
---|---|
match | 匹配的子串。(對應於上述的$&。) |
p1,p2, … | 假如replace()方法的第一個參數是一個RegExp 對象,則代表第n個括號匹配的字符串。(對應於上述的 |
offset | 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串時“bc”,那麼這個參數將是1) |
string | 被匹配的原字符串。 |
_.escape = createEscaper(escapeMap);
測試:
console.log(_.escape('Curly, Larry & Moe')//Curly, Larry & Moe
7.2 _.unescape
反轉要過濾的字符串
_.invert = function(obj) {
var result = {};
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
result[obj[keys[i]]] = keys[i];
}
return result;
};
var unescapeMap = _.invert(escapeMap);
_.unescape = createEscaper(unescapeMap);
測試:
console.log(_.unescape('Curly, Larry & Moe'));//Curly, Larry & Moe
參考閱讀:
- http://underscorejs.org/
- underscorejs中文:http://www.bootcss.com/p/underscore/
- UnderscoreJS精巧而強大工具包
- JS高手進階之路:underscore源碼經典