概述

很早就想研究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

underscore.js源码研究(5)相关推荐

  1. 常用JS库源码 - store.js源码/underscore.js源码

    常用JS库源码 Store.js源码 "use strict" // Module export pattern from // https://github.com/umdjs/ ...

  2. underscore.js源码解析2

    _.isNull(object) 如果object的值是null,返回true. _.isNull(null); => true; _.isNull(undefined); => fals ...

  3. underscore.js源码整体框架解析

    源码框架 读一些库的源码时最头疼的其实不是里面各个函数的功能,而是整体结构框架,通常库的源码都很长,跟框架相关的代码并不是在一起放着的,导致你想明白起来就很困难. 我看过通过画图的方式去讲解框架的,但 ...

  4. underscore.js 源码分析5 基础函数和each函数的使用

    isArrayLike 检测是数组对象还是纯数组 var property = function(key) {return function(obj) {return obj == null ? vo ...

  5. js define函数_不夸张,这真的是前端圈宝藏书!360前端工程师Vue.js源码解析

    优秀源代码背后的思想是永恒的.普适的. 这些年来,前端行业一直在飞速发展.行业的进步,导致对从业人员的要求不断攀升.放眼未来,虽然仅仅会用某些框架还可以找到工作,但仅仅满足于会用,一定无法走得更远.随 ...

  6. 从template到DOM(Vue.js源码角度看内部运行机制)

    写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(github.com/answershuto-)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些产出也会对同样想要学习Vue.j ...

  7. three.js 源码注释(六十一)objects/LOD.js

    商域无疆 (http://blog.csdn.net/omni360/) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:商域无疆 -  本博客专注于 敏捷开发 ...

  8. WebRTC源码研究(4)web服务器工作原理和常用协议基础

    文章目录 WebRTC源码研究(4)web服务器工作原理和常用协议基础 前言 做WebRTC 开发为啥要懂服务器开发知识 1. Web 服务器简介 2. Web 服务器的类型 3. Web 服务器的工 ...

  9. three.js 源码注释(一)./Three.js

    商域无疆 (http://blog.csdn.net/omni360/) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:商域无疆 -  本博客专注于 敏捷开发 ...

最新文章

  1. 10个经典又容易被人疏忽的JVM面试题
  2. java好过去前一天日期_Java-日期保存为前一天
  3. LeNet训练MNIST
  4. 【PAT乙级】1065 单身狗 (25 分)
  5. OpenFOAM程序开发的基本知识(基本术语)
  6. oracle删除orcl库_oracle删除数据文件
  7. linux给ftp账号分配sftp权限,Linux设置用户通过SFTP访问目录的权限教程
  8. 【MyBatis】Mybatis使用SqlSessionFactory加载xml文件
  9. 解决MVC运行controller的时候只有有参构造函数但是程序一定要走无参构造函数的方法
  10. ABP中module-zero快速集成微信用户认证
  11. python继电器控制
  12. C++ | 虚函数表内存布局
  13. c java long_C语言中输出long long型数据怎么输出
  14. xslx-style导出,表头样式表格样式,指定条件
  15. Excel 各版本每个sheet 最大行数限制
  16. 库文件 c语言 编译器,抽取VS文件组成类GCC的编译器,并编译C程序为dll动态链接库...
  17. python3 cookie_Python3标准库:http.cookies HTTP cookie
  18. matlab中随机森林实现,随机森林实现 MATLAB
  19. matplotlib.pyplot.cm结构及用法||参数详解
  20. 谷歌浏览器去广告扩展程序(网页插件)

热门文章

  1. c++ vs release没有exe_未来安全 | 第一次Geant4培训总结 | 有没有你关注的问题呢?...
  2. 【ZOJ - 2968 】Difference Game (贪心,思维模拟)
  3. 机器学习入门必备的13张“小抄”(附下载)
  4. 词法分析器c语言带注释,C语言词法分析器内容说明注释完整可运行代码.doc-资源下载在线文库www.lddoc.cn...
  5. 语言nomogram校准曲线图_R语言实现Cox模型校准度曲线绘制
  6. 计算机网络技术基础教学内容,计算机网络技术基础
  7. java工厂模式 uml_深入浅出设计模式-简单工厂模式
  8. 爬虫软件python功能_Python 网络爬虫程序详解
  9. Java实现身份证号码的验证,JAVA后台验证身份证号码
  10. Linux实验四:编译和调试工具的使用