underscore.js源码研究(5)

概述

很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以就了结研究underscore源码这一心愿吧。

underscore.js源码研究(1)
underscore.js源码研究(2)
underscore.js源码研究(3)
underscore.js源码研究(4)
underscore.js源码研究(5)
underscore.js源码研究(6)
underscore.js源码研究(7)
underscore.js源码研究(8)

参考资料:underscore.js官方注释,undersercore 源码分析,undersercore 源码分析 segmentfault

模板引擎

之前就接触过模板引擎,比如说template.js、handlebars.js、jade.js、nunjucks.js等等。后来学react的时候也接触过jsx,当时我就感到很不可思议,竟然能够把js中的变量甚至语句插入到html里面去,真的十分神奇。今天看underscore.js的源码的时候也发现里面竟然有模板引擎,于是我就来研究研究模板引擎。

实现变量替换

说到模板引擎,一个最基本的特性就是能在html代码中插入js的变量,下面我们来实现这种效果。

我们需要实现这种效果:

//定义一个模板
const tpl = 'hello {{name}}';//定义值
const data = {name: 'haha'};//渲染,最后content是name被替换过的html代码
const output = render(tpl, data);

其实仔细理了一下效果的流程之后,感觉实现这个效果挺简单的,就是用一个正则替换,把{{ name }}里面的值替换为data里面的数据就行了。

实现代码如下:

//定义替换的正则表达式
const rule = /{{([\s\S]+?)}}/g;//render函数
function render(tpl, data) {return tpl.replace(rule, (matcher, p1) => {return data[p1];})
}

注意,由于rule里面只有一个括号,所以replace第二个参数的函数里面只有p1没有p2。

变量替换改进

为了便于阅读,我们需要模板可以写成下面的形式。(name两边有空格)

//定义一个模板
const tpl = 'hello {{ name }}';

所以我们加一个去空格的函数,整个代码如下:

//定义替换的正则表达式
const rule = /{{([\s\S]+?)}}/g;//render函数
function render(tpl, data) {return tpl.replace(rule, (matcher, p1) => {return data[p1.trim()];})
}

注意:trim函数只兼容IE9,如果要兼容IE9以下的话就需要pollyfill了。

支持语句

几乎所有的模板引擎都支持写入语句,比如像下面的写法:

const tpl = 'Students:' +//注意这里只有一个大括号!!!'{ for(i = 0; i < data.students.length; i++) }' +'{{ data.students[i].name }}';
const data = {students: [{id: 1,name: ' haha '},{id: 2,name: ' yaya '}]
};
const content = render(tpl, data);

我们希望上述代码输出:

Students: haha  yaya

看起来非常复杂,可是我们用伪代码分解一下执行过程就感觉有点简单了:

//首先输出Students:
//然后执行下面的代码
for(i = 0; i < data.students.length; i++) {
//这里输出students[i].name
}
//完毕

实际写起来是这样的:

//定义要输出的内容
content = '';
content += 'Students:';
for(i = 0; i < data.students.length; i++) {content += 'data.students[i].name';
}
//输出整个content
return content

可以看到,上面有这2个要点:

  1. 变量的内容需要添加到content里面,但是语句的内容不需要添加到content里面。
  2. 不是模板的内容要记录位置,然后再通过这个位置添加到content里面。

所以好好整理一下,我们首先需要语句的正则表达式,然后通过这个正则表达式按照上述规则进行替换,代码如下:

//为了方便,我们把规则封装在一个对象里面
const rules = {//插值,对应变量interpolate: /{{([\s\S]+?)}}/,//逻辑,对应语句evaluate: /{([\s\S]+?)}/
};
//2个正则合在一起,先替换变量,再替换语句
const matcher = new RegExp([rules.interpolate.source,rules.evaluate.source
].join('|'), 'g');//render函数
function render(tpl, data) {let concating = 'let content = "";\n';let index = 0;//仍然是replace里面的第二个参数是函数的形式tpl.replace(matcher, (match, interpolate, evaluate, offset) => {//添加非模板的内容if (tpl.slice(index, offset)) {concating += 'content += "' + tpl.slice(index, offset) + '";\n';}//记录偏移量index = offset + match.length;//变量需要添加到content里面if (interpolate) {concating += 'content +=' + interpolate + ';\n';//语句不需要添加到content里面,而且不要分号} else if (evaluate) {concating += evaluate + '\n';}})concating += 'return content;';//以concating为内容,定义一个函数,参数是objconst renderFunc = new Function('obj', concating);return renderFunc(data);
}

它生成的renderFunc函数的代码如下图所示:

(function(obj
/*``*/) {
let content = "";
content += "Students:";for(i = 0; i < data.students.length; i++) 
content += data.students[i].name ;
return content;
})

可以看到有一个缺点,就是for循环没有大括号,这就导致它只执行下面的那条语句。如果要加大括号的话,就需要额外的规则,我们这里不讨论。

所以把上面所有的代码加起来就是这样的:

//为了方便,我们把规则封装在一个对象里面
const rules = {//插值,对应变量interpolate: /{{([\s\S]+?)}}/,//逻辑,对应语句evaluate: /{([\s\S]+?)}/
};//2个正则合在一起,先替换变量,再替换语句
const matcher = new RegExp([rules.interpolate.source,rules.evaluate.source
].join('|'), 'g');//定义模板和数据
const tpl = 'Students:' +//注意这里只有一个大括号!!!'{ for(i = 0; i < data.students.length; i++) }' +'{{ data.students[i].name }}';
const data = {students: [{id: 1,name: ' haha '},{id: 2,name: ' yaya '}]
};//render函数
function render(tpl, data) {let concating = 'let content = "";\n';let index = 0;//仍然是replace里面的第二个参数是函数的形式tpl.replace(matcher, (match, interpolate, evaluate, offset) => {//添加非模板的内容if (tpl.slice(index, offset)) {concating += 'content += "' + tpl.slice(index, offset) + '";\n';}//记录偏移量index = offset + match.length;//变量需要添加到content里面if (interpolate) {concating += 'content +=' + interpolate + ';\n';//语句不需要添加到content里面,而且不要分号} else if (evaluate) {concating += evaluate + '\n';}})concating += 'return content;';//以concating为内容,定义一个函数,参数是objconst renderFunc = new Function('obj', concating);return renderFunc(data);
}//输出,结果为Students: haha  yaya 
console.log(render(tpl, data));

可以看到,整个过程实际上是在拼接和替换字符串,然后利用Function接受字符串的情形生成函数,没有其他的任何内容。

在下一篇博文中我们会对这个小的模板引擎进行优化。

转载于:https://www.cnblogs.com/yangzhou33/p/8972394.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/452817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人心散了、项目必然要败

最近接手一个项目&#xff0c;是从半路上接过来的。按照常理&#xff0c;只要脑子没被驴踢&#xff0c;是不会接人家的烂摊子的。我之所以接这个项目&#xff0c;一方面是因为这个项目中的开发人员是我部门的人&#xff08;本人是部门主管&#xff09;&#xff0c;另一方面是因…

国家自科委管文科学部认定的国内30种重要期刊

国家自科委管文科学部认定的国内30种重要期刊 A类刊物&#xff08;22种&#xff09; 1、管理科学学报&#xff08;双月刊&#xff09; 2、系统工程理论与实践&#xff08;月刊&#xff09; 3、管理世界&#xff08;月刊&#xff09; 4、数量经济技术经济研究&#xff08;月刊&a…

面向对象的四个要点

•把对象(object)作为融合了数据及在数据上的操作行为的统一的软件构件•把所有对象都划分成类(class)。•按照父类与子类的关系&#xff0c;把若干个相关类组成一个层次结构的系统。•对象彼此间仅能通过发送消息互相联系。

Django二次开发对接FastDFS

1.自定义文件存储器类 配置文件settings中加入如下配置 # 设置Django的文件存储类、&#xff08;名字固定&#xff09; DEFAULT_FILE_STORAGEutils.fdfs.storage.FDFSStorage# 设置fdfs使用的client.conf文件路径&#xff08;名字自己定义&#xff09; FDFS_CLIENT_CONF./util…

微信支付 java 集成案例_Spring Boot项目中集成微信支付v3

1. 前言最近忙的一批&#xff0c;难得今天有喘气的机会就赶紧把最近在开发中的一些成果分享出来。前几日分享了自己写的一个微信支付V3的开发包payment-spring-boot-starter&#xff0c;就忙里偷闲完善了一波。期间给微信支付提交了6个BUG&#xff0c;跟微信支付的产品沟通了好…

单挑力扣(LeetCode)SQL题:1308. 不同性别每日分数总计

相信很多学习SQL的小伙伴都面临这样的困境&#xff0c;学习完书本上的SQL基础知识后&#xff0c;一方面想测试下自己的水平&#xff1b;另一方面想进一步提升&#xff0c;却不知道方法。 其实&#xff0c;对于技能型知识&#xff0c;我的观点一贯都是&#xff1a;多练习、多实…

需求与范围驾驭深刻反省总结

每天都在讲范围、说需求&#xff0c;真的到了想整理出点什么的时候&#xff0c;却一下子不知从何说起。也许是熟悉麻痹症吧。根据我的破经历&#xff0c;在需求方面有几个是最搞人的&#xff0c;只要我们方法得当&#xff0c;虽然不一定能够完全驾驭&#xff0c;但起码可以改善…

16. vim

vim编辑器是vi的升级版本&#xff0c;带颜色显示安装yum install -y vim-enhanced将passwd文件复制到其他目录下&#xff0c;vim后没有颜色 一般模式上下左右方向键或kjhl四个键移动光标n方向键 向特定方向移动n位ctrl b 或 pageup 向上翻页ctrl f 或 pagedown 向下翻页0或sh…

软件生命周期

软件生命周期由软件定义、软件开发和运行维护 ( 也称为软件维护 )3 个时期组成&#xff0c;每个时期又进一步划分成若干个 阶段 。

Jmeter(三)_配置元件

HTTP Cookie Manager 用来存储浏览器产生的用户信息 Clear Cookies each Iteration&#xff1a;每次迭代请求&#xff0c;清空cookies&#xff0c;GUI中定义的任何cookie都不会被清除。Implementation&#xff1a;默认HC4CookieHandlerCookie Policy&#xff1a;将用于管理Cook…

山寨版项目管理经验小结

不知道这个标题是否合适。 忙了互联网&#xff0c;再忙作软件&#xff0c;今天好不容易闲下来&#xff0c;写点最近总结的一些经验。最近在和北京几家大的软件公司的合作过程中&#xff0c;也许我所看到的&#xff0c;可能不代表全部&#xff0c;但是值得从事这个行业的人重视。…

Django项目--首页静态化

0前言 1.使用Celery生成静态页面 task.py中新增任务函数generate_static_index_html()&#xff0c;任务函数生成静态页面。 app.task def generate_static_index_html():产生首页静态页面# 获取商品的种类信息types GoodsType.objects.all()# 获取首页轮播商品信息goods_bann…

C语言指针,申请、释放内存,线程

2019独角兽企业重金招聘Python工程师标准>>> 1&#xff1a;普通情况下&#xff0c;C语言的指针是使用虚拟地址&#xff0c;并非物理地址&#xff1b; 2&#xff1a;C语言mallco函数可以根据输入的值&#xff0c;申请一块连续的内存&#xff1b;free&#xff08;*p&a…

Docker在Ubuntu16.04上安装

转自&#xff1a;http://blog.51cto.com/collen7788/2047800 1、添加Docker源 sudo apt-get update 2、增加CA证书 sudo apt-get install apt-transport-https ca-certificates 3、添加GPG Key(一种加密手段) sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:…

java伪协议_通过伪协议解决父页面与iframe页面通信的问题

我们经常会有父页面与iframe页面的操作&#xff0c;比如这个iframe里面的内容是js写的。如以下代码var iframe document.getElementById("iframe"),doc iframe.contentWindow.document;doc.open();doc.write("---------something------");doc.close();以…

Django项目--静态首页的数据缓存(设置、获取、更新)

0 前言 将处理计算的结果先临时保存起来&#xff0c;下次使用的时候可以先直接使用&#xff0c;如果没有这个备份的数据&#xff0c;重新进行计算处理。 将缓存数据保存在内存中 &#xff08;本项目中保存在redis中&#xff09; cache注意事项&#xff1a; 1&#xff09;如果…

关于腾讯算法大赛

腾讯算法大赛 本文参考于我协会前会长吴师兄的文档 腾讯社交广告高校算法大赛是面向高校大学生的算法大赛&#xff0c;作为腾讯核心的广告业务单元&#xff0c;腾讯社交广告通过对海量社交数据进行深入分析&#xff0c;构建多样广告场景&#xff0c;与8亿用户连接对话。在大数据…

列表推导式

#麻烦办法new_lst []for i in range(10): new_lst.append(i**2)print(new_lst)#简单办法print([i**2 for i in range(10)])# 小题下面列表中取余list_a [1,2,3,-5,20,-7]print([i%2 for i in list_a])# 30以内所有能被3整除的数print([won for won in range(30) if won%3 …

软件过程

软件过程是为了获得高质量软件所需要完成的一系列任务的框架&#xff0c;它规定了完成各项任务的工作步骤。 软件过程描述为了开发出客户需要的软件&#xff0c;什么人&#xff08;who&#xff09;、在什么时候&#xff08;when&#xff09;、做什么事&#xff08;what&#x…