underscore.js源码解析(二)
前几天我对underscore.js的整体结构做了分析,今天我将针对underscore封装的方法进行具体的分析,代码的一些解释都写在了注释里,那么废话不多说进入今天的正文。
没看过上一篇的可以猛戳这里:underscore.js源码解析(一)
underscore.js源码GitHub地址: https://github.com/jashkenas/underscore/blob/master/underscore.js本文解析的underscore.js版本是1.8.3
_.each
- _.each = _.forEach = function(obj, iteratee, context) {
- //optimizeCb( )是underscore内部用来执行函数的很重要的方法,这个我们后面再聊
- iteratee = optimizeCb(iteratee, context);
- var i, length;
- if (isArrayLike(obj)) {
- //判断是否是类数组,一般不会传入类似 {length: 3} 这样的数据
- for (i = 0, length = obj.length; i < length; i++) {
- iteratee(obj[i], i, obj);
- }
- } else {
- //对象处理,这个_.keys( )我们也后面再聊
- var keys = _.keys(obj);
- for (i = 0, length = keys.length; i < length; i++) {
- iteratee(obj[keys[i]], keys[i], obj);
- }
- }
- return obj;
- };
_.each结构很清晰,如果是数组,就遍历数组调用相应的处理方法,如果是对象的话,就遍历对象调用相应的处理方法。
其中判断是否为类数组的代码如下:
- var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
- //获取"length"属性
- var getLength = property('length');
- //判断是否是类数组
- var isArrayLike = function(collection) {
- var length = getLength(collection);
- return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
- };
类数组,即拥有 length 属性并且 length 属性值为 Number 类型的元素,例如数组、arguments、HTMLCollection 以及 NodeList 等等,当然 {length: 3} 这种对象也满足条件,但是_.each一般不会传这种值。
这种判断类数组的方法还是可以学习借鉴一下的。
optimizeCb
- var optimizeCb = function(func, context, argCount) {
- if (context === void 0) return func;
- //argCount为函数参数的个数,针对不同参数个数进行不同的处理
- switch (argCount == null ? 3 : argCount) {
- //为单值的情况,例如times函数
- case 1: return function(value) {
- return func.call(context, value);
- };
- //因为2个参数的情况没用被用到,所以在新版中被删除了
- //3个参数用于一些迭代器函数,例如map函数
- case 3: return function(value, index, collection) {
- return func.call(context, value, index, collection);
- };
- // 4个参数用于reduce和reduceRight函数
- case 4: return function(accumulator, value, index, collection) {
- return func.call(context, accumulator, value, index, collection);
- };
- }
- return function() {
- return func.apply(context, arguments);
- };
- };
cb和_.iteratee
- var cb = function(value, context, argCount) {
- //如果为空,则返回value本身(identity函数就是一个返回本身的函数 )
- if (value == null) return _.identity;
- //如果为函数,则改变所执行函数的作用域
- if (_.isFunction(value)) return optimizeCb(value, context, argCount);
- //如果是对象,判断是否匹配(matcher是一个用来判断是否匹配的,我们具体后续再聊)
- if (_.isObject(value)) return _.matcher(value);
- return _.property(value);
- };
- // 通过调用cb函数,生成每个元素的回调
- _.iteratee = function(value, context) {
- return cb(value, context, Infinity);
- };
_.keys
- _.keys = function(obj) {
- //如果不是对象,返回空数组
- if (!_.isObject(obj)) return [];
- //如果支持原生的方法,就调用原生的keys方法
- if (nativeKeys) return nativeKeys(obj);
- var keys = [];
- //记录所有属性名
- for (var key in obj) if (_.has(obj, key)) keys.push(key);
- // IE9以下枚举bug的兼容处理
- if (hasEnumBug) collectNonEnumProps(obj, keys);
- return keys;
- };
- //判断是否存在枚举bug
- var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
- //不可枚举的属性如下
- var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
- var collectNonEnumProps = function(obj, keys) {
- var nonEnumIdx = nonEnumerableProps.length;
- var constructor = obj.constructor;
- var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
- // Constructor单独处理部分.
- var prop = 'constructor';
- 如果对象和keys都存在constructor属性,则把他存入keys数组当中
- if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
- while (nonEnumIdx--) {
- prop = nonEnumerableProps[nonEnumIdx];
- //如果obj对象存在上面数组里那些不可枚举的属性但是不在原型中,并且keys数组里面也没有的话
- if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)){
- //将其添加进来
- keys.push(prop);
- }
- }
- };
既然都说到了keys那么顺带着也介绍一下allkeys吧。
_.allKeys
- _.allKeys = function(obj) {
- if (!_.isObject(obj)) return [];
- var keys = [];
- //获取所有的key
- for (var key in obj) keys.push(key);
- // 依然是IE9以下枚举bug的兼容处理
- if (hasEnumBug) collectNonEnumProps(obj, keys);
- return keys;
- };
其实keys和allKeys代码对比就少了if (_.has(obj, key)),allKeys是获取所有的,包括继承的
在介绍_.matcher之前,需要先介绍一下createAssigner 和_.isMatch。
createAssigner
- 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只有keys和allKeys两种,在下面_.extend和_.extendOwn中可以看到
- keys = keysFunc(source),
- l = keys.length;
- //进行拷贝处理
- for (var i = 0; i < l; i++) {
- var key = keys[i];
- //defaults是为了对defaults做单独处理而添加的参数,具体的解释_.defaults里做详细分析
- //在_.extend和_.extendOwn中default没有传值所以是underfinded,所以下面判断条件横为true,正常进行拷贝处理
- if (!defaults || obj[key] === void 0) obj[key] = source[key];
- }
- }
- return obj;
- };
- };
- //把后面的source拷贝到第一个对象
- _.extend = createAssigner(_.allKeys);
- //把后面的source拷贝到第一个对象(只拷贝实例的)
- // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
- _.extendOwn = _.assign = createAssigner(_.keys);
- //跟_.extend相似,只是当key相同,只会去第一次的键值对,不会被后面的覆盖
- _.defaults = createAssigner(_.allKeys, true);
- if (!defaults || obj[key] === void 0) obj[key] = source[key];
此时defaults参数为true,所以就要看obj[key] === void 0这句了,void 0返回的就是underfinded,那么这里换句通俗的话说就是判断key之前没有出现过相同的值时,才进行拷贝处理,如果后面出现相同的key,将不再进行拷贝操作,只保存第一次的键值对结果。
_.isMatch
- //用来判断该属性是否在对象中 (包括原型链)
- _.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];
- //如果两者值不等或者不在属性不在对象当中则返回false
- if (attrs[key] !== obj[key] || !(key in obj)) return false;
- }
- return true;
- };
_.matcher和_.matches
- // 判断对象是否匹配attrs的属性
- _.matcher = _.matches = function(attrs) {
- //进行拷贝
- attrs = _.extendOwn({}, attrs);
- return function(obj) {
- //用来判断该属性是否在对象中,上文有提及
- return _.isMatch(obj, attrs);
- };
- };
小结
今天介绍了underscore.js中部分封装函数,其他的会在后面的文章继续一一分析
感谢大家的观看,也希望能够和大家互相交流学习,有什么分析的不对的地方欢迎大家批评指出
参考资料
https://segmentfault.com/a/1190000000531871 http://www.w3cfuns.com/house/17398/note/class/id/bb6dc3cabae6651b94f69bbd562ff370underscore.js源码解析(二)的更多相关文章
- underscore.js源码解析(五)—— 完结篇
最近公司各种上线,所以回家略感疲惫就懒得写了,这次我准备把剩下的所有方法全部分析完,可能篇幅过长...那么废话不多说让我们进入正题. 没看过前几篇的可以猛戳这里: underscore.js源码解析( ...
- underscore.js源码解析(四)
没看过前几篇的可以猛戳这里: underscore.js源码解析(一) underscore.js源码解析(二) underscore.js源码解析(三) underscore.js源码GitHub地 ...
- underscore.js源码解析(三)
最近工作比较忙,做不到每周两篇了,周末赶着写吧,上篇我针对一些方法进行了分析,今天继续. 没看过前两篇的可以猛戳这里: underscore.js源码解析(一) underscore.js源码解析(二 ...
- underscore.js源码解析(一)
一直想针对一个框架的源码好好的学习一下编程思想和技巧,提高一下自己的水平,但是看过一些框架的源码,都感觉看的莫名其妙,看不太懂,最后找到这个underscore.js由于这个比较简短,一千多行,而且读 ...
- underscore.js源码解析【'_'对象定义及内部函数】
(function() { // Baseline setup // -------------- // Establish the root object, `window` (`self`) in ...
- underscore.js源码解析【对象】
// Object Functions // ---------------- // Keys in IE < 9 that won't be iterated by `for key in . ...
- underscore.js源码解析【函数】
// Function (ahem) Functions // ------------------ // Determines whether to execute a function as a ...
- underscore.js源码解析【数组】
// Array Functions // --------------- // Get the first element of an array. Passing **n** will retur ...
- underscore.js源码解析【集合】
// Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `f ...
随机推荐
- MySQL->AUTO_INCREMENT[20180516]
MySQL表格中自增长主键AUTO_INCREMENT使用,实现序列的最简单的方式 创建一个AUTO_INCREMENT自增的表 mysql> create table seq_test( ...
- Spring Bean d的作用域
在spring中,可以在<bean>元素的scop属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的. 默认情况下,spring只为每个在IOC容器里声明的bean创建唯 ...
- jQuery实现全选、不选和反选功能
jQuery结合Font Awesome字体图标实现全选.不选和反选功能 Font Awesome字体图标链接地址:http://www.fontawesome.com.cn/faicons/ 效果: ...
- laravel5.5源码笔记(三、门面类facade)
上次说了provider,那么这次来说说facade 首先是启动的源头,从laravel的kernel类中的$bootstrappers 数组,我们可以看到它的一些系统引导方法,其中的Register ...
- STM32 HAL库学习系列第2篇 GPIO配置
GPIO 库函数 基本就是使用以下几个函数 GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); void H ...
- MongoDB成为最受开发人员期待的数据库系统
本文翻译之MongoDB官方博客,原文地址:https://www.mongodb.com/blog/post/stack-overflow-research-developers-mongodb-m ...
- SQL学习笔记:函数
SQL函数 AVG select AVG(col) AS avgvalue from tablename select col2 from tablename where col1>(selec ...
- Oracle入门第三天(下)——子查询
一.子查询 1.子查询语法 SELECT select_list FROM table WHERE expr operator (SELECT select_list FROM table) 示例: ...
- Java基础——枚举
一.使用枚举类之前是如何实现枚举的 在JDK1.5之前,我们定义常量都是:public static fianl....:定义枚举也可以通过如下的方式: package com.jiangbei.t ...
- day3 直方图
1.绘制直方图 # coding=utf-8 import cv2 import numpy as np from matplotlib import pyplot as plt img1 = cv2 ...