20行实现javascript模板引擎

20行实现javascript模板引擎

我仍然在用AbsurdJS预处理器写javascript。起初,这只是一个CSS预处理器,后来我把它扩展为CSS/HTML预处理器。最近,它可以实现javascript到CSS/HTML的转换,因为它可以作为模板引擎来生成HTML。比如,可以用数据填充HTML模板。

然后,我就想写一个简单的模板引擎可以完美地与我当前的开发工作相配合。AbsurdJS主要是作为nodejs模块来发布的,但是它也可以作为客户端使用。有了这种想法,我意识到我不能利用现有的模板引擎。因为现在的大多数模板引擎只是基于nodejs,很难把他们复制到浏览器来使用。我需要一个体积小的用原生JS写的模板引擎。我曾经拜读过John Resig的一篇文章JavaScript Micro-Templating。这好像就是我所需要的。我把里面的代码做了些许变动,使之缩减为20行。我想这脚本的运行机制是非常有趣的。本文中,我一步步地重新创建一个模板引擎,然后你就会体会到来自John的伟大创意。

我们以下面的代码作为开始吧:

 

  1. var TemplateEngine = function(tpl, data) {
  2. // magic here ...
  3. }
  4. var template = '<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>';
  5. console.log(TemplateEngine(template, {
  6. name: "Krasimir",
  7. age: 29
  8. }));

一个简单的函数,来处理我们的模板与数据对象。

你可能会猜想到,我们最终想要达到的结果就是下面的样子: 

 

  1. <p>Hello, my name is Krasimir. I'm 29 years old.</p>

首先我们必须处理模板内部的动态语法,然后我们用传递给模板引擎的真实数据来替换这些动态语法。我决定利用正则表达式来实现。正则表达式不是我的强项,所以你可以留言建议给我一个更好的正则表达式。 

 

  1. var re = /<%([^%>]+)?%>/g;

这样,我们会捕获到分别以“%”开始与结束的分组。“g”(global全局)表示我们得到的不是一个,而是全部匹配到的。采用正则表达式的exec方法可以把匹配到分组的以数组的形式表现: 

 

  1. var re = /<%([^%>]+)?%>/g; var match = re.exec(tpl); 
  1. console.log(match);

结果: 

 

  1. [
  2. "<%name%>",
  3. " name ",
  4. index: 21,
  5. input:
  6. "<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>"
  7. ]

但是我们只得到了一个,再来改善一下: 

 

  1. var TemplateEngine = function(tpl, data) {
  2. var re = /<%([^%>]+)?%>/g;
  3. while(match = re.exec(tpl)) {
  4. tpl = tpl.replace(match[0], data[match[1]])
  5. }
  6. return tpl;
  7. }

OK,我们的最初目标达到了,但这远远不够。这只能容易获取到data['property']。但在实践中,我们可能遇到复杂的嵌套对象。比如: 

 

  1. {
  2. name: "Krasimir Tsonev",
  3. profile: { age: 29 }
  4. }

我们先前所做的工作就这样失效了!

那我们就分析一下其他的情况。比如,我们有个这样的模板: 

 

  1. var template = '<p>Hello, my name is <%this.name%>. I\'m <%this.profile.age%> years old.</p>';

再或者我们可能还见过这样的模板: 

 

  1. var template =
  2. 'My skills:' +
  3. '<%for(var index in this.skills) {%>' +
  4. '<a href=""><%this.skills[index]%></a>' +
  5. '<%}%>';

这该怎么办呢? John采用了 new Function 语法实现——可以从字符串来创建函数。

那我们先来熟悉一下这种语法。看一个简单的例子: 

 

  1. var fn = new Function("arg", "console.log(arg + 1);");
  2. fn(2); // outputs 3

上述代码创建的函数fn等价于: 

 

  1. function fn(arg){
  2. console.log(arg+1);
  3. }
  4. fn(2) // outputs 3

这样我们可以自定义函数,其参数与函数体可以来自简单的字符串。而我们所需要的方法应该能够返回为最终的编译模板。就像这样: 

 

  1. return
  2. "<p>Hello, my name is " +
  3. this.name +
  4. ". I\'m " +
  5. this.profile.age +
  6. " years old.</p>";

而对于 

 

  1. var template =
  2. 'My skills:' +
  3. '<%for(var index in this.skills) {%>' +
  4. '<a href=""><%this.skills[index]%></a>' +
  5. '<%}%>';

我们所需要的应该是这样: 

 

  1. return
  2. 'My skills:' +
  3. for(var index in this.skills) { +
  4. '<a href="">' +
  5. this.skills[index] +
  6. '</a>' +
  7. }

当然我们所设想的会产生语法错误,那我们可以改变一下: 

 

  1. var r = [];
  2. r.push('My skills:');
  3. for(var index in this.skills) {
  4. r.push('<a href="">');
  5. r.push(this.skills[index]);
  6. r.push('</a>');
  7. }
  8. return r.join('');

有了预想结果,下一步就是分别处理每一行来产生自定义函数。

在进行处理每一行的时候,我们应该考虑到以下问题:

  • 引号的转义,否则产生的脚本不可用
  • <% %>里面的字符不应该被当做字符串处理 
  1. var TemplateEngine = function(tpl, data) {
  2. var re = /<%([^%>]+)?%>/g,
  3. code = 'var r=[];\n',
  4. cursor = 0;
  5. var add = function(line) {
  6. // 引号转义,将"替换为\"放入定义的函数体
  7. code += 'r.push("' + line.replace(/"/g, '\\"') + '");\n';
  8. }
  9. //匹配到<% %>
  10. while(match = re.exec(tpl)) {
  11. // <% %>之前当做字符串放入函数体
  12. add(tpl.slice(cursor, match.index));
  13. // <% %>中间部分
  14. add(match[1]);
  15. //迭代处理<% %>后面部分
  16. cursor = match.index + match[0].length;
  17. }
  18. add(tpl.substr(cursor, tpl.length - cursor));
  19. code += 'return r.join("");'; // <-- return the result
  20. console.log(code);
  21. return tpl;
  22. }
  23. var template = '<p>Hello, my name is <%this.name%>. I\'m <%this.profile.age%> years old.</p>';
  24. console.log(TemplateEngine(template, {
  25. name: "Krasimir Tsonev",
  26. profile: { age: 29 }
  27. }));

考虑到if/else等JS语句,我们再做优化: 

 

  1. var TemplateEngine = function(html, options) {
  2. var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[];\n', cursor = 0;
  3. var add = function(line, js) {
  4. js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
  5. (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
  6. return add;
  7. }
  8. while(match = re.exec(html)) {
  9. add(html.slice(cursor, match.index))(match[1], true);
  10. cursor = match.index + match[0].length;
  11. }
  12. add(html.substr(cursor, html.length - cursor));
  13. code += 'return r.join("");';
  14. return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);
  15. }

终极目标实现!!

详见最终版本

原文作者:http://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值