最近是在所在实习公司的第一个sprint,有个朋友又请假了,所以任务比较重,一直这么久都没怎么更新了,这个周末赖了个床,纠结了一会儿决定还是继续写这个系列,虽然比较乏味,但是学到的东西还是很多的。

之前主要是针对函数处理部分的API做解读,经过那些天的努力,基本已经解读完了,现在把重点移到数组上。对于数组处理API的解读,从这篇文章开始。

flatten是一个很基础的函数,在Underscore中也算是一个工具函数,为了方便以后的讲解,今天先阅读flatten函数的源码。

首先,我们带着问题来阅读源码,如果你参加面试,考官让你手写一个展开数组的函数,你会怎么写?

实现一个flatten函数

我们接受的参数应该是一个数组,我们可以使用一个叫array的变量表示它,它的返回值应该是一个数组,使用result表示:

function flatten(array) {
var result = [];
// ... 展开代码
return result
}

  

然后我们应该对传入的数组进行类型验证,如果不是数组,我们应该抛出一个类型异常:

function flatten(array) {
var result = [];
if(Object.prototype.toString.call(array) !== '[object Array]')
throw new TypeError('Please pass a array-type object as parameter to flatten function');
else {
// ... 展开代码
}
return result
}

  

这样就可以保证我们接收到的参数是一个数组,接下来我们应该遍历array参数,对于它的每一项,如果不是数组,我们就将其添加到result中,否则继续展开:

function flatten(array) {
var result = [];
if(Object.prototype.toString.call(array) !== '[object Array]')
throw new TypeError('Please pass a array-type object as parameter to flatten function');
else {
for(var i = 0; i < array.length; i++) {
if(Object.prototype.toString.call(array[i]) === '[object Array]') {
// ... 继续展开。
}
else {
result.push(array[i]);
}
}
}
return result
}

  

当数组中的项还是一个数组时,我们应当如何展开呢? 由于不确定到底是嵌套了多少层数组,所以最好是使用递归来展开,但是有新的问题,我们的flatten函数返回一个数组结果,但是我们如何把递归结果返回给我们的result呢,是使用concat方法还是怎样?

由于函数中对象类型的参数是引用传值,所以我们可以把result传递给flatten自身,使其直接修改result即可:

function flatten(array, result) {
var result = result || [];
if(Object.prototype.toString.call(array) !== '[object Array]')
throw new TypeError('Please pass a array-type object as parameter to flatten function');
else {
for(var i = 0; i < array.length; i++) {
if(Object.prototype.toString.call(array[i]) === '[object Array]') {
// ... 递归展开。
arguments.callee(array[i], result);
}
else {
result.push(array[i]);
}
}
}
return result
}

  

以上函数,就基本实现了flatten的功能,再美化一下:

var flatten = function(array, result) {
var result = result || [];
var length = array.length;
var toString = Object.prototype.toString;
var type = toString.call(array);
if(type !== '[object Array]')
throw new TypeError('The parameter you passed is not a array');
else {
for(var i = 0; i < length; i++) {
if(toString.call(array[i]) !== '[object Array]') {
result.push(array[i]);
}
else {
arguments.callee(array[i], result);
}
}
}
return result;
}

  

大家可以把上面这段代码拷贝到控制台进行实验。

Underscore中的flatten函数

通过我们自己亲手实现一个flatten函数,阅读Underscore源码就变得简单了。 下面是Underscore中flatten函数的源码(附注释):

var flatten = function (input, shallow, strict, output) {
output = output || [];
var idx = output.length;
//遍历input参数。
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
// Flatten current level of array or arguments object.
//如果input数组的元素是数组或者类数组对象,根据是否shallow来展开,如果shallow为true,那么只展开一级。
if (shallow) {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
} else {
//如果shallow为false,那么递归展开所有层级。
flatten(value, shallow, strict, output);
idx = output.length;
}
} else if (!strict) {
//如果value不是数组或类数组对象,并且strict为false。
//那么直接将value添加到输出数组,否则忽略value。
output[idx++] = value;
}
}
return output;
};

  

Underscore实现的flatten更加强大,它支持类数组对象而不仅仅是数组,并且它多了两个参数——shallow和strict。

当shallow为true时,flatten只会把输入数组的数组子项展开一级,如果shallow为false,那么会全部展开。

当strict为false时,只要是非数组对象,flatten都会直接添加到output数组中;如果strict为true,那么会无视input数组中的非类数组对象。

更多Underscore源码阅读:GitHub

理解Underscore中的flatten函数的更多相关文章

  1. 理解Underscore中的节流函数

    上一篇中讲解了Underscore中的去抖函数(_.debounced),这一篇就来介绍节流函数(_.throttled). 经过上一篇文章,我相信很多人都已经了解了去抖和节流的概念.去抖,在一段连续 ...

  2. 理解Underscore中的restArgs函数

    虽然Underscore并没有在API手册中提及到restArgs函数,我们仍然可以通过_.restArgs接口使用restArgs函数.如果不去阅读源码,我们很难发现Underscore中还有这样的 ...

  3. 理解Underscore中的uniq函数

    uniq函数,是Underscore中的一个数组去重函数,给它传递一个数组,它将会返回该数组的去重副本. 1 ES6版本去重 在ES6版本中,引入了一个新的数据结构——set,这是一种类似数组的数据结 ...

  4. 理解Underscore中的_.bind函数

    最近一直忙于实习以及毕业设计的事情,所以上周阅读源码之后本周就一直没有进展.今天在写完开题报告之后又抽空看了一眼Underscore源码,发现上次没有看明白的一个函数忽然就豁然开朗了,于是赶紧写下了这 ...

  5. 简单理解ECMAScript2015中的箭头函数新特性

    箭头函数(Arrow functions),是ECMAScript2015中新加的特性,它的产生,主要有以下两个原因:一是使得函数表达式(匿名函数)有更简洁的语法,二是它拥有词法作用域的this值,也 ...

  6. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  7. 理解Underscore中的_.template函数

    Underscore中提供了_.template函数实现模板引擎功能,它可以将JSON数据源中的数据对应的填充到提供的字符串中去,类似于服务端渲染的模板引擎.接下来看一下Underscore是如何实现 ...

  8. 理解javascript中的回调函数(callback)

    以下内容来源于:http://www.jb51.net/article/54641.htm 最近在看 express,满眼看去,到处是以函数作为参数的回调函数的使用.如果这个概念理解不了,nodejs ...

  9. 再次理解js中的call函数

    a.call(b); 网上说明的版本比较多.有的说,是指针替换.有说,将a对象的方法加在b对象执行.官方说:什么对象替换什么对象.反正看了几个版本,尽管有具体的实例,看了我三次都没看懂它的具体含义.看 ...

随机推荐

  1. input输入提示历史记录

    一般便于用户的输入习惯,我们都会提示历史消息,让用户有更好的使用体验,以前可能比较多朋友会用js来实现,现在HTML5的datalist可以轻松帮我们实现这个功能!只需以下几行代码 <!doct ...

  2. jq访问网络接口实例

    最近需要在app生活频道上,需要添加一些类目,这就需要用到一些公用的开放接口,ajax其实调用并不复杂,但是结合jquery则显得更简洁一些,下面一起来看看jquery调用后台api. 代码如下: & ...

  3. CF520B——Two Buttons——————【广搜或找规律】

    J - Two Buttons Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Su ...

  4. 项目管理系列--谷歌的code review

    Google 开源项目风格指南 (中文版) 在线文档托管在 ReadTheDocs : 在线阅读最新版本 中文风格指南 GitHub 托管地址:zh-google-styleguide 声明. 本项目 ...

  5. Android 屏蔽recent task 按钮

    Step 1 Add this permission to the manifest.xml file <uses-permission android:name="android.p ...

  6. jquery ui dialog弹出窗 清空缓存Cache或强制刷新

    我用jquery ui 弹出一个购物车的对话,通过AJAX加载的数据.发现购物车被缓存,一直看到是旧数据.为了刷新购物车更新,我必须去加一个刷新按钮,点击后更新购物车页面.有没有一种方法来自动刷新加载 ...

  7. 利用canvas制作图片(可缩放和平移)+相框+文字

    前言: 公司一个售前问我能不能用H5做一个手机拍照,给相片添加相框和添加文字上传到服务器的功能,我当时一琢磨觉得可行,就利用空余时间做了一个demo,去掉了拍照和上传,如果以后有机会,会给补上,当然对 ...

  8. 【转】Encrypt ConnectionString in Web.Config 【加密ASP.NET web.config数据库链接字串】

    原文链接:https://www.codeproject.com/Tips/795135/Encrypt-ConnectionString-in-Web-Config web.config中一般会存放 ...

  9. 一:SpringMVC架构流程

    架构流程: 1.用户发送请求至前端控制器DispatcherServlet 2.DispatcherServlet收到请求调用HandlerMapping处理器映射器. 3.处理器映射器根据请求url ...

  10. shiro权限控制入门

    一:权限控制两种主要方式 粗粒度 URL 级别权限控制和细粒度方法级别权限控制 1.粗粒度 URL 级别权限控制 可以基于 Filter 实现在数据库中存放 用户.权限.访问 URL 对应关系, 当前 ...