为了提高下本身的技术水平,开始研究us中的方法,the first one,哈哈css
按照underscore 1.8.3版原本进行研究数组
先从集合方法开始研究bash
在us中集合的方法有25个,其中有each,map,reduce,find,filter,max,size等, 具体能够参照文档闭包
※ 今天分析第一个_.eachapp
使用:
_.each(list, iteratee, context)
iteratee为迭代函数,函数里具备三个参数
传入数组
_.each([1,2,3], alert); // 会分别alert弹出1,2,3
传入对象
_.each({
site: 'so.com',
from: 'weibo',
url: 'xxx.com/',
}, console.log);
// 这里会调用console.log直接打印出对象的值
// so.com site {site: "so.com", from: "weibo", url: "xxx.com/"}
// weibo from {site: "so.com", from: "weibo", url: "xxx.com/"}
// xxx.com/ url {site: "so.com", from: "weibo", url: "xxx.com/"}
复制代码
☆: list为数组时,三个参数分别为item数组的每一项,index数组的索引和arr数组自己。函数
☆:list为对象时,三个参数分别为value对象的值,key对象的key和obj对象自己ui
大体介绍了一下,那就废话很少说,开始写吧this
// 首先是个闭包环境,防止形成全局污染
(function() {
// _实际上是个构造函数,支持无new调用
// 而后将传入的参数赋给this._wrapped属性(暂时还不清楚这个属性的做用)
var _ = function (obj) {
// 若是obj是_的实例直接返回obj
if (obj instanceof _) {
return obj;
}
// 若是不是_的实例,那就new一个
if (!(this instanceof _)) {
return new _(obj);
}
// 将obj赋值给this._wrapped属性
this._wrapped = obj;
return obj;
}
// 写一些ES5原生方法放在这
var nativeKeys = Object.keys;
// 在_.each里调用
// optimizeCb函数来返回一个回调函数
var optimizeCb = function (fn, context) {
// 若是没有传context,就直接返回fn
if (context === void 0) {
return fn;
}
return function () {
// 返回一个函数指向context
return fn.apply(context, arguments);
}
};
// 在getLength时调用
// 传入key值
var property = function (key) {
return function (obj) {
return obj !== null && obj[key];
}
}
// 在_.isArrayLike调用
// 经过一个闭包的形势去获取length属性
var getLength = property('length');
// 在_.isArrayLike调用
// js里Length的最大值
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
// 在_.each里调用
// 数组或者类数组都有length属性
// 类型是number
// 而且不能大于js里的最大值
_.isArrayLike = function (obj) {
var length = getLength(obj);
return typeof length === 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
// 在_.each调用
// 遍历对象的key以数组的形势返回
_.keys = function (obj) {
// 若是传入的obj不是对象,就直接返回一个[]
// 那么接下来继续写个_.isObject
if (!_.isObject(obj)) return [];
// 若是支持ES5原生方法直接用,别客气
if (nativeKeys) return nativeKeys(obj);
// 不支持的话,那就手动写吧
var keys = [];
for (var key in obj) {
// 判断是否是私有属性,若是不是私有的会带上一堆原型上的,这可不行
if (_.has(obj, key)) keys.push(key);
}
return keys;
};
// 在_.keys调用
// hasOwnProperty.call会把指向放到obj上,防止hasOwnProperty被写在对象属性上修改掉
_.has = function (obj, key) {
return obj !== null && hasOwnProperty.call(obj, key);
};
// 在_.keys调用
// 这里的对象还要算上function
_.isObject = function (obj) {
var type = typeof obj;
return type === 'object' || type === 'function' && !!obj; // !!obj 判断数组用的 !![] true
};
// 源码在148行
// each方法直接挂载在_的prototype上
_.each = _.forEach = function (obj, iteratee, context) {
// 经过context获得iteratee迭代函数
iteratee = optimizeCb(iteratee, context);
var i, len;
//each其实就是循环两种类型,一种是数组(类数组),另外一种就是对象
// 用_.isArrayLike去判断是否是数组或类数组
if (_.isArrayLike(obj)) {
// 遍历数组
// 执行iteratee函数,分别传入item, index, arr
for (i = 0, len = obj.length; i < len; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj); // 拿到obj的key值以数组的形势返回,等同Object.keys
// 接下来遍历对象
for (i = 0, len = keys.length; i < len; i++) {
iteratee(obj[keys[i]], keys[i], obj); // value, key, obj
}
}
// 返回obj,供链式调用
return obj;
};
// 挂载在window对象上,这样其实很很差,经过export导出更合理
window._ = _;
}.call(this));
复制代码
总结一下吧: 遍历其实就是两种类型,先要判断两种类型,而后根据传入的iteratee迭代函数再遍历便可,写的很差请多多包涵!url