乐者为王

Do one thing, and do it well.

Ruby DBI的安装和使用

Ruby DBI是一个为Ruby程序访问数据库提供的与数据库无关的统一数据库编程接口。结构层次上可以分为两层:

  1. Database Interface 数据库接口层,与数据库无关,提供与数据库无关的标准接口
  2. Database Driver 数据库驱动,与数据库相关

Ruby DBI模块可以从 http://ruby-dbi.sourceforge.net/ 取得,下载后解压缩,配置,然后安装:

1
2
3
ruby setup.rb config --with=dbi,dbd_mysql
ruby setup.rb setup
ruby setup.rb install

注意:--with参数必须根据你所安装的数据库类型来选择。可以配置多个,但不能选择没有安装在你机器上的数据库类型。记得刚开始的时候我使用ruby setup.rb config来配置(我机器上只安装了MySQL和SQL Server),结果在ruby setup.rb setup时一直出现像下面这样的错误信息:

ruby-dbi

下面是一个完整的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# dbitest.rb
require 'dbi'

# Connect to a database
dbh = DBI.connect('dbi:Mysql:rubydbi:localhost', 'root', '123456')

dbh.do('DROP TABLE IF EXISTS people')
dbh.do('CREATE TABLE people (id int, name varchar(30))')

# Insert some rows, use placeholders
sql = 'INSERT INTO people VALUES (?, ?)'
dbh.prepare(sql) do |sth|
    sth.execute('100', 'Michael')
    sth.execute('200', 'John')
end

# Select all rows
sth = dbh.prepare('SELECT * FROM people')
sth.execute

# Print out each row
while row = sth.fetch do
    p row
end

# Close the statement handle when done
sth.finish

dbh.do('DROP TABLE people')

# Finally, disconnect
dbh.disconnect

JPA + Spring 2入门

JPA(Java Persistence API)是EJB 3.0 新引入的一个把Java数据对象映射成关系数据库对象的数据持久化编程模型,它弥补了JDBC、ORM、EJB 2等在Java对象持久化方面的不足之处,并且非常易于使用。JPA可以被当作一个单独的 POJO(Plain Old Java Object)持续化使用,或者被集成到任意Java EE兼容的容器或其它的轻量级框架(例如Spring)等一起使用。

配置JPA

Spring提供了两种方法创建JPA的EntityManagerFactory对象。

方法1:

1
2
3
4
5
<beans>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="persistenceUnit" />
    </bean>
</beans>

FactoryBean创建的EntityManagerFactory适用于仅通过JPA进行数据访问的环境。由于使用了PersistenceProvider自动侦测机制,所以只能从默认路径classpath:META-INF/persistence.xml中读取配置信息。

方法2:

1
2
3
4
5
6
7
8
<beans>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="someDataSource" />
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver" />
        </property>
    </bean>
</beans>

FactoryBean提供了对JPA EntityManagerFactory的完整控制,非常适合那种有简单定制需要的环境。你可以处理多个persistence.xml配置文件;覆盖persistence.xml文件的默认路径;可以传递Spring托管的JDBC DataSource给JPA PersistenceProvider,用来替代persistence.xml中的JDBC配置(这个Spring托管的DataSource通常被作为nonJtaDataSource传送给PersistenceProvider,并且覆盖persistence.xml中相同的nonJtaDataSource)。

数据访问

基于JPA的DAO可以通过三种方式进行数据访问JpaDaoSupport,JpaTemplate和plain JPA。其中JpaTemplate是plain JPA的封装,而JpaDaoSupport又是JpaTemplate的封装。无疑,使用不对Spring产生任何依赖的Plain JPA的API进行编程是最好选择。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ProductDaoImpl implements ProductDao {
    private EntityManager entityManager = null;

    @PersistenceContext
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public Collection loadProductsByCategory(String category) {
         Query query = em.createQuery("from Product as p where p.category = :category");
         query.setParameter("category", category);
         return query.getResultList();
    }
}

需要注意的是,必须激活PersistenceAnnotationBeanPostProcessor功能才能让Spring识别@PersistenceContext注解。

1
2
3
4
5
6
<beans>
    <!-- JPA annotations bean post processor -->
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

    <bean id="productDao" class="com.codemany.netlink.dao.impl.ProductDaoImpl" />
</beans>

异常转化

Spring提供了一个允许通过使用@Repository注解进行透明的异常转化的解决方案。

1
2
@Repository
public class ProductDaoImpl implements ProductDao {
1
2
3
<beans>
    <!-- Exception translation bean post processor -->
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

后置处理器将自动的寻找所有的异常转化器(PersistenceExceptionTranslator这个接口的实现类)并通知所有打上@Repository注解的bean,从而能够使得被找到的异常转化器能够在抛出异常时做相应的异常转化工作。

总的来说,DAO能够基于普通的Java持久层API和注解来实现,但同样也能享受到由Spring管理事务、IoC和透明的异常转化(转化成为Spring的异常体系)等好处。

重新认识button元素

英文原文:http://www.particletree.com/features/rediscovering-the-button-element/

简介

对每个应用程序设计者来说,为你的用户创建一套一致的界面是个不懈的斗争。在Web上构建一致性尤为艰难,因为跨越浏览器和操作系统的视觉渲染差异是完全不同的,并且在什么能做和不能做上几乎是随心所欲的。在你处理 表单元素时这个问题变得更为明显。在标准外观的战争里,它们中最大的输家是臭名昭著的提交按钮。

现状是,带有type="submit"的输入要么是太丑陋(Firefox),有点缺陷(Internet Explorer),要么完全缺乏弹性(Safari)。大多数的解决方案是使用图片输入和自己创建这个该死的东西。这是不幸的,因为那样做以后每当我们急需一个新按钮时工作就被归结为启用Photoshop的繁琐任务。我们需要的是更好的东西——一些对于设计者来说更加灵活和有意义的东西。我们很幸运,因为解决方案已经存在,它所需要的是一点点爱。我的朋友们,让我向你介绍我的小朋友:<button>元素。

输入 vs 按钮

这里是标准的提交按钮标记:

1
<input type="submit" value="Submit" />

它在浏览器三兄弟里看起来像这样:

input-submit

嗯。这里是创建一个用于提交的按钮元素时使用的标记:

1
<button type="submit">Submit</button>

并且它看起来像这样:

button-tag

这些按钮与上面对等物的工作和行为方式完全相同。除提交表单外,你可以让它们禁用,添加一个accesskey甚至指定一个tabindex。除了Safari里的按钮看起来有视觉差异(它没有把不自然的aqua界面放到按钮上,这对我们是有利的),关于<button>标签最酷的事情是你可以在它们里面放置有用的HTML元素,像图片:

1
<button type="submit"><img src="" alt="" /> Submit</button>

它们看起来像这样:

button-images

非常好。(好吧,它们有点丑,但是我说过它们需要一点点爱。)实际上,根据W3C所说,这些特殊的视觉差异正是<button>元素被创建的原因。

Buttons created with the BUTTON element function just like buttons created with the INPUT element, but they offer richer rendering possibilities: the BUTTON element may have content. For example, a BUTTON element that contains an image functions like and may resemble an INPUT element whose type is set to “image”, but the BUTTON element type allows content.

The Button Element - W3C

所以这里我们正在寻找一种设计解决方案,好在拥有海量资料的互联网上有一段标记可以帮助我们解决这个问题。这很方便,然而不幸的是大多数设计者和开发者甚至不知道该元素存在。现在,在我直接用按钮元素替换所有在Wufoo中的图片输入之前,我决定标记和CSS必须满足几个需求:

需求

  1. 它们必须看起来像按钮。
  2. 它们必须在浏览器里看起来相同。
  3. 我用于按钮的样式需要同样能被使用在链接上(因为在Wufoo中的交互总是由一个表单提交或者从链接来的一个Ajax调用引发,这些东西很可能并排排列,我需要它们具有相同的视觉重量感)。
  4. 在许多不同情况下使用时标记需要保持弹性和易于修改。
  5. 我应该能够有效地使用图标和颜色来传递关于这种交互将要发生的信息。

带着这些适当的挑战,我潜进CSS,在解决一些跨浏览器的挑战之后,得出了以下内容(你也可以在Wufoo上看到所有的这些内容):

结果

buttons

没有什么疯狂的。简单,但是有效。现在,我喜欢这种处理按钮的方式是我可以使用FAMFAMFAM的1000个图标库去说明大量可笑的想法和动作而不必每一次我需要一些新东西时都要从Photoshop生成。如果我们快速查看下标记,你会注意到在那里的最后两个按钮实际上是链接:

1
2
3
4
5
6
7
8
9
10
11
<div class="buttons">
    <button type="submit" class="positive">
        <img src="/images/icons/tick.png" alt=""/> Save
    </button>
    <a href="/password/reset/">
        <img src="/images/icons/key.png" alt=""/> Change Password
    </a>
    <a href="#" class="negative">
        <img src="/images/icons/cross.png" alt=""/> Cancel
    </a>
</div>

这很有用的原因是因为在Web应用中许多动作是由REST驱动的,所以只要通过链接把用户请求发送到某个特定的URL就会发起它们需要做的事情。使用可以在这两种类型的元素(链接和按钮)上工作的样式,为我们提供了灵活性,以保持我们的交互方式看起来是一致的和适当的,不管它是通过Ajax完成还是一个标准的提交。

仅仅是一些简短的题外话。你可能想知道我为什么把那些图标图片中的alt属性留成空白。它可能会令一些人感到惊讶,虽然每个图片的alt属性是必需的,实际上描述它们不是必需的。空的alt属性完全有效,并且帮助屏幕阅读器知道哪些信息事实上要被忽略,在你试图寻找下一个适当的可操作条目时节省你用户宝贵的时间。因为图标实际上是多余的,我宁愿不要浪费用户的时间听到我用于可视化的图片发生了什么事。它们只会听到“Submit”而不是“Checkmark Submit”,这实际上会让事情有点困惑。

CSS

在大多数情况下,样式化这些按钮的CSS是相当直白的。让人抓破脑袋的跨浏览器的不一致性导致下列代码中填充的数量不一致,但没什么不可能,你们很幸运,这个问题已经解决了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* BUTTONS */
.buttons a, .buttons button {
    display: block;
    float: left;
    margin: 0 7px 0 0;
    background-color: #f5f5f5;
    border: 1px solid #dedede;
    border-top: 1px solid #eee;
    border-left: 1px solid #eee;
    font-family: "Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
    font-size: 100%;
    line-height: 130%;
    text-decoration: none;
    font-weight: bold;
    color: #565656;
    cursor: pointer;
    padding: 5px 10px 6px 7px;    /* Links */
}
.buttons button {
    width: auto;
    overflow: visible;
    padding: 4px 10px 3px 7px;    /* IE6 */
}
.buttons button[type] {
    padding: 5px 10px 5px 7px;    /* Firefox */
    line-height: 17px;    /* Safari */
}
*:first-child+html button[type] {
    padding: 4px 10px 3px 7px;    /* IE7 */
}
.buttons button img, .buttons a img {
    margin: 0 3px -3px 0 !important;
    padding: 0;
    border: none;
    width: 16px;
    height: 16px;
}

当致力于不一致性的时候有件事出现了,就是Internet Explorer中关于显示长按钮有个渲染缺陷的事实。你可以在Jehiah.cz阅读相关内容,但它是什么导致了以上代码中一些宽度和溢出的声明。

添加一些颜色

在Wufoo中,我们使悬停颜色蓝色成为中性动作,对于正面和负面内涵适当地使用绿色和红色。以下的代码是我们为处理按钮而创建的样式,这些按钮意味着显示像添加和保存这样的正面交互以及像取消和删除这样的负面交互。对我们来说这是一次不错的尝试,显然你可以根据自己的喜好选择颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/* STANDARD */
button:hover, .buttons a:hover {
    background-color: #dff4ff;
    border: 1px solid #c2e1ef;
    color: #336699;
}
.buttons a:active {
    background-color: #6299c5;
    border: 1px solid #6299c5;
    color: #fff;
}
/* POSITIVE */
button.positive, .buttons a.positive {
    color: #529214;
}
.buttons a.positive:hover, button.positive:hover {
    background-color: #e6efc2;
    border: 1px solid #c6d880;
    color: #529214;
}
.buttons a.positive:active {
    background-color: #529214;
    border: 1px solid #529214;
    color: #fff;
}
/* NEGATIVE */
.buttons a.negative, button.negative {
    color: #d12f19;
}
.buttons a.negative:hover, button.negative:hover {
    background: #fbe3e4;
    border: 1px solid #fbc2c4;
    color: #d12f19;
}
.buttons a.negative:active {
    background-color: #d12f19;
    border: 1px solid #d12f19;
    color: #fff;
}

总结

最后,这只是我们决定如何用Wufoo处理事情,它是为我们制定的开发流程。当然,这不是玩这个游戏的唯一方法。有许多你可以给它添加趣味的方法(使用渐变!)和改变周围的事物(在标记中的图片上使用图片替换)。因为<button>标签几乎可以处理在它里面的任何标记,你可以添加一些<span>标签,然后使用Alex Griffioen最近写的方法来创建真正漂亮的圆角渐变作品。老实说,我希望对于许多与他们应用中可复用表单界面斗争的设计者来说这只是一个很好的起点。或许甚至,我希望你再次看看这个经常被浪费的表单元素,在不经思考就为提交采用输入和PSD之前要二思。

批量更新表中某个字段的值到另一个字段的存储过程(MySQL版)

对博客做了一些修改,需要将entries表中的updatetime字段值替换成pubtime字段的值,为此写了一个MySQL存储过程,其中涉及游标的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DELIMITER //

CREATE PROCEDURE sp_batch_replace_field_value_with_another()
BEGIN
    DECLARE p_id INT;
    DECLARE p_pubtime DATETIME;
    DECLARE p_done BOOL;
    DECLARE p_cursor CURSOR FOR SELECT id, pubtime FROM entries;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET p_done = true;

    OPEN p_cursor;
        REPEAT
            FETCH p_cursor INTO p_id, p_pubtime;
            UPDATE entries SET updatetime = p_pubtime WHERE id = p_id;
            UNTIL p_done = true
        END REPEAT;
    CLOSE p_cursor;
END//

DELIMITER ;

第一次在SQLyog中写存储过程,在修改了存储过程后竟然不知道如何保存。点击File -> Save却是保存为另外一个文件,而不是直接保存修改的存储过程。经过多番尝试后发现按F5键就可以了。

双色球(Union Lotto)模拟摇奖器

双色球基本规则:

  1. 彩票投注区分为红色球号码区和蓝色球号码区;
  2. 每注投注号码由6个红色球号码和1个蓝色球号码组成。红色球号码从1-33中选择;蓝色球号码从1-16中选择;
  3. 摇奖时先摇出6个红色球号码,再摇出1个蓝色球号码,摇出的红色球号码按从小到大的顺序和蓝色球号码一起公布。

双色球模拟摇奖器代码:

1
2
3
4
5
6
7
# union_lotto.rb
red_balls = Array.new(33) {|i| i + 1}
blue_balls = Array.new(16) {|i| i + 1}
1.upto(6) do
  print red_balls.delete_at(rand(red_balls.length)), ' '
end
puts '- ' + blue_balls.delete_at(rand(blue_balls.length)).to_s

Cannot delete or update a parent row: a foreign key constraint fails

在SQLyog中Import Data或Empty Table时,出现:

1
2
Error No. 1451
Cannot delete or update a parent row: a foreign key constraint fails

查看官方文档Setting FOREIGN_KEY_CHECKS to 0 can also be useful for ignoring foreign key constraints during LOAD DATA and ALTER TABLE operations.知道这可能是MySQL在InnoDB中设置了foreign key关联,造成无法更新或删除数据。可以通过设置FOREIGN_KEY_CHECKS变量来避免这种情况。

1
SET FOREIGN_KEY_CHECKS = 0;

JavaScript中的undefined

JavaScript有6种类型,undefined、null、boolean、number、string和object。其中,null类型只有一个值null。undefined类型也只有一个值,即undefined,当声明的变量没有被赋值的时候,该变量的默认值就是undefined。

1
var foo;

上面这行代码声明了变量foo,没有赋值。脚本引擎在处理代码的时候会给该变量赋予值undefined。实际上,该行代码等价于

1
var foo = undefined;

可以用下面的代码测试:

1
2
3
4
var foo;
alert(foo == undefined);    // true
alert(foo === undefined);    // true
alert(typeof(foo));    // undefined

JavaScript另一个有趣的方面是在使用变量之前不必声明。例如:

1
2
3
var foo = "Hello ";
bar = foo + "world!";
alert(bar);    // Hello world!

变量bar并没有用var运算符定义,这里只是使用了它,就像已经声明过它一样。脚本引擎遇到未声明过的标识符时,用该变量名创建一个全局变量,并将其初始化为指定的值(这里是foo + "world")。这是该语言的便利之处,不过如果不能紧密跟踪变量,这样做也很危险。考虑下面的代码:

1
2
3
4
5
var foo;
// Make sure this variable isn't defined
//var bar;
alert(foo);
alert(bar);

执行的结果是:

undefined

undefined-error

alert(bar)出现错误是因为bar被创建成全局变量后没有被初始化,还处在未初始化(uninitialized)状态。但是,typeof运算符并不真正区分这两种值。考虑下面的代码:

1
2
3
4
5
var foo;
// Make sure this variable isn't defined
//var bar;
alert(typeof(foo));    // undefined
alert(typeof(bar));    // undefined

前面的代码对两个变量的输出都是“undefined”,即使变量bar在使用前没有被声明。

当函数无明确返回值时,返回的也是值undefined,如下所示:

1
2
3
function foobar() { }
alert(foobar() == undefined);    // true
alert(foobar() === undefined);    // true

ECMAScript认为undefined是从null派生出来的,所以把它们定义为相等的:

1
alert(null == undefined);    // true

尽管这两个值相等,但它们的含义不同。undefined是声明了变量但未赋值时赋予该变量的值,null则用于表示尚未存在的对象(但从技术上来说,null仍然是原始值)。如果要区分两者,要使用===或typeof运算符。

CSS盒模型

平面示意图

box-model-01

3D示意图(原图出自:hicksdesign)

box-model-02

Flash示意模型

W3C DOM - {setAttribute()}

setAttribute(string name, string value):增加一个指定名称和值的新属性,或者把一个现有的属性设定为指定的值。

setAttribute()的差异

我们经常需要在JavaScript中给Element动态添加各种属性,这可以通过使用setAttribute()来实现,这就涉及到了浏览器的兼容性问题。

1
2
var obj = document.getElementById("obj");
obj.setAttribute("onclick", "javascript:alert('This is a test!');");

这里利用setAttribute指定Element的onclick属性,简单,很好理解。但是IE不支持,IE并不是不支持setAttribute这个函数,而是不支持用setAttribute设置某些属性,例如对象属性、集合属性、事件属性,也就是说用setAttribute设置style和onclick 这些属性在IE中是行不通的。为达到兼容各种浏览器的效果,可以用点符号法来设置Element的对象属性、集合属性和事件属性。

1
2
3
4
document.getElementById("obj").className = "fruit";
document.getElementById("obj").style.cssText = "color: #00f;";
document.getElementById("obj").style.color = "#00f";
document.getElementById("obj").onclick = function () { alert("This is a test!"); }

关于class和className

class属性在W3C DOM中扮演着很重要的角色,但由于浏览器差异性仍然存在。使用setAttribute("class", vName)语句动态设置element的class属性在Firefox中是行的通的,在IE中却不行。因为使用IE内核的浏览器不认识“class”,要改用“className”;同样,Firefox也不认识“className”。所以常用的方法是二者兼备:

1
2
element.setAttribute("class", vName);
element.setAttribute("className", vName);    // for IE

cellspacing和cellpadding

虽然在CSS中存在与cellpadding和cellspacing这两个属性等价的样式属性padding和border-spacing。然而,浏览器对这些样式属性支持的不一致,有时仍会使用cellpadding和cellspacing来调整表格的间距。不过,在Firefox中有效的setAttribute("cellpadding", value)到了IE下就不行了,必须改成cellPadding才可以(注意:p要大写)。幸好Firefox也支持setAttribute("cellPadding", value)。所以二者兼容的代码是:

1
2
element.setAttribute("cellSpacing", value);
element.setAttribute("cellPadding", value);

JSON in JavaScript

英文原文:http://www.json.org/js.html

JavaScript是一门通用编程语言,被作为页面脚本语言引入Netscape Navigator。它仍被广泛地认为是Java的一个子集,但它不是。它是一门有着类C语法soft objects的类Scheme语言,JavaScript在ECMAScript Language Specification, Third Edition中被标准化。

JSON是JavaScript对象字面量表示法(object literal notation)的一个子集。因为JSON是JavaScript的一个子集,所以它可以毫不费力地被用在这门语言中。

1
2
3
4
5
6
var myJSONObject = {"bindings": [
        {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"},
        {"ircEvent": "PRIVMSG", "method": "deleteURI", "regex": "^delete.*"},
        {"ircEvent": "PRIVMSG", "method": "randomURI", "regex": "^random.*"}
    ]
};

在这个例子中,一个含有单个成员“bindings”的对象被创建,该成员包含一个含有三个对象的数组,每个对象含有“ircEvent”,“method”和“regex”三个成员。

成员可以通过点或下标运算符检索。

1
myJSONObject.bindings[0].method    // "newURI"

你可以使用eval函数将一段JSON文本转换成一个对象,eval函数会调用JavaScript编译器。因为JSON是JavaScript的一个真子集(a proper subset),编译器将正确地解析文本并产生一个对象结构。文本必须被包裹在括号内,以免在JavaScript的语法中的歧义上犯错误。

1
var myObject = eval('(' + myJSONtext + ')');

eval函数是非常快的。然而,它可以编译和执行任何JavaScript程序,所以可能会有安全问题。当源代码是可信的与合格的的时候才可以使用eval函数。使用JSON语法分析器则安全的多。在基于XMLHttpRequest的Web应用中,通讯只被允许朝着提供该页面的同源的方向,所以它是可信的。但它可能不是合格的。如果服务器在它的JSON编码里不是严格的,或者它没有严谨地验证所有的输入,然后它可能交付无效的JSON文本,它们可能携带危险的脚本。eval函数将会执行这些脚本,释放恶意。

为抵御这些,应该使用JSON语法分析器。JSON语法分析器将只识别JSON文本,拒绝所有的脚本。在提供本地JSON支持的浏览器里,JSON语法分析器也比eval快的多。预计本地JSON支持将会被包含在下一个ECMAScript标准中。

1
var myObject = JSON.parse(myJSONtext, reviver);

可选的reviver参数是一个函数,它会被最终结果的各个层面上的每个键和值调用。每个值会被reviver函数的返回结果替换。这可以被用来将通用对象改编成为伪类实例,或者被用来将date字符串转化成Date对象。

1
2
3
4
5
6
7
8
9
10
myData = JSON.parse(text, function (key, value) {
    var type;
    if (value && typeof value === 'object') {
        type = value.type;
        if (typeof type === 'string' && typeof window[type] === 'function') {
            return new (window[type])(value);
        }
    }
    return value;
});

JSON stringifier则作相反的工作,它将JavaScript数据结构转换为JSON文本。JSON不支持循环的数据结构,所以注意不要把循环的结构交给JSON stringifier。

1
var myJSONText = JSON.stringify(myObject, replacer);

如果stringify方法看到一个对象包含toJSON方法,它会调用该方法,并字符串化返回的值。这允许一个对象去确定其自己的JSON表示。

stringify~~原文是stringifier,疑似拼错~~方法可以接受一个可选的字符串数组。这些字符串被用于选取那些将会被包含在JSON文本中的属性。

stringify~~原文是stringifier,疑似拼错~~方法可以接受一个可选的replacer函数。该函数会在结构中每个值的toJSON方法(如果有)之后被调用。它将每个键和值作为参数传递,并且this将会被绑定到持有键的对象上。返回的值会被字符串化。

没有在JSON中表示的值(例如函数和undefined)被排除在外。

非有限的数字被替换为null。要替换成其它的值,你可以使用像这样的replacer函数:

1
2
3
4
5
6
function replacer(key, value) {
    if (typeof value === 'number' && !isFinite(value)) {
        return String(value);
    }
    return value;
}

给JSON.parse一个相应的reviver可以还原这些。

The open source code of a JSON parser and JSON stringifier is available. When minified it is less than 2.5K.