每天打算研究一个方法,用以提升下自己的技术水平,the first one,哈哈
使用:
_.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对象本身
大致介绍了一下,那就废话不多说,开始写吧
// 1首先是个闭包环境,防止造成全局污染
(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迭代函数再遍历即可,写的不好请多多包涵!