JavaScript获取昨天、今天、明天的时间

本文的标题是比较实例化的,我们抽象点说就是如何获取基于今天的一个日期,像PHP里面有如下的方式:

echo "明天:",date("Y-m-d",strtotime("+1 day")), "<br>";

JavaScript里面该怎么做一个类似的实现呢?下面是简单的实现:

function getDateStr(count)
{
    var date = new Date();
    date.setDate(dd.getDate() + count);
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    return year + "-" + month + "-" + day;
}

昨天就是getDateStr(-1)

今天就是getDateStr(0)

明天就是getDateStr(1)

那么前天、后天都可以以此类推。

JavaScript中substr()和substring()的区别

名为substr的函数在很多语言中都有,我们在使用JavaScript做截取字符串的时候一定会使用,但是你会发现有两个函数:substr()和substring(),从名字上看几乎是一样的,但是他们的用法还是有区别的:
var foo = "hello world!";
foo.substr(0, 10); //返回 "hello worl"
foo.substring(0, 10); //返回 "hello worl"
上面的代码看起来substr()和substring()的结果相同,但是原理是不同的,substr()是从第0个位置开始向后取10个字符,而substring()是取从第0个位置到第10个位置的字符。下面这段代码就可以看出这个区别:
foo.substr(3, 10); //返回 "lo world!"
foo.substring(3, 10); //返回 "lo worl"
除了原理上的区别还有就是substring()支持第二个参数为负数-n表示向前取n个字符,而substr没有这个功能:
foo.substr(13, -13); //返回 ""
foo.substring(13, -13); //返回 "hello world!"
综上所述,substr()方法用于截取固定长度的字符串的场景上,而substring()方法用于取字符串指定的位置,我现在区分它们俩的偏方是substring比substr名字上多了ing,ing中包含了i,i表示index,就是字符串指定位置的意思。

JavaScript根据函数名字符串调用函数的方法

最近遇到一个问题,JavaScript如何通过函数名的字符串来调用这个函数,到stackoverflow上逛了圈,收获良多,这里总结下:

如果你是要直接调用全局的函数,可以用如下方式:

//定义一个名为foo的函数
function foo() 
{
    alert(1);
}
//第一种调用方式
window['foo']();
//第二种调用方式
eval('foo()');

如果你想要在一个对象里面调用对象自己的属性函数,可以用如下方式:

var test = {
	foo : function(a) {
		alert(a);
	},
	bar : function() {
		var property = 'foo';
		var parameter = ['this is a test!'];
		var fn = this[property];
		if(typeof fn === 'function') {
			fn.apply(this, parameter);
		}
	}
};
test.bar();

上面这段代码是我写的一个小例子,其中的

fn.apply(this, parameter);

有几个好处,第一点就是可以让你调用的foo这个属性函数能够使用到当前的this,如果不用apply是无法调用到当前的this的。第二点就是传递参数变得很自由,如果属性函数需要传入多个参数可以用数组传入。

上面这段小代码就是我在“基于Requirejs和Backbonejs,将Zend Framework 2官方Demo改为RESTful架构的应用”里面做到自动路由分发的核心代码。

使用Underscore.js的template将Backbone.js的js代码和html代码分离

这段时间在学习Require.js和Backbone.js的过程中,发现有些项目里的HTML代码都是写在View的js代码里面的,渲染的时候需要对Collection进行循环,再将HTML代码拼接上去,这似乎不是一件非常好的事情,因为将js代码和html代码融合到一起会增加代码的维护难度,而且这个过程中考虑到性能的因素,需要将HTML代码放到一个数组中,最后进行拼接,代码写起来比较麻烦。我看到他们的代码之后就在考虑是否有一种类似php模板引擎的东西可以将Collection传递进去然后渲染。

我查阅了Backbone.js的手册http://backbonejs.org/#View-template ,里面有一段文字:

However, we suggest choosing a nice JavaScript templating library. Mustache.js, Haml-js, and Eco are all fine alternatives. Because Underscore.js is already on the page, _.template is available, and is an excellent choice if you prefer simple interpolated-JavaScript style templates.

Whatever templating strategy you end up with, it’s nice if you never have to put strings of HTML in your JavaScript.

它建议我们使用js的模板库,而刚好Backbone.js强依赖于Underscore.js所以Underscore.js已经被引入了,它提供了一个_.template方法,这个方法支持使用内嵌js代码的html模板代码,在js代码里没有出现HTML代码是一件非常nice的事情!这正符合了我们MVC的思想,前端的HTML代码也便于维护,要不然就真的成为意大利面条式代码了!

关于Underscore.js的template的说明在http://underscorejs.org/#template ,这里有教你怎么使用。

Template functions can both interpolate variables, using <%= … %>, as well as execute arbitrary JavaScript code, with <% … %>. If you wish to interpolate a value, and have it be HTML-escaped, use <%- … %>

上面这段文字告诉我们在这个模板的代码里面js内嵌代码的标签如何使用,接下来我举一个例子:

我们先建一个template,位于:template/album/index.html

<%
var title = 'My albums';
document.title = title;
%>
<h1><%= title %></h1>
<p>
    <a href="album-rest/add">Add new album</a>
</p>
<table class="table">
<thead>
    <tr>
        <th>Title</th>
        <th>Artist</th>
        <th> </th>
    </tr>
</thead>
<tbody id="album-list">
<% _.each(albums, function(album) { %>
<tr class="album-row">
    <td><%= album.get('title') %></td>
    <td><%= album.get('artist') %></td>
    <td>
        <a href="album-rest/edit/<%= album.get('id') %>">Edit</a>
        <a href="album-rest/delete/<%= album.get('id') %>">Delete</a>
    </td>
</tr>
<% }); %>
</tbody>
</table>

下面的这个代码片段是定义了一个Backbone的View,sync属性会去请求服务端获取获取所有album的数据,最后将数据存放到albumList这个Collection里面。随后执行render方法,在render里面this.template = _.template(AlbumTpl, albums);这句代码就是用来完成数据和模板混合的工作的,AlbumTpl来自template/album/index.html,另外必须要将Collection中的所有的model以数组的形式获取到赋给albums,除非你在模板里面又进行了对Collection的解析操作,否则不能只传入一个Collection,因为Underscore.js的template是无法识别Backbone.js的Collection的对象结构的。

define(["model/album", "collection/album-list", "text", 'text!template/album/index.html'], function(Album, AlbumList, text, AlbumTpl) {
	var IndexView = Backbone.View.extend({
		model : Album,
		initialize: function() {
		},
		sync : function (render) {
			var albumList = new AlbumList;
			var view = this;
			Backbone.sync('read', albumList, {
				success : function (result) {
					albumList.add(result.ret);
					view.collection = albumList;
					view.render();
				}
			});
		},
		render: function() {
			albumList = this.collection;
			albums = albumList.models;
			console.log(_.template(AlbumTpl, albums));
			this.template = _.template(AlbumTpl, albums);
			$("#page-wrapper").html(this.template);
		}
	});
	return IndexView;
});

通过上面的操作,就可以实现js代码和html代码分离了。

A Rule of Thumb When DOM Scripting

原则是:尽可能的不要接触DOM(touch the DOM lightly, and stay within ECMAScript as much as possible).

Here is an example

function aBadInnerHTMLLoop() {
    for (var i = 0; i < 15000; i++) {
        document.getElementById('here').innerHTML += 'a';
    }
}

上面这段代码的效率之低下,令人叹为观止,在我的Chrome下面,执行了4900ms!

function aBetterInnerHTMLLoop() {
    var str = '';
    for (var i = 0; i < 15000; i++) {
        str += 'a';
    }
    document.getElementById('here').innerHTML = str;
}

上面这段代码只执行了4ms。。。

下面我引用一段文献[High Performance JavaScript]来说明为什么DOM的执行如此低下。

DOM in the Browser World
The Document Object Model(DOM) is a language-independent application interface(API) for working with XML and HTML documents. … so it’s common across browsers to keep DOM and JavaScript implementions indepent of each other, this makes DOM scripting inherently slow. Because simply having two separate pieces of functionality interfaceing with eath other will always come at a cost. An excellent analogy is to think of DOM as piece of land and JavaScript as another piece of land, both connected with a toll bridge. Every time your EMACScript needs access to the DOM, you have to cross this bridge and pay the performance toll fee. The more you work with the DOM, the more you pay So the general recommendation is to cross that bridge as few times as possible and strive to stay in ECMAScript land.