动手DIY一个underscorejs库及underscorejs源码分析2

接着第一篇《动手DIY一个underscorejs库及underscorejs源码分析1》

所有代码挂在我的github上。

1.兼容requirejs和seajs模块化

  • requirejs
    在代码的尾部加上
1
2
3
4
5
6
if (typeof define == 'function' && define.amd) {
//定义一个模块并且起个名字
define('_underscore', [], function() {
return _;
});
}

使用测试:代码请点我
demo3.html

1
2
3
<body>
<script data-main="demo3" src="lib/require.js"></script>
</body>

demo3.js

1
2
3
4
5
define(function(require) {
require(['../DIY/2/_underscore'], function() {
console.log(_);
});
});

  • 加上支持seajs的代码
1
2
3
4
5
6
7
8
9
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

1
2
3
4
<script src="lib/sea-debug.js"></script>
<script>
seajs.use('./demo2');
</script>

demo2.js

1
2
3
4
define(function(require, exports, module) {
var _ = require('../DIY/2/_underscore');
console.info(_);
});

2.支持nodejs

1
root._ = _;

修改为:

1
2
3
4
5
6
7
8
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}

3._.extend

使用:

1
2
console.log(_.extend({name: 'moe'}, {age: 50}));
//结果Object {name: "moe", age: 50}
1
2
3
4
5
6
7
8
9
//类似与_.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;
};

主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

很简单但是是默认的迭代器

1
2
3
_.identity = function(value) {
return value;
};

测试很简单

1
2
var obj1 = {name:'zry'};
console.log(obj1 === _.identity(obj1));//true

4.2 _.matcher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
_.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;

};

测试:

1
2
3
4
5
var obj2 = {selected: true, visible: true};
var ready = _.isMatch(obj2,{selected: true, visible: true});
//返回一个断言函数,这个函数会给你一个断言 可以用来辨别
//给定的对象是否匹配attrs指定键/值属性
console.log(ready);//true

4.3 _.property

property函数在第一篇博客中已经实现

1
_.property = property;

5._.map

1
2
3
4
5
6
7
8
9
10
11
12
_.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

1
2
3
4
5
6
7
8
_.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;
};

测试:

1
var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });//[2,4,6]

7.两个常用的工具函数_.escape,_.unescape`

7.1 _.escape

要过滤的字符串

1
2
3
4
5
6
7
8
var escapeMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;'
};

主函数

1
2
3
4
5
6
7
8
9
10
11
12
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个括号匹配的字符串。(对应于上述的$1,$2等。)
offset 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串时“bc”,那么这个参数将是1)
string 被匹配的原字符串。
1
_.escape = createEscaper(escapeMap);

测试:

1
console.log(_.escape('Curly, Larry & Moe')//Curly, Larry &amp; Moe

7.2 _.unescape

反转要过滤的字符串

1
2
3
4
5
6
7
8
9
10
_.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);

测试:

1
console.log(_.unescape('Curly, Larry &amp; Moe'));//Curly, Larry & Moe

参考阅读: