当前位置: 首页 > 编程日记 > 正文

打造属于自己的underscore系列 ( 一 )

underscore作为开发中比较常用的一个javascript工具库,提供了一套丰富的函数式编程功能,该库并没有拓展原有的javascript原生对象,而是在自定义的_对象上,提供了100多个方法函数。在这个系列中,将从uderscore源码角度, 打造一个自己的underscore框架

一,框架设计

1.1 自执行函数

现代js 库的框架设计,一般都是以自执行函数的形式,自执行函数一般有两种形式

(function(){// 形式一
}())
(function(){// 形式二
})()

我们知道,函数声明的形式会挂载到window对象作为方法存在,而函数表达式的形式则会挂载在window对象作为属性存在,这都会造成变量污染,而自执行函数的好处在于可以防止变量的污染,函数执行完后便立刻销毁掉。

1.2 使用风格

underscore有两种风格形式可以使用,一种是面向对象类型,另一种是函数类型。

// 例子
_.map([1, 2, 3], function(n){ return n * 2; });
_([1, 2, 3]).map(function(n){ return n * 2; });

因此,在定义underscore类的时候需要考虑对象和函数两种场景。当以函数的形式调用时需要把 _ 当作一个构造函数并返回他的实例化。代码如下

(function(root){var _ = function (obj) {if (!(this instanceof _)) {return new _(obj)}}root._ = _
}(this))

1.3 使用环境

现在前端开发重视模块化,以node服务端而论, 我们有commonjs规范,以客户端而论,我们有AMD 和 CMD规范,对应的模块加载器为 requirejs 和 seajs。目前通行的javascript模块规范主要集中在commonjs 和 AMD,因此为了让定义的underscore库能够适用于各种规范。在框架的定义时需检测使用环境并兼容各种规范。

  • 服务端:commonjs规范,检测module.exports 是否存在,满足时通过 module.exports = {} 将 underscore暴露出来,不满足则 通过window对象暴露出来。
  • 客户端: AMD 规范, 检测 define.amd 是否存在,满足时通过 define('**', [], function(){ return '***' })暴露模块
(function (root) {var _ = function (obj) {if (!(this instanceof _)) {return new _(obj)}}// commonjs 规范 检测 module.exports 是否存在if ((typeof module !== 'undefined' && module.exports)) {module.exports = {_: _}} else {root._ = _;// window 对象暴露方法}// amd 规范,检测 define.amd 是否存在if (typeof define == 'function' && define.amd) {define('underscore', [], function () {return _;});}}(this))
1.3.1 服务端使用
// commonjs
const _ = require('./underscore.js')
console.log(_)
1.3.2 客户端使用
// AMD
require(['underscore'], function (underscore) {console.log(underscore)
})

1.4 方法定义

underscore的调用,既可以通过_.unique(),也可以通过 _().unique(),两种方法效果相同却需要在框架设计时定义两套方法,一套是定义 _ 对象的静态方法,另一套是扩展 _对象原型链上的方法。

_.uniqe = function() {}_.prototype.unique = function() {}

为了避免冗余代码,可以将定义好的静态方法复制一份成为原型链上的方法

(function(root){···_.mixins = function() {// 复制静态方法到原型上}_.mixins() // 执行方法
}(this))

mixins 方法的实现,需要遍历 underscore 对象上所有的静态方法,因此需要先完成对 遍历方法 _.each 的实现

1.41 _.each

_.each(list, iteratee, [context]) Alias: forEach
遍历list中的所有元素,按顺序用每个元素当做参数调用 iteratee 函数。如果传递了context参数,则把iteratee绑定到context对象上。每次调用iteratee都会传递三个参数:(element, index, list)。如果list是个JavaScript对象,iteratee的参数是 (value, key, list))。返回list以方便链式调用。

each 的第一个参数按照文档可以支持 数组,类数组,对象三种类型,数组类数组和对象在遍历时的处理方式不同。前者回调函数处理的是 值和下标,后者处理的是 值和属性。

// 判断数组,类数组方法
(function(root) {···_.each = function (list, callback, context) {// context 存在会改变callback 中this 的指向var i = 0;var key;if (isArrayLikeLike(list)) { //  数组,类数组for (var i = 0; i < list.length; i++) {context ? callback.call(context, list[i], i, list) : callback(list[i], i, list)}} else { // 对象for (key in list) {context ? callback.call(context, list[key], key) : callback(list[key], key)}}}var isArrayLike = function (collection) {// 返回参数 collection 的 length 属性值var length = collection.length;// length是数值,非负,且小于等于MAX_ARRAY_INDEX// MAX_ARRAY_INDEX = Math.pow(2, 53) - 1return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;}
}(this))
1.4.2 _.mixin
mixin方法的设计,目的是为了在underscore原型对象上扩展更多的方法,它既可以用来扩展用户自定义的方法,比如
_.mixin({capitalize: function(string) {return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();}
});
_("fabio").capitalize();
=> "Fabio"

当然也可以用来内部拷贝静态方法到原型链的方法上。

(function(root){···var push = Array.prototype.pushvar _ = function (obj) {if (!(this instanceof _)) {return new _(obj)}this.wrap = obj // 存储实例对象传过来的参数}_.mixins = function (obj) {_.each(obj, function (value, key) {_.prototype[key] = function () {var args = [this.wrap]push.apply(args, arguments)return value.apply(this, args)}})}_.mixins(_)
}(this))

其中关注点在arguments 的处理上,静态方法需要传递目标源作为方法的参数 即_.unique(目标源, 回调函数),而实例方法的目标源存储在构造对象的属性中 ,即_(目标源).unique(回调函数),因此定义实例方法时需要合并属性和回调函数。即Array.prorotype.push.apply([this.wrap], arguments),之后将他作为参数传递给静态方法并返回处理结果。

将类数组转成数组的方法

  • Array.prototype.slice.call(类数组)
  • var a = []; Array.prototype.push.apply(a, 类数组); console.log(a);
  • var a = []; Array.prototype.concat.apply(a, 类数组); console.log(a);
  • ES6方法 Array.from(类数组)
  • ES6扩展运算符 var args = [...类数组]

1.5 链式调用

1.5.1 _.chain()

返回一个封装的对象. 在封装的对象上调用方法会返回封装的对象本身, 直道 value 方法调用为止。

underscore中方法的调用返回的是处理后的值,因此无法支持方法的链式调用。如果需要链式调用,需要使用chain()方法,chain的使用会使每次调用方法后返回underscore的实例对象,直到 调用value方法才停止返回。

(function(root){···// chain方法会返回 _ 实例,并且标注该实例是否允许链式调用的_.chain = function(obj) {var instance = _(obj);instance.chain = true; return instance}// 增加是否支持链式调用的判断,如果支持,则返回该实例,不支持则直接返回结果,var chainResult = function (instance, obj) {return instance.chain ? _(obj).chain() : obj}_.mixins = function (obj) {_.each(obj, function (value, key) {_.prototype[key] = function () {var args = [this.wrap]push.apply(args, arguments)return chainResult(this, value.apply(this, args)) // 修改实例方法的返回值,返回值通过chainResult 包装,根据chainResult的判断结果改变返回值}})}
}(this))
1.5.2 value()

因为链式调用会使underscore的方法返回他的实例对象,所以当需要结束这一调用行为时,需要使用value()。 value()方法会返回调用的结果。

(function(root){···_.value = function(instance) {return instance.wrap}
}(this))
未完待续。。。

转载于:https://www.cnblogs.com/kidflash/p/10077643.html

相关文章:

Java案例——字符串拼接

Java案例——字符串拼接案例 1.案例需求 定义一个方法&#xff0c;把int数组中的数据按照指定的格式拼接成一个字符串返回&#xff0c;调用该方法&#xff0c;并在控制台输出结果 例如&#xff0c;数字为int[] arr {1,2,3};执行方法后的输出结果为&#xff1a;[1,2,3] 2.思路…

SQL同时删除两张表中的数据

DELETE user,orders from user,orders where user.idorders.user_id AND user.id#{id}; 转载于:https://www.cnblogs.com/duneF/p/7196472.html

安全与用户输入

用户数据&#xff0c;就是任何种类的输入&#xff08;来自于 Web 请求或者 URL 中的数据&#xff0c;输入在 Microsoft Windows 窗体应用程序的控件中的数据&#xff0c;等等&#xff09;&#xff0c;它能够对代码产生影响&#xff0c;因为这些数据经常被直接当成参数来使用并且…

谁能搞定中国的文艺复兴,我就能搞定中国的政治改革

文化--------------经济------------------政治转载于:https://blog.51cto.com/73945/12249

构造函数以及this

实际上构造函数与普通的函数并没有区别&#xff0c;所以一般在开发中会使用大驼峰命名规则来区别普通的函数&#xff0c;构造函数实际上是通过返回一个this值来完成构造函数的创建的. 这个rutern this的操作由new这个操作符来完成&#xff0c;当然个人也可以手动来设置return的…

java案例——字符串反转

java案例——字符串反转 1.需求&#xff1a; 定义一个方法&#xff0c;实现字符串反转。键盘录入一个字符串&#xff0c;调用该方法后&#xff0c;在控制台输出结果 例如&#xff0c;键盘录入abc,输出结果cba2.思路&#xff1a; 1.键盘录入一个字符串&#xff0c;用Scanner实…

Jetson tk1 安装 CUDA,ROS,OpenCV和kinect2以及刷机以及ssh远程控制

我的jetson tk1的系统是&#xff1a;LTR21.3&#xff0c;ubuntu14.04。本文仅仅是个人总结&#xff0c;亲测成功。 注意&#xff1a;如果你是使用校园网进行安装的话&#xff0c;有很多源是没办法访问的&#xff0c;安装的时候就会出现很多问题&#xff0c;所以&#xff0c;尽量…

Refactor!™ for ASP.NET--ASP.NET代码重构插件

Teaching Demo: http://www.devexpress.com/Products/NET/IDETools/CodeRush/Training.xml有些功能在JBuilder2005中早就有了。大家了解一下吧&#xff0c;比较不错。Refactor! is freely available to all ASP.NET 2.0 developers and offers a comprehensive suite of tools …

面向对象的程序开发技术C++教学课件系列之四

面向对象的程序开发技术C教学课件系列之四转载于:https://blog.51cto.com/hnxdd/13205

python自动华 (十四)

Python自动化 【第十四篇】&#xff1a;HTML介绍 本节内容&#xff1a; Html概述HTML文档常用标签2. CSS 概述CSS选择器CSS常用属性1.HTML 1.1概述 HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写&#xff0c;他是一种制作万维网页面标准语言&#xff08;标记&a…

一些有趣的题目(java)持续更新

有趣的编程题1.面试题2.某公司面试题1.面试题 此处为正确的代码 package Java.king01.Test;class MicrosoftTest {public static void main(String[] args) {int[] arr new int[]{12,3,3,34,56,78,432};for(int i arr.length - 1;i > 0;i--){arr[i] arr[i]/arr[0];}for(…

lunix开放端口

以mysql服的3306端口为例。 1、直接打开端口&#xff1a;iptables -I INPUT -p tcp --dport 3306 -j ACCEPT 2、永久打开某端口首先&#xff0c;用vim打开防火墙配置文件&#xff1a;vim /etc/sysconfig/iptables然后&#xff0c;在iptables文件内容中加入如下内容:-A RH-Firew…

开机运行记事本怎么回事

1.删除文件%SystemRoot%\system32\wincfgs.exe%SystemRoot%\KB20060111.exe2.清除移动存储设备病毒连接好usb设备后&#xff0c;打开我的电脑&#xff0c;点击右键选择打开&#xff08;不要直接打开或点“open”&#xff09;&#xff0c;然后打开菜单栏的“工具--文件夹选项--查…

IDEA快捷键及基本使用方法

IDEA常用设置一级目录快捷键导包&#xff1a;自动导包设置开启自动编译按住Ctrl之后滑动鼠标滚轮可以实现代码自由缩放一级目录 快捷键 导包&#xff1a; 1.手动导包 import java.util.Scanner; 2. 快捷键导包 Alt Enter 3.自动导包快捷键功能CtrlD复制光标所在行到下一行…

php 前台生成多维数组 后台批量添加

同一个地方绊倒两次&#xff0c;记录一下哈 1&#xff09;前台表单&#xff0c;看 name 1 <div class"tab-pane row " id"tab-1" >2 <input type"hidden" name"data[1][Type]" value class"form-control" id&q…

plsql循环语句

循环结构有loop。。end&#xff0c;while和for循环 loop基本结构 LOOP 要执行的语句; EXIT WHEN <条件语句> /*条件满足&#xff0c;退出循环语句*/ END LOOP; declare int number(4) : 1;begin loop dbms_output.put_line(我是||int|||); int : int 1; exit when int …

净空法师认为忧郁症源于缺乏伦理教育和因果教育

昨天上海的几个豆瓣佛友聚会&#xff0c;大家谈到了忧郁症的问题。   净空老法师是从教育和预防的角度讲的&#xff0c;大家的看法如何。     下面是老法师开示的连接     http://www.360doc.com/showWeb/0/0/393089.aspx 转载于:https://www.cnblogs.com/chenge/arc…

C语言博客作业04--数组

1.本章学习总结 1.1 思维导图 1.2 本章学习体会及代码量学习体会 1.2.1 学习体会 关于数组&#xff0c;数组是最基本的构造类型&#xff0c;它是一组相同类型数据的有序组合。数组中的元素在内存中连续存放&#xff0c;每个元素都属于相同的数据类型&#xff0c;用数组名和下表…

AJAX ControlToolkit学习日志-ModalPopupExtender(16)

ModalPopupExtender控件用于设置网页上文本的样式。下面看一个示例&#xff1a;1)在Vs2005中新建一个ASP.NET AJAX-Enabeld Web Project项目工程&#xff0c;命名为ModalPopupExtender1。2)在Default.aspx中的<div/>标签中添加一段文字。再添加一个LinkButton控件&#x…

Ubuntu 想要更新源 报错 “E: 无法获得锁 /var/lib/dpkg/lock-frontend - open (11: 资源暂时不可用)”

不得不说&#xff0c;想要apt -get update 一下还真的是很慢啊&#xff0c;但是刚刚装的Ubuntu有没有vim,我没法修改更新源。悲催呐~ 能够得到这个也是查了好多资料 出现这个问题的原因可能是有另外一个程序正在运行&#xff0c;由于它在运行时&#xff0c;会占用软件源更新时…

1.lamp网站构建

bs、cs结构 及优缺点 s-server , c-client , b-broswer cs结构&#xff1a;客户端--服务器 &#xff0c; 比如QQ&#xff0c;首先要下载QQ客户端&#xff0c;之后是客户端与服务器连接 &#xff0c; bs结构&#xff1a;浏览器--服务器 &#xff0c; 浏览器直接登录的&#xff…

Database design best practice(1):关于primary key及其它

1. The job of the primary key is to uniquely identify records, not to store business data ; any use of business data in a primary key is a dangerous practice, since any changes to such data will have large ripple effects (from javapractices) 这是不是意味着…

ReentrantLock+线程池+同步+线程锁

1、并发编程三要素&#xff1f;1&#xff09;原子性原子性指的是一个或者多个操作&#xff0c;要么全部执行并且在执行的过程中不被其他操作打断&#xff0c;要么就全部都不执行。2&#xff09;可见性可见性指多个线程操作一个共享变量时&#xff0c;其中一个线程对变量进行修改…

String.hashCode 哈希值出现重复?

String.hashCode重复 在我学习hash的时候&#xff0c;没有按照教程里面的字符串的值去获取了一个hashcode,然后让这个小白的我惊讶了&#xff0c;这个竟然和教程里面的那个不一样&#xff0c; 对此&#xff0c;我对于这个问题进行了“研究” &#xff0c;在这里先写点我已经知道…

Selenium2(WebDriver)总结(二)---Firefox的firebug插件参数设置(补充)

Selenium2(WebDriver)总结(二)---Firefox的firebug插件参数设置(补充) 本文是对上一节的补充&#xff1a;http://www.cnblogs.com/puresoul/p/4251536.html 使用Selenium2(webdriver)启动firefox且自动加载firebug插件时&#xff0c;切换到firebug插件的网络和cookies部分时&am…

个人信息管理器

转&#xff1a;http://www.cnblogs.com/maxianghui/archive/2006/10/10/524873.html 经过一个多月的努力&#xff0c;终于搞定了这个小软件&#xff0c;请大家给点意见我。采用VC# Access2003 XML开发&#xff0c;扩展了TreeView控件&#xff0c;扩展了RichTextBox控件&#…

分享一些我在开发过程中用过的资源

以下所提到的控件/组件均为开源或免费的。1,ComboBox控件: Upgrade Your Select Element to a Combo Box2,DateTimePicker控件: GrayMetterSoft3,TabStrip控件: A simple ASP.NET Web TabStrip User Control4,Grid控件: XGrid5,数据结构Tree: A Generic Tree Collection6,csv文…

Ubuntu16.04如何彻底删除Apache2

虽然作为运维人员通常情况不建议随意删除Linux系统上面的任何软件&#xff0c;主要指生产环境下&#xff0c;测试环境也不能太随意。 但是有的时候&#xff0c;比如系统环境要变一变&#xff0c;我们就需要替换一些淘汰的软件&#xff0c;对此我们一般都会删除。 按照下面的步骤…

Java案例——统计字符串中每个字符串出现的次数

统计字符串中每个字符串出现的次数 需求&#xff1a; 1.键盘录入一个字符串&#xff0c;要求统计字符串中每个字符串出现的次数 举例&#xff1a;键盘录入“aababcabcdabcde” 在控制台输出&#xff1a;“a(5)f(4&#xff09;c(3)g(2)e(1)” 思路&#xff1a; 1.键盘录入一个字…

在C#中怎样推断线程当前所处的状态

在C#中怎样推断线程当前所处的状态老帅 在C#中。线程对象Thread使用ThreadState属性指示线程状态。它是带Flags特性的枚举类型对象。ThreadState 为线程定义了一组全部可能的执行状态。一旦线程被创建。它就至少处于当中一个状态中。直到终止。在公共语言执行时中创建的线程最…