<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>为之漫笔 &#187; 编程技术</title>
	<atom:link href="http://www.cn-cuckoo.com/category/programming/feed" rel="self" type="application/rss+xml" />
	<link>http://www.cn-cuckoo.com</link>
	<description>为之漫笔（李松峰），本博客专注于Web前后端技术、移动平台开发技术、交互设计和技术翻译。声明一下，因为时常需要外出审稿，而且基本不带笔记本，所以有时可能会迟一点回复大家的留言。</description>
	<lastBuildDate>Mon, 12 Dec 2011 01:43:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>使用JavaScript和Canvas开发游戏（五）</title>
		<link>http://www.cn-cuckoo.com/2011/08/17/game-development-with-javascript-and-the-canvas-element-5-2645.html</link>
		<comments>http://www.cn-cuckoo.com/2011/08/17/game-development-with-javascript-and-the-canvas-element-5-2645.html#comments</comments>
		<pubDate>Wed, 17 Aug 2011 00:22:03 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2645</guid>
		<description><![CDATA[原文作者：Matthew Casperson • 编辑：Michele McDonough 原文链接: Game Development with JavaScript and the Canvas element 1、认识一下Canvas 2、在Canvas上绘图 3、通过Canvas元素实现高级图像操作 4、写一个游戏框架（一） 5、写一个游戏框架（二） 6、通过Canvas实现视差滚动 7、动画 8、JavaScript键盘输入 9、综合运用 10、定义级别 11、跳跃与坠落 12、添加道具 13、加载资源 14、添加主菜单 在Canvas元素上实现视差滚动 http://www.brighthub.com/internet/web-development/articles/40511.aspx 视差滚动是在2D应用中创造立体纵深感的一种技术。这篇文章就来看一看在我们刚刚创建的游戏框架基础上实现视差滚动有多容易。 视差滚动 有了游戏框架，就可以通过画布元素来做一些好玩的东西了。 视差滚动指的是屏幕上的几个图层发生相对位移的效果，换句话说，背景图层滚动得比它前面的那些图层要慢一些。这种创造视觉纵深感的技术在2D游戏中的应用极为普遍。 RepeatingGameObject.js /** 这个类可以重复显示纹理图像，支持纹理图像在x轴或y轴偏移 @class */ function RepeatingGameObject() { /** 最终图像占据的宽度 @type Number */ this.width = 0; /** 最终图像占据的高度 @type Number */ [...]]]></description>
			<content:encoded><![CDATA[<p>原文作者：Matthew Casperson • 编辑：Michele McDonough<br />
原文链接: <a href="http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx" target="_blank">Game Development with JavaScript and the Canvas element</a></p>
<p>1、认识一下Canvas<br />
2、在Canvas上绘图<br />
3、通过Canvas元素实现高级图像操作<br />
4、写一个游戏框架（一）<br />
5、写一个游戏框架（二）<br />
6、通过Canvas实现视差滚动<br />
7、动画<br />
8、JavaScript键盘输入<br />
9、综合运用<br />
10、定义级别<br />
11、跳跃与坠落<br />
12、添加道具<br />
13、加载资源<br />
14、添加主菜单</p>
<h1>在Canvas元素上实现视差滚动</h1>
<p><a href="http://www.brighthub.com/internet/web-development/articles/40511.aspx" target="_blank">http://www.brighthub.com/internet/web-development/articles/40511.aspx</a></p>
<p>视差滚动是在2D应用中创造立体纵深感的一种技术。这篇文章就来看一看在我们刚刚创建的游戏框架基础上实现视差滚动有多容易。</p>
<h2>视差滚动</h2>
<p>有了游戏框架，就可以通过画布元素来做一些好玩的东西了。</p>
<p>视差滚动指的是屏幕上的几个图层发生相对位移的效果，换句话说，背景图层滚动得比它前面的那些图层要慢一些。这种创造视觉纵深感的技术在2D游戏中的应用极为普遍。</p>
<h2>RepeatingGameObject.js</h2>
<pre class="brush: js; ">

/**
    这个类可以重复显示纹理图像，支持纹理图像在x轴或y轴偏移
    @class
*/
function RepeatingGameObject()
{
    /** 最终图像占据的宽度
 	@type Number
    */
    this.width = 0;
    /** 最终图像占据的高度
    	@type Number
    */
    this.height = 0;
    /** 绘制时应用多少scrollX和scrollY
	@type Number
    */
    this.scrollFactor = 1;

    /**
        初始化对象
        @return 对初始化对象的引用
    */
    this.startupRepeatingGameObject = function(image, x, y, z, width, height, scrollFactor)
    {
        this.startupVisualGameObject(image, x, y, z);
        this.width = width;
        this.height = height;
        this.scrollFactor = scrollFactor;
        return this;
    }

    /**
        清理对象
    */
    this.shutdownstartupRepeatingGameObject = function()
    {
        this.shutdownVisualGameObject();
    }

	/**
        把当前元素绘制到后台缓冲
        @param dt 自上一帧绘制起经过的秒数
		@param context 绘制上下文
		@param xScroll x轴的全局滚动值
		@param yScroll y轴的全局滚动值

    */
    this.draw = function(dt, canvas, xScroll, yScroll)
    {
        var areaDrawn = [0, 0];

        for (var y = 0; y &lt; this.height; y += areaDrawn[1])
        {
            for (var x = 0; x &lt; this.width; x += areaDrawn[0])
            {
                // 绘制下一张贴片左上角的点
		var newPosition = [this.x + x, this.y + y];
		// 剩余的绘制空间
                var newFillArea = [this.width - x, this.height - y];
		// 第一次必须从图像的中央开始绘制
		// 后续贴片从上方或左侧绘制
                var newScrollPosition = [0, 0];
                if (x==0) newScrollPosition[0] = xScroll * this.scrollFactor;
                if (y==0) newScrollPosition[1] = yScroll * this.scrollFactor;
                areaDrawn = this.drawRepeat(canvas, newPosition, newFillArea, newScrollPosition);
            }
        }
    }

    this.drawRepeat = function(canvas, newPosition, newFillArea, newScrollPosition)
    {
        // 找到重复绘制纹理图像的起点（左上角）
        var xOffset = Math.abs(newScrollPosition[0]) % this.image.width;
        var yOffset = Math.abs(newScrollPosition[1]) % this.image.height;
        var left = newScrollPosition[0]&lt;0?this.image.width-xOffset:xOffset;
        var top = newScrollPosition[1]&lt;0?this.image.height-yOffset:yOffset;
        var width = newFillArea[0] &lt; this.image.width-left?newFillArea[0]:this.image.width-left;
        var height = newFillArea[1] &lt; this.image.height-top?newFillArea[1]:this.image.height-top;

        // 绘制图像
        canvas.drawImage(this.image, left, top, width, height, newPosition[0], newPosition[1], width, height);

        return [width, height];
    }
}
RepeatingGameObject.prototype = new VisualGameObject();
</pre>
<p>这个RepeatingGameObject类可以让图像在一个确定的区域内重复和滚动。此前，我们已经实现了绘制整幅图像。而RepeatingGameObject的不同之处在于，它拿到一幅图像并用它来填充一块范围既定的区域（其尺寸与绘制的图像无关）。我们通过这个类每次显示一幅大图像（如一座山的全景图）的一小部分，从而创建出一个背景。</p>
<p>也许你已经注意到了GameObjectManager的xScroll和yScroll属性，它们被传递给了GameObject的draw和updata函数。这两个值定义的是摄像机沿x轴或y轴移动了多远。RepeatingGameObject使用这两个值来它们显示的纹理，以创造移动的假象。</p>
<p>首先，定义RepeatingGameObject要绘制的区域。底层的GameObject类的x和y属性定义了左上角位置，而新的width和height属性定义的是绘制区域。</p>
<pre class="brush: js; ">

    /** 最终图像占据的宽度
 	@type Number
    */
    this.width = 0;
    /** 最终图像占据的高度
    	@type Number
    */
    this.height = 0;
    /** 绘制时应用多少scrollX和scrollY
	@type Number
    */
</pre>
<p>而scrollFactor属性用于改变RepeatingGameObject滚动的量，该变化是通过传递到draw函数的xScroll和yScroll来控制的。将scrollFactor设置为小于1的值，会导致滚动变慢，从而造就画面中的远景。</p>
<pre class="brush: js; ">

    /** 绘制时应用多少scrollX和scrollY
	@type Number
    */
    this.scrollFactor = 1;
</pre>
<p>最后两个draw和drawRepeat函数具体负责渲染贴片及偏移的纹理。</p>
<pre class="brush: js; ">

	/**
        把当前元素绘制到后台缓冲
        @param dt 自上一帧绘制起经过的秒数
		@param context 绘制上下文
		@param xScroll x轴的全局滚动值
		@param yScroll y轴的全局滚动值

    */
    this.draw = function(dt, canvas, xScroll, yScroll)
    {
        var areaDrawn = [0, 0];

        for (var y = 0; y &lt; this.height; y += areaDrawn[1])
        {
            for (var x = 0; x &lt; this.width; x += areaDrawn[0])
            {
                // 绘制下一张贴片左上角的点
		var newPosition = [this.x + x, this.y + y];
		// 剩余的绘制空间
                var newFillArea = [this.width - x, this.height - y];
		// 第一次必须从图像的中央开始绘制
		// 后续贴片从上方或左侧绘制
                var newScrollPosition = [0, 0];
                if (x==0) newScrollPosition[0] = xScroll * this.scrollFactor;
                if (y==0) newScrollPosition[1] = yScroll * this.scrollFactor;
                areaDrawn = this.drawRepeat(canvas, newPosition, newFillArea, newScrollPosition);
            }
        }
    }

    this.drawRepeat = function(canvas, newPosition, newFillArea, newScrollPosition)
    {
        // 找到重复绘制纹理图像的起点（左上角）
        var xOffset = Math.abs(newScrollPosition[0]) % this.image.width;
        var yOffset = Math.abs(newScrollPosition[1]) % this.image.height;
        var left = newScrollPosition[0]&lt;0?this.image.width-xOffset:xOffset;
        var top = newScrollPosition[1]&lt;0?this.image.height-yOffset:yOffset;
        var width = newFillArea[0] &lt; this.image.width-left?newFillArea[0]:this.image.width-left;
        var height = newFillArea[1] &lt; this.image.height-top?newFillArea[1]:this.image.height-top;

        // 绘制图像
        canvas.drawImage(this.image, left, top, width, height, newPosition[0], newPosition[1], width, height);

        return [width, height];
    }   
</pre>
<h2>ApplicationManager.js</h2>
<pre class="brush: js; ">

/**
    ApplicationManager用于管理应用
    @class
*/
function ApplicationManager()
{
    /**
        初始化对象
        @return A 对初始化对象的引用
    */
    this.startupApplicationManager = function()
    {
        this.startupGameObject();
	this.background3 = new RepeatingGameObject().startupRepeatingGameObject(g_back2, 0, 100, 3, 600, 320, 1);
        this.background2 = new RepeatingGameObject().startupRepeatingGameObject(g_back1, 0, 100, 2, 600, 320, 0.75);
        this.background = new RepeatingGameObject().startupRepeatingGameObject(g_back0, 0, 0, 1, 600, 320, 0.5);
        return this;
    }

	/**
        更新当前对象
        @param dt 自上一帧绘制起经过的秒数
        @param context 绘制上下文
        @param xScroll x轴的全局滚动值
        @param yScroll y轴的全局滚动值
    */
    this.update = function(/**Number*/ dt, /**CanvasRenderingContext2D*/ context, /**Number*/ xScroll, /**Number*/ yScroll)
    {
		g_GameObjectManager.xScroll += 50 * dt;
	}
}
ApplicationManager.prototype = new GameObject
</pre>
<p>在这里，我们通过ApplicationManager创建了三个RepeatingGameObject类的实例，每个实例分别显示为一个图层，使用z（深度）和scrollFactor值来创造RepeatingGameObject 渐远和渐慢的效果。</p>
<p>最终结果很不错。视差滚动为画布赋予了完美的纵深感，而整个效果只多编写了一个类就实现了。</p>
<p>看一看视差滚动的Demo吧。<a href="http://webdemos.sourceforge.net/jsplatformer4/jsplatformer4.html" target="_blank">http://webdemos.sourceforge.net/jsplatformer4/jsplatformer4.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/08/17/game-development-with-javascript-and-the-canvas-element-5-2645.html/feed</wfw:commentRss>
		<slash:comments>44</slash:comments>
		</item>
		<item>
		<title>使用JavaScript和Canvas开发游戏（四）</title>
		<link>http://www.cn-cuckoo.com/2011/08/15/game-development-with-javascript-and-the-canvas-element-4-2639.html</link>
		<comments>http://www.cn-cuckoo.com/2011/08/15/game-development-with-javascript-and-the-canvas-element-4-2639.html#comments</comments>
		<pubDate>Mon, 15 Aug 2011 11:56:57 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2639</guid>
		<description><![CDATA[原文作者：Matthew Casperson • 编辑：Michele McDonough 原文链接: Game Development with JavaScript and the Canvas element 1、认识一下Canvas 2、在Canvas上绘图 3、通过Canvas元素实现高级图像操作 4、写一个游戏框架（一） 5、写一个游戏框架（二） 6、通过Canvas实现视差滚动 7、动画 8、JavaScript键盘输入 9、综合运用 10、定义级别 11、跳跃与坠落 12、添加道具 13、加载资源 14、添加主菜单 4、写一个游戏框架（二） 在这篇文章里，我们继续介绍构成这个基本JavaScript游戏框架的其他必要的类。 前一篇文章介绍了GameObjectManager类，该类负责画布的渲染并允许GameObject类更新和删除它们自己。下面就来看一看GameObject类。 GameObject.js /** 游戏中出现的所有元素的基类 @class */ function GameObject() { /** 显示的深度次序。较小的zOrder值表示先渲染，因而会在背景中。 @type Number */ this.zOrder = 0; /** x轴的坐标 @type Number */ this.x = 0; [...]]]></description>
			<content:encoded><![CDATA[<p>原文作者：Matthew Casperson • 编辑：Michele McDonough<br />
原文链接: <a href="http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx" target="_blank">Game Development with JavaScript and the Canvas element</a></p>
<p>1、认识一下Canvas<br />
2、在Canvas上绘图<br />
3、通过Canvas元素实现高级图像操作<br />
4、写一个游戏框架（一）<br />
5、写一个游戏框架（二）<br />
6、通过Canvas实现视差滚动<br />
7、动画<br />
8、JavaScript键盘输入<br />
9、综合运用<br />
10、定义级别<br />
11、跳跃与坠落<br />
12、添加道具<br />
13、加载资源<br />
14、添加主菜单</p>
<h1>4、写一个游戏框架（二）</h1>
<p>在这篇文章里，我们继续介绍构成这个基本JavaScript游戏框架的其他必要的类。</p>
<p>前一篇文章介绍了GameObjectManager类，该类负责画布的渲染并允许GameObject类更新和删除它们自己。下面就来看一看GameObject类。</p>
<h2>GameObject.js</h2>
<pre class="brush: js; ">

/**
    游戏中出现的所有元素的基类
    @class
*/
function GameObject()
{
    /** 显示的深度次序。较小的zOrder值表示先渲染，因而会在背景中。
        @type Number
    */
    this.zOrder = 0;
    /**
        x轴的坐标
        @type Number
    */
    this.x = 0;
    /**
        y轴的坐标
        @type Number
    */
    this.y = 0;

    /**
        初始化游戏对象，并将其添加到GameObjectManager维护的对象列表中
        @param x        x轴的坐标
        @param y        y轴的坐标
        @param z        元素的z次序（背景元素的z值较小）
    */
    this.startupGameObject = function(/**Number*/ x, /**Number*/ y, /**Number*/ z)
    {
        this.zOrder = z;
        this.x = x;
        this.y = y;
        g_GameObjectManager.addGameObject(this);
        return this;
    }

    /**
        清理当前对象，将其从GameObjectManager维护的对象列表中删除
    */
    this.shutdownGameObject = function()
    {
        g_GameObjectManager.removeGameObject(this);
    }
}
</pre>
<p>这个GameObject类（是一个引擎类）的目的，是为游戏中将会出现的所有对象定义一些共有的属性，包括它们的位置（x和y）和深度（z）。需要注意的是，我们不会直接创建GameObject类的实例，而是会再创建一个类来扩展它。</p>
<p>这个类的x和y坐标值没有什么好说的——就是相应对象左上角位置在画布上的坐标。关键是GameObject中的z值，这个值定义的是对象的深度。理解这个值很重要，这个值较小的GameObject会先绘制到画布上。换句话说，z值较大的GameObject将被绘制到z值较小的GameObject上面。<br />
<span id="more-2639"></span><br />
上一篇文章里介绍过，所有类都是通过一个类似startupClassName的函数完成自身初始化的。因此，GameObject类就有一个名为startupGameObject的函数。在这个函数里，除了初始化所有变量外，还会通过addGameObject函数把当前的GameObject添加到由GameObjectManager维护的GameObject列表中。</p>
<pre class="brush: js; ">

    /**
        初始化游戏对象，并将其添加到GameObjectManager维护的对象列表中
        @param x        x轴的坐标
        @param y        y轴的坐标
        @param z        元素的z次序（背景元素的z值较小）
    */
    this.startupGameObject = function(/**Number*/ x, /**Number*/ y, /**Number*/ z)
    {
        this.zOrder = z;
        this.x = x;
        this.y = y;
        g_GameObjectManager.addGameObject(this);
        return this;
    }
</pre>
<p>函数shutdownGameObject用于清除GameObject。这里所谓的清除，是指GameObject通过removeGameObject函数将自身从GameObjectManager中删除。</p>
<pre class="brush: js; ">

    /**
        清理当前对象，将其从GameObjectManager维护的对象列表中删除
    */
    this.shutdownGameObject = function()
    {
        g_GameObjectManager.removeGameObject(this);
    }
</pre>
<h2>VisualGameObject.js</h2>
<pre class="brush: js; ">

/**
    出现在游戏中的所有元素的基类
    @class
*/
function VisualGameObject()
{
    /**
        由当前对象显示的图像
        @type Image
    */
    this.image = null;

    /**
        将当前元素绘制到后台缓冲
        @param dt 自上一帧绘制起经过的秒数
    */
    this.draw = function(/**Number*/ dt, /**CanvasRenderingContext2D*/ context, /**Number*/ xScroll, /**Number*/ yScroll)
    {
        context.drawImage(this.image, this.x - xScroll, this.y - yScroll);
    }

    /**
        初始化当前对象
        @param image 要显示的图像
    */
    this.startupVisualGameObject = function(/**Image*/ image, /**Number*/ x, /**Number*/ y, /**Number*/ z)
    {
        this.startupGameObject(x, y, z);
        this.image = image;
        return this;
    }

    /**
        清理当前对象
    */
    this.shutdownVisualGameObject = function()
    {
        this.shutdownGameObject();
    }
}
VisualGameObject.prototype = new GameObject;
</pre>
<p>VisualGameObject也是一个引擎类，它扩展了GameObject类，为将在屏幕上绘制的对象定义了更具体的属性和函数。顾名思义，可见对象显然是需要绘制的对象，因此VisualGameObject定义了一个image属性，当把当前对象绘制到后台缓冲时，将以这个属性作为图形的来源。</p>
<pre class="brush: js; ">

    /**
        由当前对象显示的图像
        @type Image
    */
    this.image = null;
</pre>
<p>此外，还需要写几行代码，以便把这个对象实际地绘制到后台缓冲——这就是draw函数了，它接受图像并基于GameObject类中定义的x和y值将其复制到后台缓冲。</p>
<pre class="brush: js; ">

    /**
        将当前元素绘制到后台缓冲
        @param dt 自上一帧绘制起经过的秒数
    */
    this.draw = function(/**Number*/ dt, /**CanvasRenderingContext2D*/ context, /**Number*/ xScroll, /**Number*/ yScroll)
    {
        context.drawImage(this.image, this.x - xScroll, this.y - yScroll);
    }
</pre>
<h2>ApplicationManager.js</h2>
<pre class="brush: js; ">

/**
    ApplicationManager用于管理应用
    @class
*/
function ApplicationManager()
{
    /**
        初始化对象
        @return 对初始化对象的引用
    */
    this.startupApplicationManager = function()
    {
        this.bounce = new Bounce().startupBounce(g_image);
        return this;
    }
}
</pre>
<p>ApplicationManager是第一个应用类，之所以将其归为应用类，是因为它用来定义应用的运行方式，而不是定义与浏览器的底层交互。这个类非常简单，只用来创建并初始化Bounce类的一个新实例。表面上看，创建一个类仅仅是为了创建一个对象有点多此一举。但在更复杂的应用中，把创建和管理游戏对象的逻辑放到一起是很有必要的。</p>
<h2>Bounce.js</h2>
<pre class="brush: js; ">

/**
    测试类，用于演示VisualGameObject类的用法
    @class
*/
function Bounce()
{
	/** x轴的运动方向
        @type Number
    */
	this.xDirection = 1;
	/** y轴的运动方向
        @type Number
    */
	this.yDirection = 1;
	/** 运动速度
        @type Number
    */
	this.speed = 10;

	/**
        初始化对象
        @return 对初始化对象的引用
    */
	this.startupBounce = function(image)
	{
		this.startupVisualGameObject(image, 0, 0, 0);
		return this;
	}

	/**
       更新对象
        @param dt 自上一帧绘制起经过的秒数
        @param context 绘制上下文
        @param xScroll x轴的全局滚动值
        @param yScroll y轴的全局滚动值
    */
	this.update = function (/**Number*/ dt, /**CanvasRenderingContext2D*/context, /**Number*/ xScroll, /**Number*/ yScroll)
	{
		this.x += dt * this.speed * this.xDirection;
		this.y += dt * this.speed * this.yDirection;

		if (this.x &gt;= 450)
		{
			this.x = 450;
			this.xDirection = -1;
		}
		else if (this.x &lt;= 0)
		{
			this.x = 0;
			this.xDirection = 1;
		}

		if (this.y &gt;= 250)
		{
			this.y = 250;
			this.yDirection = -1;
		}
		else if (this.y &lt;= 0)
		{
			this.y = 0;
			this.yDirection = 1;
		}
	}
}
Bounce.prototype = new VisualGameObject;
</pre>
<p>Bounce是第二个应用类，它扩展了VisualGameObject类，并将把自己绘制到屏幕上。Bounce类会显示一幅在屏幕上反弹的图像，效果非常类似第一篇文章中举的例子。这个类是在前面所有类的基础上实现最终动画的关键。</p>
<p>startupBounce函数接受一幅图像，通过调用startupVisualGameObject来初始化这个基本的类。</p>
<pre class="brush: js; ">

	/**
        初始化对象
        @return 对初始化对象的引用
    */
	this.startupBounce = function(image)
	{
		this.startupVisualGameObject(image, 0, 0, 0);
		return this;
	}
</pre>
<p>而update函数（将被GameObjectManager在渲染期间调用）会更新图像的位置，在图像到达画布边缘时反转方向。</p>
<pre class="brush: js; ">

	/**
       更新对象
        @param dt 自上一帧绘制起经过的秒数
        @param context 绘制上下文
        @param xScroll x轴的全局滚动值
        @param yScroll y轴的全局滚动值
    */
	this.update = function (/**Number*/ dt, /**CanvasRenderingContext2D*/context, /**Number*/ xScroll, /**Number*/ yScroll)
	{
		this.x += dt * this.speed * this.xDirection;
		this.y += dt * this.speed * this.yDirection;

		if (this.x &gt;= 450)
		{
			this.x = 450;
			this.xDirection = -1;
		}
		else if (this.x &lt;= 0)
		{
			this.x = 0;
			this.xDirection = 1;
		}

		if (this.y &gt;= 250)
		{
			this.y = 250;
			this.yDirection = -1;
		}
		else if (this.y &lt;= 0)
		{
			this.y = 0;
			this.yDirection = 1;
		}
	}
}
</pre>
<p>就这些了。你可能会想，怎么没有与绘制这个对象有关的代码呢？相应的代码都在VisualGameObject类的draw函数中了。而且，由于VisualGameObject类扩展了GameObject类，所以我们知道每渲染一帧都会调用一次update和draw函数。Bounce类中的所有代码只跟让图像反弹有关，也就是修改变量x和y。</p>
<p>好啦，我们已经创建了一批类，基于这些类也实现了与第一个示例相同的效果。而有了这个框架，再创建游戏就不必因为绘制画布等底层逻辑以及管理游戏对象等问题而重复编码了。</p>
<p>看看示例Demo吧。<a href="http://webdemos.sourceforge.net/jsplatformer3/jsplatformer3.html" target="_blank">http://webdemos.sourceforge.net/jsplatformer3/jsplatformer3.html</a></p>
<p>为之漫笔 最后编辑于：<br />
2011/08/15 @ 22:25</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/08/15/game-development-with-javascript-and-the-canvas-element-4-2639.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>使用JavaScript和Canvas开发游戏（三）</title>
		<link>http://www.cn-cuckoo.com/2011/08/14/game-development-with-javascript-and-the-canvas-element-3-2604.html</link>
		<comments>http://www.cn-cuckoo.com/2011/08/14/game-development-with-javascript-and-the-canvas-element-3-2604.html#comments</comments>
		<pubDate>Sun, 14 Aug 2011 00:16:38 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2604</guid>
		<description><![CDATA[原文作者：Matthew Casperson • 编辑：Michele McDonough 原文链接: Game Development with JavaScript and the Canvas element 1、认识一下Canvas 2、在Canvas上绘图 3、通过Canvas元素实现高级图像操作 4、写一个游戏框架（一） 5、写一个游戏框架（二） 6、通过Canvas实现视差滚动 7、动画 8、JavaScript键盘输入 9、综合运用 10、定义级别 11、跳跃与坠落 12、添加道具 13、加载资源 14、添加主菜单 4、写一个游戏框架（一） http://www.brighthub.com/internet/web-development/articles/40512.aspx 在知道了如何使用画布元素之后，接下来我教大家写一个框架，有了这个框架，我们就可以把它作为基础来创建游戏。在这第一部分，我们会介绍前两个文件/类。 编写代码之前，我们先来看一看随后几篇文章将致力于创建的示例Demo。表面上看起来，这个Demo跟第二篇文章里的那个没啥区别，但如果你看看后台（查看网页源代码）就会发现，为了更方便地创建这个最终效果，一个凝聚不少心血的基础框架已经写好了。 下面我们要介绍的JavaScript代码使用面向对象的方式来编写。对于没有编写过多少JavaScript代码的人来说，恐怕第一眼看到它们会觉得有点奇怪。如果你真的不太熟悉JavaScript的面向对象编程，建议通过Mozilla Developer Network的这个教程https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript来补补课。这篇教程里解释了我们稍后会用到的一些编程技术。 从设计思想上来看，这个框架可以分成两部分：与底层的2D引擎交互的类（用于操作画布、控制渲染循环、处理输入等的代码）和用来创建对象以便构成游戏的类。前者可以归为引擎类，后者可以归为应用类。由于应用类要构建于引擎类之上，所以我们需要先来创建引擎类。 Main.js 如果你研究了前面例子中的代码，就会发现Main.js文件中包含了不少代码。 /** 每秒多少帧 @type Number */ var FPS = 30; /** 两帧间间隔的秒数 @type Number */ var SECONDS_BETWEEN_FRAMES = [...]]]></description>
			<content:encoded><![CDATA[<p>原文作者：Matthew Casperson • 编辑：Michele McDonough<br />
原文链接: <a href="http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx" target="_blank">Game Development with JavaScript and the Canvas element</a></p>
<p>1、认识一下Canvas<br />
2、在Canvas上绘图<br />
3、通过Canvas元素实现高级图像操作<br />
4、写一个游戏框架（一）<br />
5、写一个游戏框架（二）<br />
6、通过Canvas实现视差滚动<br />
7、动画<br />
8、JavaScript键盘输入<br />
9、综合运用<br />
10、定义级别<br />
11、跳跃与坠落<br />
12、添加道具<br />
13、加载资源<br />
14、添加主菜单</p>
<h1>4、写一个游戏框架（一）</h1>
<p><a href="http://www.brighthub.com/internet/web-development/articles/40512.aspx" target="_blank">http://www.brighthub.com/internet/web-development/articles/40512.aspx</a><br />
在知道了如何使用画布元素之后，接下来我教大家写一个框架，有了这个框架，我们就可以把它作为基础来创建游戏。在这第一部分，我们会介绍前两个文件/类。</p>
<p>编写代码之前，我们先来看一看<a href="http://webdemos.sourceforge.net/jsplatformer3/jsplatformer3.html" target="_blank">随后几篇文章将致力于创建的示例Demo</a>。表面上看起来，这个Demo跟第二篇文章里的那个没啥区别，但如果你看看后台（查看网页源代码）就会发现，为了更方便地创建这个最终效果，一个凝聚不少心血的基础框架已经写好了。<br />
<img class="colorbox-2604"  src="/cache/img/2604_1.jpg" /></p>
<p>下面我们要介绍的JavaScript代码使用面向对象的方式来编写。对于没有编写过多少JavaScript代码的人来说，恐怕第一眼看到它们会觉得有点奇怪。如果你真的不太熟悉JavaScript的面向对象编程，建议通过Mozilla Developer Network的这个教程<a href="https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript" target="_blank">https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript</a>来补补课。这篇教程里解释了我们稍后会用到的一些编程技术。</p>
<p>从设计思想上来看，这个框架可以分成两部分：与底层的2D引擎交互的类（用于操作画布、控制渲染循环、处理输入等的代码）和用来创建对象以便构成游戏的类。前者可以归为引擎类，后者可以归为应用类。由于应用类要构建于引擎类之上，所以我们需要先来创建引擎类。<br />
<span id="more-2604"></span></p>
<h2>Main.js</h2>
<p>如果你研究了前面例子中的代码，就会发现Main.js文件中包含了不少代码。</p>
<pre class="brush: js; ">

/** 每秒多少帧
    @type Number
*/
var FPS = 30;
/** 两帧间间隔的秒数
    @type Number
*/
var SECONDS_BETWEEN_FRAMES = 1 / FPS;
/** GameObjectManager 实例的全局引用
    @type GameObjectManager
*/
var g_GameObjectManager = null;
/** 应用中用到的图像
    @type Image
*/
var g_image = new Image();
g_image.src = &quot;jsplatformer3-smiley.jpg&quot;;

// 将应用的入口设置为init函数
window.onload = init;

/**
    应用的入口
*/
function init()
{
    new GameObjectManager().startupGameObjectManager();
}
</pre>
<p>首先是定义全局变量的代码。然后，跟以前一样，当页面加载完毕后立即运行init函数。在init函数里，创建GameObjectManager类的实例。</p>
<p>这里在GameObjectManager类的实例上调用了startupGameObjectManager函数。这篇文章以及后面的几篇文章还将多次提到几个命名上具有startupClassName形式的函数。这些函数实际上充当了各自类的构造函数，这样做有两个原因。</p>
<p>首先，JavaScript不支持函数重载（至少不容易实现）。如果你想让一个类有多个构造函数，那么这就成了问题。而通过把构造工作分配给另一组函数（如startupClassName1、startupClassName2），就可以比较容易地定义构造类的不同方式了。</p>
<p>第二个原因（很大程度上也是个人的问题）是我经常会在构造函数中引用尚未定义的变量。这可能是我使用C++、Java和C#这些语言落下的毛病，在这些语言里，类变量在源代码中的位置对其在构造函数中的可见性没有影响。拿下面这个C#类为例：</p>
<pre class="brush: js; ">

class Test
{
	public void Test() {this.a = 5;}
	public int a;
}
</pre>
<p>这些代码是合乎语法的，可以正常工作。下面再看看JavaScript中一个相同的例子：</p>
<pre class="brush: js; ">

function Test()
{
	this.a = 5;
	var a;
}
</pre>
<p>这段代码的问题在于，局部变量a在我们把数值5赋给它的时候还不存在。只有运行到var a;这一行，变量a才存在。尽管这个例子有点故意编排的意味，但的确能够说明我所遇到的问题。通过把类的创建放到一个类似startupClassName这样的函数中完成，并且在构造函数中定义（但不初始化）局部变量，然后当我在这些构建函数中引用相应的局部变量时，就能够确保它们一定是存在的。</p>
<h2>GameObjectManager.js</h2>
<pre class="brush: js; ">

/**
    管理游戏中所有对象的管理器
    @class
*/
function GameObjectManager()
{
    /** 保存游戏中对象的数组
        @type Arary
    */
    this.gameObjects = new Array();
    /** 上一次帧被渲染的时间
        @type Date
    */
    this.lastFrame = new Date().getTime();
    /** x轴的全局滚动值
        @type Number
    */
    this.xScroll = 0;
    /** y轴的全局滚动值
        @type Number
    */
    this.yScroll = 0;
    /** 对ApplicationManager实例的引用
        @type ApplicationManager
    */
    this.applicationManager = null;
    /** 对画布元素的引用
        @type HTMLCanvasElement
    */
    this.canvas = null;
    /** 对画布元素2D上下文的引用
        @type CanvasRenderingContext2D
    */
    this.context2D = null;
    /** 对内存中用作后台缓冲区的画布的引用
        @type HTMLCanvasElement
    */
    this.backBuffer = null;
    /** 对后台缓冲画布的2D上下文的引用
        @type CanvasRenderingContext2D
    */
    this.backBufferContext2D = null;

    /**
        初始化这个对象
        @return A reference to the initialised object
    */
    this.startupGameObjectManager = function()
    {
        // 设置引用this对象的全局指针
        g_GameObjectManager = this;

        // 取得画布元素及其2D上下文的引用
        this.canvas = document.getElementById(&#039;canvas&#039;);
        this.context2D = this.canvas.getContext(&#039;2d&#039;);
        this.backBuffer = document.createElement(&#039;canvas&#039;);
        this.backBuffer.width = this.canvas.width;
        this.backBuffer.height = this.canvas.height;
        this.backBufferContext2D = this.backBuffer.getContext(&#039;2d&#039;);

        // 创建一个新的ApplicationManager
        this.applicationManager = new ApplicationManager().startupApplicationManager();

        // 使用setInterval来调用draw函数
        setInterval(function(){g_GameObjectManager.draw();}, SECONDS_BETWEEN_FRAMES);

        return this;
    }

    /**
        渲染循环
    */
    this.draw = function ()
    {
        // 计算从上一帧到现在的时间
        var thisFrame = new Date().getTime();
        var dt = (thisFrame - this.lastFrame)/1000;
        this.lastFrame = thisFrame;

        // 清理绘制上下文
        this.backBufferContext2D.clearRect(0, 0, this.backBuffer.width, this.backBuffer.height);
        this.context2D.clearRect(0, 0, this.canvas.width, this.canvas.height);

        // 首先更新所有游戏对象
        for (x in this.gameObjects)
        {
            if (this.gameObjects[x].update)
            {
                this.gameObjects[x].update(dt, this.backBufferContext2D, this.xScroll, this.yScroll);
            }
        }

        // 然后绘制所有游戏对象
        for (x in this.gameObjects)
        {
            if (this.gameObjects[x].draw)
            {
                this.gameObjects[x].draw(dt, this.backBufferContext2D, this.xScroll, this.yScroll);
            }
        }

        // 将后台缓冲复制到当前显示的画布
        this.context2D.drawImage(this.backBuffer, 0, 0);
    };

    /**
        向gameObjects集合中添加一个GameObject
        @param gameObject The object to add
    */
    this.addGameObject = function(gameObject)
    {
        this.gameObjects.push(gameObject);
        this.gameObjects.sort(function(a,b){return a.zOrder - b.zOrder;})
    };

    /**
        从gameObjects集合中删除一个GameObject
        @param gameObject The object to remove
    */
    this.removeGameObject = function(gameObject)
    {
        this.gameObjects.removeObject(gameObject);
    }
}
</pre>
<p>首先看一看GameObjectManager类。GameObjectManager是一个引擎类，用于管理画布的绘制操作，还负责分派GameObject类（下一篇文章里介绍）的事件。</p>
<p>GameObjectManager类的startupGameObjectManager函数的代码如下：</p>
<pre class="brush: js; ">

    /**
        初始化这个对象
        @return A reference to the initialised object
    */
    this.startupGameObjectManager = function()
    {
        // 设置引用this对象的全局指针
        g_GameObjectManager = this;

        // 取得画布元素及其2D上下文的引用
        this.canvas = document.getElementById(&#039;canvas&#039;);
        this.context2D = this.canvas.getContext(&#039;2d&#039;);
        this.backBuffer = document.createElement(&#039;canvas&#039;);
        this.backBuffer.width = this.canvas.width;
        this.backBuffer.height = this.canvas.height;
        this.backBufferContext2D = this.backBuffer.getContext(&#039;2d&#039;);

        // 创建一个新的ApplicationManager
        this.applicationManager = new ApplicationManager().startupApplicationManager();

        // 使用setInterval来调用draw函数
        setInterval(function(){g_GameObjectManager.draw();}, SECONDS_BETWEEN_FRAMES);

        return this;
    }
</pre>
<p>前面已经说过，我们会把每个类的初始化工作放在startupClassName函数中来做。因此，GameObjectManager类将由startupGameObjectManager函数进行初始化。</p>
<p>而引用这个GameObjectManager实例的全局变量g_GameObjectManager经过重新赋值，指向了这个新实例。</p>
<pre class="brush: js; ">

        // 设置引用this对象的全局指针
        g_GameObjectManager = this;
</pre>
<p>对画布元素及其绘图上下文的引用也同样保存起来：</p>
<pre class="brush: js; ">

        // 取得画布元素及其2D上下文的引用
        this.canvas = document.getElementById(&#039;canvas&#039;);
        this.context2D = this.canvas.getContext(&#039;2d&#039;);
</pre>
<p>在前面的例子中，所有绘图操作都是直接在画布元素上完成的。这种风格的渲染一般称为单缓冲渲染。在此，我们要使用一种叫做双缓冲渲染的技术：任意游戏对象的所有绘制操作，都将在一个内存中的附加画布元素（后台缓冲）上完成，完成后再通过一次操作把它复制到网页上的画布元素（前台缓冲）。</p>
<p>双缓冲技术（<a href="http://www.brighthub.com/internet/web-development/articles/11012.aspx" target="_blank">http://www.brighthub.com/internet/web-development/articles/11012.aspx</a>）通常用于减少画面抖动。我自己在测试的时候从没发现直接向画布元素上绘制有抖动现象，但我在网上的确听别人念叨过，使用单缓冲渲染会导致某些浏览器在渲染时发生抖动。</p>
<p>不管怎么说，双缓冲还是能够避免最终用户看到每个游戏对象在绘制过程中最后一帧的组合过程。在通过JavaScript执行某些复杂绘制操作时（例如透明度、反锯齿及可编程纹理），这种情况是完全可能发生的。</p>
<p>使用附加缓冲技术占用的内存非常少，多执行一次图像复制操作（把后台缓冲绘制到前台缓冲）导致的性能损失也可以忽略不计，可以说实现双缓冲系统没有什么缺点。</p>
<p>如果将在HTML页面中定义的画布元素作为前台缓冲，那就需要再创建一个画布来充当后台缓冲。为此，我们使用了document.createElement函数在内存里创建了一个画布元素，把它用作后台缓冲。</p>
<pre class="brush: js; ">

        this.backBuffer = document.createElement(&#039;canvas&#039;);
        this.backBuffer.width = this.canvas.width;
        this.backBuffer.height = this.canvas.height;
        this.backBufferContext2D = this.backBuffer.getContext(&#039;2d&#039;);
</pre>
<p>接下来，我们创建了ApplicationManager类的一个新实例，并调用startupApplicationManager来初始化它。这个ApplicationManager类将在下一篇文章中介绍。</p>
<pre class="brush: js; ">

        // 创建一个新的ApplicationManager
        this.applicationManager = new ApplicationManager().startupApplicationManager();
</pre>
<p>最后，使用setInterval函数重复调用draw函数，这个函数是渲染循环的核心所在。</p>
<pre class="brush: js; ">

        // 使用setInterval来调用draw函数
        setInterval(function(){g_GameObjectManager.draw();}, SECONDS_BETWEEN_FRAMES);
</pre>
<p>下面来看一看draw函数。</p>
<pre class="brush: js; ">

    /**
        渲染循环
    */
    this.draw = function ()
    {
        // 计算从上一帧到现在的时间
        var thisFrame = new Date().getTime();
        var dt = (thisFrame - this.lastFrame)/1000;
        this.lastFrame = thisFrame;

        // 清理绘制上下文
        this.backBufferContext2D.clearRect(0, 0, this.backBuffer.width, this.backBuffer.height);
        this.context2D.clearRect(0, 0, this.canvas.width, this.canvas.height);

        // 首先更新所有游戏对象
        for (x in this.gameObjects)
        {
            if (this.gameObjects[x].update)
            {
                this.gameObjects[x].update(dt, this.backBufferContext2D, this.xScroll, this.yScroll);
            }
        }

        // 然后绘制所有游戏对象
        for (x in this.gameObjects)
        {
            if (this.gameObjects[x].draw)
            {
                this.gameObjects[x].draw(dt, this.backBufferContext2D, this.xScroll, this.yScroll);
            }
        }

        // 将后台缓冲复制到当前显示的画布
        this.context2D.drawImage(this.backBuffer, 0, 0);
    };
</pre>
<p>这个draw函数就是所有渲染循环的核心。在前面的例子中，渲染循环的函数会直接修改要绘制到屏幕上的对象（笑脸）。如果只需绘制一个对象，这样做没有问题。但是，一个游戏要由几十个单独的对象组成，所以这个draw函数并没有直接在渲染循环中直接处理要绘制的对象，而是维护了一个保存着这些对象的数组，让这些对象自己来更新和绘制自己。</p>
<p>首先，计算自上一帧渲染所经过的时间。即便我们在代码里写了每秒钟调用30次draw函数，但谁也无法保证事实如此。通过计算自上一帧渲染所经过的时间，可以做到尽可能让游戏的执行与帧速率无关。</p>
<pre class="brush: js; ">

        // 计算从上一帧到现在的时间
        var thisFrame = new Date().getTime();
        var dt = (thisFrame - this.lastFrame)/1000;
        this.lastFrame = thisFrame;
</pre>
<p>接着清理绘制上下文。</p>
<pre class="brush: js; ">

        // 清理绘制上下文
        this.backBufferContext2D.clearRect(0, 0, this.backBuffer.width, this.backBuffer.height);
        this.context2D.clearRect(0, 0, this.canvas.width, this.canvas.height);
</pre>
<p>然后，就是调用游戏对象（这些对象是由GameObject类定义的，下一篇文章将介绍该类）自己的更新（update）和绘制（draw）方法。注意，这两个方法是可选的（这也是我们在调用它们之前先检查它们是否存在的原因），但差不多每一个对象都需要更新和绘制自已。</p>
<pre class="brush: js; ">

        // 首先更新所有游戏对象
        for (x in this.gameObjects)
        {
            if (this.gameObjects[x].update)
            {
                this.gameObjects[x].update(dt, this.backBufferContext2D, this.xScroll, this.yScroll);
            }
        }

        // 然后绘制所有游戏对象
        for (x in this.gameObjects)
        {
            if (this.gameObjects[x].draw)
            {
                this.gameObjects[x].draw(dt, this.backBufferContext2D, this.xScroll, this.yScroll);
            }
        }
</pre>
<p>最后，把后台缓冲复制到前台缓冲，最终用户就可以看到下一帧了。</p>
<pre class="brush: js; ">

        // 将后台缓冲复制到当前显示的画布
        this.context2D.drawImage(this.backBuffer, 0, 0);
</pre>
<p>理解了draw函数，下面再分别讲一讲addGameObject和removeGameObject函数。</p>
<pre class="brush: js; ">

    /**
        向gameObjects集合中添加一个GameObject
        @param gameObject The object to add
    */
    this.addGameObject = function(gameObject)
    {
        this.gameObjects.push(gameObject);
        this.gameObjects.sort(function(a,b){return a.zOrder - b.zOrder;})
    };

    /**
        从gameObjects集合中删除一个GameObject
        @param gameObject The object to remove
    */
    this.removeGameObject = function(gameObject)
    {
        this.gameObjects.removeObject(gameObject);
    }
</pre>
<p>利用addGameObject和removeGameObject（在Utils.js文件里通过扩展Array.prototype添加）函数，可以在GameObjectManager所维护的GameObject集合（即gameObjects变量）中添加和删除游戏对象。</p>
<p>GameObjectManager类是我们这个游戏框架中最复杂的一个类。在下一篇文章中，我们会讲解游戏框架的另外几个类：GameObject、VisualGameObject、Bounce和ApplicationManager。</p>
<p>好了，现在放松一下，<a href="http://webdemos.sourceforge.net/jsplatformer3/jsplatformer3.html" target="_blank">看一看Demo吧</a>。</p>
<p>为之漫笔 最后编辑于：<br />
2011/08/14 @ 23:19</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/08/14/game-development-with-javascript-and-the-canvas-element-3-2604.html/feed</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>使用JavaScript和Canvas开发游戏（二）</title>
		<link>http://www.cn-cuckoo.com/2011/08/11/game-development-with-javascript-and-the-canvas-element-2-2585.html</link>
		<comments>http://www.cn-cuckoo.com/2011/08/11/game-development-with-javascript-and-the-canvas-element-2-2585.html#comments</comments>
		<pubDate>Thu, 11 Aug 2011 11:41:56 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2585</guid>
		<description><![CDATA[原文作者：Matthew Casperson • 编辑：Michele McDonough 原文链接: Game Development with JavaScript and the Canvas element 1、认识一下Canvas 2、在Canvas上绘图 3、通过Canvas元素实现高级图像操作 4、通过Canvas实现视差滚动 5、写一个游戏框架（一） 6、写一个游戏框架（二） 7、动画 8、JavaScript键盘输入 9、综合运用 10、定义级别 11、跳跃与坠落 12、添加道具 13、加载资源 14、添加主菜单 3、通过Canvas元素实现高级图像操作 http://www.brighthub.com/internet/web-development/articles/39509.aspx 这篇文章将带领大家学习使用JavaScript和Canvas元素操作图像了几种不同的方式，这些方式在Canvas元素出现之前是不可能的事儿。 上一篇文章演示了如何利用Canvas实现一个基本的图像动画。那个例子很简单，同样的效果通过修改IMG或DIV等标准HTML元素的一些属性，照样也可以轻易实现。下面我们就来演示一下画布元素的高级应用，展示一下它的真正威力。 首先，还是准备一个HTML页面。 &#60;!DOCTYPE HTML PUBLIC &#34;-//W3C//DTD HTML 4.01//EN&#34;&#62; &#60;html lang=&#34;en&#34;&#62; &#60;head&#62; &#60;title&#62;JavaScript Platformer 2&#60;/title&#62; &#60;script type=&#34;text/javascript&#34; src=&#34;jsplatformer2.js&#34;&#62;&#60;/script&#62; &#60;style type=&#34;text/css&#34;&#62; body { font-family: Arial,Helvetica,sans-serif;} &#60;/style&#62; [...]]]></description>
			<content:encoded><![CDATA[<p>原文作者：Matthew Casperson • 编辑：Michele McDonough<br />
原文链接: <a href="http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx" target="_blank">Game Development with JavaScript and the Canvas element</a></p>
<p>1、认识一下Canvas<br />
2、在Canvas上绘图<br />
3、通过Canvas元素实现高级图像操作<br />
4、通过Canvas实现视差滚动<br />
5、写一个游戏框架（一）<br />
6、写一个游戏框架（二）<br />
7、动画<br />
8、JavaScript键盘输入<br />
9、综合运用<br />
10、定义级别<br />
11、跳跃与坠落<br />
12、添加道具<br />
13、加载资源<br />
14、添加主菜单</p>
<h1>3、通过Canvas元素实现高级图像操作</h1>
<p><a href="http://www.brighthub.com/internet/web-development/articles/39509.aspx" target="_blank">http://www.brighthub.com/internet/web-development/articles/39509.aspx</a></p>
<p>这篇文章将带领大家学习使用JavaScript和Canvas元素操作图像了几种不同的方式，这些方式在Canvas元素出现之前是不可能的事儿。</p>
<p>上一篇文章演示了如何利用Canvas实现一个基本的图像动画。那个例子很简单，同样的效果通过修改IMG或DIV等标准HTML元素的一些属性，照样也可以轻易实现。下面我们就来演示一下画布元素的高级应用，展示一下它的真正威力。</p>
<p>首先，还是准备一个HTML页面。</p>
<pre class="brush: js; ">

  &lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot;&gt;
  &lt;html lang=&quot;en&quot;&gt;
     &lt;head&gt;
        &lt;title&gt;JavaScript Platformer 2&lt;/title&gt;
        &lt;script type=&quot;text/javascript&quot; src=&quot;jsplatformer2.js&quot;&gt;&lt;/script&gt;
        &lt;style type=&quot;text/css&quot;&gt;
           body { font-family: Arial,Helvetica,sans-serif;}
        &lt;/style&gt;
     &lt;/head&gt;
    &lt;body&gt;
       &lt;p&gt;
          &lt;a href=&quot;http://www.brighthub.com/internet/web-development/articles/38364.aspx&quot;&gt;
             Game Development with Javascript and the canvas element
          &lt;/a&gt;
       &lt;/p&gt;
       &lt;canvas id=&quot;canvas&quot; width=&quot;600&quot; height=&quot;400&quot;&gt;
          &lt;p&gt;Your browser does not support the canvas element.&lt;/p&gt;
       &lt;/canvas&gt;
       &lt;br /&gt;
       &lt;button onclick=&quot;currentFunction=alpha;&quot;&gt;Change Alpha&lt;/button&gt;
       &lt;button onclick=&quot;currentFunction=shear;&quot;&gt;Shear&lt;/button&gt;
       &lt;button onclick=&quot;currentFunction=scale;&quot;&gt;Scale&lt;/button&gt;
       &lt;button onclick=&quot;currentFunction=rotate;&quot;&gt;Rotate&lt;/button&gt;
    &lt;/body&gt;
 &lt;/html&gt;
</pre>
<p>与上个一例子的HTML页面相比，唯一的区别就是添加了一些按钮。单击这些按钮，就会设置currentFunction变量（稍后介绍）的值，用以改变在渲染循环中运行的函数。<br />
<span id="more-2585"></span><br />
以下是 jsplatformer2.js 的代码。</p>
<pre class="brush: js; ">

 // 每秒多少帧
 const FPS = 30;
 const SECONDSBETWEENFRAMES = 1 / FPS;
 const HALFIMAGEDIMENSION = 75;
 const HALFCANVASWIDTH = 300;
 const HALFCANVASHEIGHT = 200;
 var image = new Image();
 image.src = &quot;jsplatformer2-smiley.jpg&quot;; //还是第一个例子中的图像
 var canvas = null;
 var context2D = null;
 var currentFunction = null;
 var currentTime = 0;
 var sineWave = 0;

 window.onload = init;

 function init()
 {
    canvas = document.getElementById(&#039;canvas&#039;);
    context2D = canvas.getContext(&#039;2d&#039;);
    setInterval(draw, SECONDSBETWEENFRAMES * 1000);
    currentFunction = scale;
 }

 function draw()
 {
     currentTime += SECONDSBETWEENFRAMES;
     sineWave = (Math.sin(currentTime) + 1) / 2;

     context2D.clearRect(0, 0, canvas.width, canvas.height);

     context2D.save();

     context2D.translate(HALFCANVASWIDTH - HALFIMAGEDIMENSION, HALFCANVASHEIGHT - HALFIMAGEDIMENSION);

     currentFunction();

     context2D.drawImage(image, 0, 0);

     context2D.restore();
 }

 function alpha()
 {
     context2D.globalAlpha = sineWave;
 }

 function shear()
 {
     context2D.transform(1, 0, (sineWave - 0.5), 1, 0, 0);
 }

 function scale()
 {
     context2D.translate(HALFIMAGEDIMENSION * (1 - sineWave), HALFIMAGEDIMENSION * (1 - sineWave));
     context2D.scale(sineWave, sineWave);
 }

 function rotate()
 {
     context2D.translate(HALFIMAGEDIMENSION, HALFIMAGEDIMENSION);
     context2D.rotate(sineWave * Math.PI * 2);
     context2D.translate(-HALFIMAGEDIMENSION, -HALFIMAGEDIMENSION);
 }
</pre>
<p>跟前面一样，这个JavaScript文件先定义了一些全局变量。</p>
<ul>
<li>
FPS：每秒多少帧
</li>
<li>
SECONDSBETWEENFRAMES：两帧之间间隔的秒数（FPS的倒数）
</li>
<li>
HALFIMAGEDIMENSION：要绘制图像的宽度/高度的一半，用于把图像定位到画布的中心点
</li>
<li>
HALFCANVASWIDTH：画布宽度的一半，用于配合HALFIMAGEDIMENSION使用，以便在画布上居中图像
</li>
<li>
HALFCANVASHEIGHT：画布高度的一半，用于配合HALFIMAGEDIMENSION使用，以便在画布上居中图像
</li>
<li>
currentFunction：渲染循环（参见上一篇文章）中运行的函数
</li>
<li>
currentTime：应用已经运行了多少秒
</li>
<li>
sineWave：0到1之间的一个值，用于控制图像的运动
</li>
<li>
image：要在画布上绘制的图像
</li>
<li>
canvas：画布元素的引用
</li>
<li>
context2D：画布元素的2D上下文的引用
</li>
</ul>
<p>然后，跟前面一样，要设置在window的onload事件发生时立即调用init函数（关于init函数的介绍，请参见上一篇文章）。</p>
<h2>draw函数</h2>
<p>下面来看一看draw函数：</p>
<pre class="brush: js; ">

function draw()
{
    currentTime += SECONDSBETWEENFRAMES;
    sineWave = (Math.sin(currentTime) + 1) / 2;

    context2D.clearRect(0, 0, canvas.width, canvas.height);

    context2D.save();
    context2D.translate(HALFCANVASWIDTH - HALFIMAGEDIMENSION, HALFCANVASHEIGHT - HALFIMAGEDIMENSION);
    currentFunction();
    context2D.drawImage(image, 0, 0);
    context2D.restore();
}
</pre>
<p>这个例子要演示4种效果：修改alpha值（透明度），以及缩放、旋转和切变图像。为了展示这些效果，需要基于某一范围内的值来应用变化。变量sineWave就用来定义这个范围值的基准。</p>
<p>标准的正弦函数能够在-1到1之间产生非常完美的波形图。首先，我们通过递增currentTime变量来反映动画已经运行了多长时间，然后再利用这个值在正弦曲线上找到一个点。给正弦函数返回的值（从-1到1）先加1再除以2，就可以把它们转换成0到1这个范围内的值。</p>
<pre class="brush: js; ">

    currentTime += SECONDSBETWEENFRAMES;
    sineWave = (Math.sin(currentTime) + 1) / 2;
</pre>
<p>然后，调用clearRect方法清空画布，以便为后面的绘图准备一个干净的版面。</p>
<pre class="brush: js; ">

    context2D.clearRect(0, 0, canvas.width, canvas.height);
</pre>
<p>应用到画布上面的效果是可以累积的，因而就可以利用几个简单的函数来“组合”出效果来。例如，在向屏幕上绘制之前，可能会有一艘飞船需要旋转、变换和缩放。因为所有效果都对画布起作用，所以这些效果会应用到将被绘制在屏幕上的所有对象，而不仅仅是某一幅图像或某一个形状（比如一艘飞船）。</p>
<p>其中，save和restore函数为应用这些累积的效果提供了一种简单的机制，可以将应用了这些效果的图像或图形绘制到画布上，然后“撤销”这些改变。后台的操作是什么呢？save函数把当前的绘制状态推进栈里，而restore函数则把最后一个状态弹出栈。还拿前面提到的飞船为例，需要执行下列操作：</p>
<ul>
<li>调用save函数（保存当前的绘制状态）</li>
<li>旋转、变换和缩放上下文</li>
<li>绘制飞船</li>
<li>调用restore函数，移除自上一次调用save方法以来所添加的任何效果，也就是撤销之前的变化</li>
</ul>
<p>在这里，我们就是要组合起来使用这两个方法。首先，在把任何效果应用到画布之前，先保存绘制状态。</p>
<pre class="brush: js; ">

    context2D.save();
</pre>
<p>保存了绘制状态之后，就该应用目标效果了。为此，首先调用translate函数，从而将随后要绘制的图像在画布上居中。</p>
<pre class="brush: js; ">

    context2D.translate(HALFCANVASWIDTH - HALFIMAGEDIMENSION, HALFCANVASHEIGHT - HALFIMAGEDIMENSION);
</pre>
<p>接下来，调用由变量currentFunction引用的函数。正是这些被引用的函数，是让图像发生alpha（透明度）变化以及缩放、旋转和切变的关键。这些函数我们稍后再介绍。</p>
<pre class="brush: js; ">

    currentFunction();
</pre>
<p>为图像应用完效果之后，就可以把它绘制到画布上面了。所以，接下来就是调用drawImage来绘图。</p>
<pre class="brush: js; ">

    context2D.drawImage(image, 0, 0);
</pre>
<p>最后，再调用restore函数，把自调用save函数以来应用的所有效果从画布上移除。</p>
<pre class="brush: js; ">

    context2D.restore();
</pre>
<h2>alpha函数</h2>
<pre class="brush: js; ">

 function alpha()
 {
     context2D.globalAlpha = sineWave;
 }
</pre>
<p>通过修改上下文对象的globalAlpha属性，所有后续绘制操作的透明度都会被修改。将globalAlpha设置为0，意味着被绘制的任何对象都将完全透明，而将这个属性设置为1，则意味着任何绘制操作都会保持原有的透明度级别。在此，我们通过修改这个globalAlpha属性，可以实现笑脸的淡入和淡出效果。</p>
<h2>shear函数</h2>
<pre class="brush: js; ">

 function shear()
 {
     context2D.transform(1, 0, (sineWave - 0.5), 1, 0, 0);
 }
</pre>
<p>切变操作是通过transform函数向画布应用一个矩阵来实现的。变换矩阵本身就是一个值得研究的主题，但对我们来说，如果不想理解背后的数学原理，可以在网上找到很多标准的2D变换矩阵（<a href="http://en.wikipedia.org/wiki/Transformation_matrix#Examples_in_2D_graphics" target="_blank">http://en.wikipedia.org/wiki/Transformation_matrix#Examples_in_2D_graphics</a>），直接使用transform函数来应用它们即可。所谓切变，其实就是把图像的顶部或底部推到一边。</p>
<h2>scale函数</h2>
<pre class="brush: js; ">

 function scale()
 {
     context2D.translate(HALFIMAGEDIMENSION * (1 - sineWave), HALFIMAGEDIMENSION * (1 - sineWave));
　   context2D.scale(sineWave, sineWave);
 }
</pre>
<p>顾名思义，scale（缩放）函数修改的是图像的大小。但在此之前，我们还调用了一次transalte函数。这是为了让缩放后的图像在画布上居中。如果你把这行代码注释掉，就会发现图像会从左上角向右下角膨胀。调用translate函数就是为抵消其圆心的位移，让图像始终居中。</p>
<h2>rotate函数</h2>
<pre class="brush: js; ">

 function rotate()
 {
     context2D.translate(HALFIMAGEDIMENSION, HALFIMAGEDIMENSION);
     context2D.rotate(sineWave * Math.PI * 2);
     context2D.translate(-HALFIMAGEDIMENSION, -HALFIMAGEDIMENSION);
 }
</pre>
<p>与scale函数类似，rotate（旋转）函数的作用也正如其名：旋转图像。与scale函数同样类似的是，这里也额外调用了translate函数以确保图像围绕中心点而不是左上角旋转。建议大家把对translate函数的调用注释掉，自己看一看结果有什么不同。</p>
<p>刚刚我们看到了使用画布元素实现的4种也还算简单的效果，这些效果使用标准的HTML元素几乎是不可能做到的。其中，有的效果可以使用scale和rotate等内置函数来实现，而使用transform函数则可以完成大量的图像操作（切变只是其中之一）。</p>
<p>看看Demo吧。<a href="http://webdemos.sourceforge.net/jsplatformer2/jsplatformer2.html" target="_blank">http://webdemos.sourceforge.net/jsplatformer2/jsplatformer2.html</a></p>
<p>为之漫笔 最后编辑于：<br />
2011/08/12 @ 06:29</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/08/11/game-development-with-javascript-and-the-canvas-element-2-2585.html/feed</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>使用JavaScript和Canvas开发游戏（一）</title>
		<link>http://www.cn-cuckoo.com/2011/08/10/game-development-with-javascript-and-the-canvas-element-2554.html</link>
		<comments>http://www.cn-cuckoo.com/2011/08/10/game-development-with-javascript-and-the-canvas-element-2554.html#comments</comments>
		<pubDate>Wed, 10 Aug 2011 12:26:29 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2554</guid>
		<description><![CDATA[原文作者：Matthew Casperson • 编辑：Michele McDonough 原文链接: Game Development with JavaScript and the Canvas element 1、认识一下Canvas 2、在Canvas上绘图 3、通过Canvas元素实现高级图像操作 4、通过Canvas实现视差滚动 5、写一个游戏框架（一） 6、写一个游戏框架（二） 7、动画 8、JavaScript键盘输入 9、综合运用 10、定义级别 11、跳跃与坠落 12、添加道具 13、加载资源 14、添加主菜单 1、认识一下Canvas http://www.brighthub.com/internet/web-development/articles/38364.aspx Canvas元素以及JavaScript引擎中新增的一些特性，让Web开发人员不必借助第三方插件，即可设计开发出精细且具有交互性的2D网页。这篇文章就向大家介绍一下Canvas元素，以及它的一些可能的用途。 JavaScript与Canvas元素 HTML是为创建静态页面而生的。HTML所能实现的动态效果，也仅限于显示GIF动画和闪烁的文本。JavaScript改变了这一切，通过它能够动态修改网页。今天，很多Web服务都利用AJAX来创建网页，为用户提供更加流畅的体验，也超越了标准HTML页面中常见的“点击－重新加载－点击”式的交互模式。 然而，JavaScript的某些功能会受到其宿主浏览器的制约。尽管可以在网页中创建和修改任何元素，但JavaScript不能（轻易地）让浏览器显示一种新对象。通过JavaScript修改文本、插入图像或者缩放表格都很容易，因为这些对象本来就是HTML所支持的。如果你想再玩得刺激一点，比如写一个网页游戏，怎么办？那恐怕就得苦心积虑地改变标准HTML元素的用途，克服种种不测才能达到目的。要么，你就得求助于Flash或Silverlight这样的插件。 Canvas元素登场了。这个新HTML元素为JavaScript开发者提供了一种无需插件即可在网页中直接绘图的机制。Canvas元素最早是由苹果公司在其WebKit框架中引入的，Safari浏览器和Dashboard微件都在使用。Canvas元素现在也被建议加入了HTML5规范，得到了最新的Chrome、Firefox、Opera以及Konqueror等浏览器的支持。Internet Explorer（至少在IE8之前）还不支持Canvas，但ExplorerCanvas项目倒是为IE提供了与Canvas元素类似的功能。 Canvas元素对做过2D图形编程的人是小菜一碟。可以在这个元素上画线、画各种形状、画渐变，甚至可以利用与其他2D API中类似的函数来修改其中的每一个像素。得益于Chrome的V8、Firefox的SpiderMonkey以及Safari的Nitro等最新JavaScript引擎的性能，创建精细且具有交互性的Web应用已经完全没有问题。 我们这一系列文章将教会大家使用JavaScript和Canvas元素创建一个简单的平台游戏。将要涉及的内容包括动画、加载资源、分层渲染、滚动和交互。通过一步一步地展示示例代码和实际效果，你可以很快学会如何驾驭强大的Canvas元素。 2、在Canvas上绘图 http://www.brighthub.com/internet/web-development/articles/38744.aspx 下面，我们就通过一个循序渐进的示例及实时演示，来介绍如何使用JavaScript在Canvas元素上绘图及实现动画。 准备工作 知道了什么是Canvas元素之后，该学习在屏幕上绘图了。首先，需要一个HTML页面来放置和显示Canvas元素。 &#60;!DOCTYPE HTML PUBLIC &#34;-//W3C//DTD HTML 4.01//EN&#34;&#62; &#60;html lang=&#34;en&#34;&#62; &#60;head&#62; &#60;title&#62;JavaScript Platformer [...]]]></description>
			<content:encoded><![CDATA[<p>原文作者：Matthew Casperson • 编辑：Michele McDonough<br />
原文链接: <a href="http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx" target="_blank">Game Development with JavaScript and the Canvas element</a></p>
<p>1、认识一下Canvas<br />
2、在Canvas上绘图<br />
3、通过Canvas元素实现高级图像操作<br />
4、通过Canvas实现视差滚动<br />
5、写一个游戏框架（一）<br />
6、写一个游戏框架（二）<br />
7、动画<br />
8、JavaScript键盘输入<br />
9、综合运用<br />
10、定义级别<br />
11、跳跃与坠落<br />
12、添加道具<br />
13、加载资源<br />
14、添加主菜单</p>
<h1>1、认识一下Canvas</h1>
<p><a href="http://www.brighthub.com/internet/web-development/articles/38364.aspx" target="_blank">http://www.brighthub.com/internet/web-development/articles/38364.aspx</a></p>
<p>Canvas元素以及JavaScript引擎中新增的一些特性，让Web开发人员不必借助第三方插件，即可设计开发出精细且具有交互性的2D网页。这篇文章就向大家介绍一下Canvas元素，以及它的一些可能的用途。</p>
<h2>JavaScript与Canvas元素</h2>
<p>HTML是为创建静态页面而生的。HTML所能实现的动态效果，也仅限于显示GIF动画和闪烁的文本。JavaScript改变了这一切，通过它能够动态修改网页。今天，很多Web服务都利用AJAX来创建网页，为用户提供更加流畅的体验，也超越了标准HTML页面中常见的“点击－重新加载－点击”式的交互模式。</p>
<p>然而，JavaScript的某些功能会受到其宿主浏览器的制约。尽管可以在网页中创建和修改任何元素，但JavaScript不能（轻易地）让浏览器显示一种新对象。通过JavaScript修改文本、插入图像或者缩放表格都很容易，因为这些对象本来就是HTML所支持的。如果你想再玩得刺激一点，比如写一个网页游戏，怎么办？那恐怕就得苦心积虑地改变标准HTML元素的用途，克服种种不测才能达到目的。要么，你就得求助于Flash或Silverlight这样的插件。</p>
<p>Canvas元素登场了。这个新HTML元素为JavaScript开发者提供了一种无需插件即可在网页中直接绘图的机制。Canvas元素最早是由苹果公司在其WebKit框架中引入的，Safari浏览器和Dashboard微件都在使用。Canvas元素现在也被建议加入了HTML5规范，得到了最新的Chrome、Firefox、Opera以及Konqueror等浏览器的支持。Internet Explorer（至少在IE8之前）还不支持Canvas，但ExplorerCanvas项目倒是为IE提供了与Canvas元素类似的功能。</p>
<p>Canvas元素对做过2D图形编程的人是小菜一碟。可以在这个元素上画线、画各种形状、画渐变，甚至可以利用与其他2D API中类似的函数来修改其中的每一个像素。得益于Chrome的V8、Firefox的SpiderMonkey以及Safari的Nitro等最新JavaScript引擎的性能，创建精细且具有交互性的Web应用已经完全没有问题。</p>
<p>我们这一系列文章将教会大家使用JavaScript和Canvas元素创建一个简单的平台游戏。将要涉及的内容包括动画、加载资源、分层渲染、滚动和交互。通过一步一步地展示示例代码和实际效果，你可以很快学会如何驾驭强大的Canvas元素。</p>
<h1>2、在Canvas上绘图</h1>
<p><a href="http://www.brighthub.com/internet/web-development/articles/38744.aspx" target="_blank">http://www.brighthub.com/internet/web-development/articles/38744.aspx</a></p>
<p>下面，我们就通过一个循序渐进的示例及实时演示，来介绍如何使用JavaScript在Canvas元素上绘图及实现动画。</p>
<h2>准备工作</h2>
<p>知道了什么是Canvas元素之后，该学习在屏幕上绘图了。首先，需要一个HTML页面来放置和显示Canvas元素。<br />
<span id="more-2554"></span></p>
<pre class="brush: html; ">

  &lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot;&gt;
  &lt;html lang=&quot;en&quot;&gt;
     &lt;head&gt;
        &lt;title&gt;JavaScript Platformer 1&lt;/title&gt;
        &lt;script type=&quot;text/javascript&quot; src=&quot;jsplatformer1.js&quot;&gt;&lt;/script&gt;
        &lt;style type=&quot;text/css&quot;&gt;
           body { font-family: Arial,Helvetica,sans-serif;}
        &lt;/style&gt;
     &lt;/head&gt;
    &lt;body&gt;
       &lt;p&gt;
          &lt;a href=&quot;http://www.brighthub.com/internet/web-development/articles/38364.aspx&quot;&gt;
             Game Development with Javascript and the canvas element
          &lt;/a&gt;
       &lt;/p&gt;
       &lt;canvas id=&quot;canvas&quot; width=&quot;600&quot; height=&quot;400&quot;&gt;
          &lt;p&gt;Your browser does not support the canvas element.&lt;/p&gt;
       &lt;/canvas&gt;
    &lt;/body&gt;
 &lt;/html&gt;
</pre>
<p>这些HTML代码很直观。其中有两个重要的元素。</p>
<pre class="brush: js; ">

&lt;script type=&quot;text/javascript&quot; src=&quot;jsplatformer1.js&quot;&gt;&lt;/script&gt;
</pre>
<p>这里包含的是将会修改Canvas元素的JavaScript代码，对应的Canvas元素的标记如下：</p>
<pre class="brush: js; ">

&lt;canvas id=&quot;canvas&quot; width=&quot;600&quot; height=&quot;400&quot;&gt;
	&lt;p&gt;Your browser does not support the canvas element.&lt;/p&gt;
&lt;/canvas&gt;
</pre>
<p>以上代码创建了一个Canvas元素。不支持Canvas的浏览器，比如Internet Explorer（IE8之前的版本），会忽略这个元素，而只显示其子元素。在这个简单的例子中，这个子元素就是一个段落，其中的文本告诉用户他们的浏览器不支持Canvas元素。而对于那些支持Canvas元素的浏览器，如Chrome、Opera和Firefox，则会忽略Canvas元素的子元素。</p>
<p>这个Canvas元素的ID属性很重要，因为后面的JavaScript将通过它来取得对该元素的引用。而width和height属性指定了画布的宽度和高度，这两个属性跟table或img等其他HTML元素中的同名属性作用一样。</p>
<p>以下是 jsplatformer1.js的代码：</p>
<pre class="brush: js; ">

//每秒钟target帧
const FPS = 30;
var x = 0;
var y = 0;
var xDirection = 1;
var yDirection = 1;
var image = new Image();
//建议读者将图片下载到本地加载（经测试，此图片响应头部的Content-Type为application/empty，浏览器无法识别）
image.src = &quot;http://javascript-tutorials.googlecode.com/files/jsplatformer1-smiley.jpg&quot;;
var canvas = null;
var context2D = null;

window.onload = init;
function init(){
	canvas = document.getElementById(&#039;canvas&#039;);
	context2D = canvas.getContext(&#039;2d&#039;);
	setInterval(draw, 1000/FPS);
}
function draw(){
	context2D.clearRect(0, 0, canvas.width, canvas.height);
	context2D.drawImage(image, x, y);
	x += 1* xDirection;
	y += 1* yDirection;

	if (x &gt;= 450) {
		x = 450;
		xDirection = -1;
	}else if(x &lt;= 0){
		x = 0;
		xDirection = 1;
	}
	if (y &gt;= 250) {
		y = 250;
		yDirection = -1;
	}else if(y &lt;= 0){
		y = 0;
		yDirection = 1;
	}
}
</pre>
<p>如果只是一个Canvas元素，也没有什么用。JavaScript必须要在这块画布上面画点什么，相应的代码保存在 jsplatformer1.js中。简单来说，JavaScript在这里先加载了一幅图像，然后将其画在画布上面，最后让它在画布上移动。</p>
<p>首先，定义一些全局变量。</p>
<pre class="brush: js; ">

const FPS = 30;
</pre>
<p>FPS定义的是画布重绘的频率。</p>
<pre class="brush: js; ">

var x = 0;
var y = 0;
var xDirection = 1;
var yDirection = 1;
</pre>
<p>变量x、y、xDirection和yDirection用于定义图像（相对于画布左上角）的位置，以及它在任意一时刻移动的方向。</p>
<pre class="brush: js; ">

var image = new Image();
image.src = &quot;http://javascript-tutorials.googlecode.com/files/jsplatformer1-smiley.jpg&quot;;
</pre>
<p>要把图像画到画布上，必须先加载一幅图像。为此，我们创建一个Image对象，将其src属性设置为一幅图像文件的URL（建议把图片下载到本地。——译者注）。</p>
<pre class="brush: js; ">

var canvas = null;
var context2D = null;
</pre>
<p>我们还需要取得对Canvas元素以及绘图上下文（稍后再详细介绍绘图上下文）的引用。稍后我们会把正确的值赋给这两个变量，现在先把它们设置为null。</p>
<pre class="brush: js; ">

window.onload = init;
</pre>
<p>最后，当页面加载完成后，我们必须知道立即运行绘制画布的代码；因此，在window对象的onload事件发生时，立即调用init函数。</p>
<h2>init函数</h2>
<pre class="brush: js; ">

function init(){
	canvas = document.getElementById(&#039;canvas&#039;);
	context2D = canvas.getContext(&#039;2d&#039;);
	setInterval(draw, 1000/FPS);
}
</pre>
<p>页面加载完毕后就会调用上面这个init函数。在这个函数中，我们先通过在HTML文件中指定的ID属性取得画布元素（毫无疑问，除了把它叫做画布，还能叫个啥？），然后再取得这个画布的2D绘图上下文对象。</p>
<p>上下文对象用于定义如何在画布上绘图。顾名思义，2D上下文嘛，支持在画布上绘制2D图形、图像和文本。支持画布元素的浏览器都支持2D上下文，除了2D上下文，还有其他试验性的上下文对象。Opera有一个专门为游戏设计的2D上下文，而Mozilla则有一个能够显示3D场景的上下文。可惜呀，目前这些上下文对象只有特定的浏览器才支持。如果你想用画布来创建Web应用，最好还是只使用常见的2D上下文。</p>
<p>因为我们在这里是想绘制一幅能移动的图像，所以必须建立渲染循环（render loop）。所谓渲染循环，实际上就是一个被重复调用的函数，渲染循环的每一次迭代，（在这个例子中）都可以让图像在屏幕上产生一点位移，如此循环往复就能给人图像在移动的感觉。为此，我们调用了setInterval函数，它的第一个参数是应该被重复调用的函数，这里的函数名是draw。setInterval函数的第二个参数指定调用函数的频率。这个参数值的单位是毫秒，而用1000除以早先定义的FPS得到的就是每次调用之间相隔的毫秒数。</p>
<p>这里需要注意一下，虽然我们指定每秒钟调用30次draw函数，但实际上不会调用30次。多长时间调用一次draw函数，取决于底层JavaScript引擎的速度和要执行的draw函数代码的复杂程度。如果系统很慢的话，很可能每秒钟只能调用一次draw函数。所以说，这里指定给setInterval的频率只是一种最理想的情况。</p>
<h2>draw函数</h2>
<p>在画布上绘图的操作实际上都是由draw函数来完成的。下面我们就一步一步地说明其中的绘图操作。</p>
<pre class="brush: js; ">

context2D.clearRect(0, 0, canvas.width, canvas.height);
</pre>
<p>所有绘图操作都是在上下文对象上发生的，并不是在画布元素上发生的。这里首先清空上下文，以便为绘制每一帧画面准备一个干净的版面。</p>
<pre class="brush: js; ">

context2D.drawImage(image, x, y);
</pre>
<p>紧接着，就把图像绘制到上下文对象中，参数x和y指定了绘制图像的左上角坐标。</p>
<pre class="brush: js; ">

x += 1 * xDirection;
y += 1 * yDirection;
</pre>
<p>为了让图像在画布上移动，需要根据xDirection和yDirection是等于1（向右或向下）还是等于-1（向左或向上），来递增或递减x与y的值。</p>
<pre class="brush: js; ">

if (x &gt;= 450){
	x = 450;
	xDirection = -1;
} else if (x &lt;= 0) {
	x = 0;
	xDirection = 1;
}
if (y &gt;= 250) {
	y = 250;
	yDirection = -1;
} else if (y &lt;= 0) {
	y = 0;
	yDirection = 1;
}
</pre>
<p>如果图像移动到了画布外面，则反转图像的移动方向。我们知道图像的大小是150×150像素，而画布的大小的是600×400像素，因而就有了450（600 &#8211; 150）和250（400 &#8211; 150）这两个值。</p>
<p>最后的效果就是笑脸图像会在画布的范围内反弹往复。此时此刻，有读者可能会想：同样的效果如果通过修改DIV元素的位置来实现可能更容易一些。这一点我不否认。但这个例子只演示了画布元素所能实现的简单效果。下一篇文章我们就会介绍使用画布元素能够实现的高级效果，同样的效果若采用其他方式，恐怕就要困难多了。</p>
<p>为之漫笔 最后编辑于：<br />
2011/08/12 @ 06:39</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/08/10/game-development-with-javascript-and-the-canvas-element-2554.html/feed</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>JavaScript是Web的汇编语言（二）：疯狂，亦或只是精神错乱？</title>
		<link>http://www.cn-cuckoo.com/2011/07/21/javascript-is-assembly-language-for-the-web-ii-2531.html</link>
		<comments>http://www.cn-cuckoo.com/2011/07/21/javascript-is-assembly-language-for-the-web-ii-2531.html#comments</comments>
		<pubDate>Thu, 21 Jul 2011 00:40:28 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2531</guid>
		<description><![CDATA[原文地址：JavaScript is Assembly Language for the Web: Part 2 &#8211; Madness or just Insanity? 有些人认为“JavaScript是Web的汇编语言”完全是精神病说的话。为此，我询问了几位JavaScript权威，比如Brendan Eich（JavaScript之父）、Douglas Crockford（JSON之父），还有Mike Shaver（Mozilla技术副总裁）。以下都是从个人邮件里摘过来的，得到了以上几位的许可。 Mike Shaver： 以前我就听说过这种比较，我认为很大程度上确实如此。但是，这种说法忽视了JS开发人员与机器之间的人机工程学方面的大量努力，因为汇编语法设计得没有那么人性化（特别是现代的汇编语言）。 Brendan Eich： 几年前，我曾说过“JS是Web的x86”（好像是在一次JSConf上），不过我不敢说我是第一个这么说的。（Nick Thompson今年也在Hacker News中这么说过。） 关键在于，JS确实在按照我们想的，越来越往低级方向发展了。但它也具备高级的特性。 Shaver说得没错，汇编缺少可靠的宏处理器，因此不适合程序员，也不够安全。但JS可不是这样。所以，这个比喻需要加点限制条件，不然就要闹出笑话来了。 无论从高级函数式编程还是内存安全角这个角度看，还是从低级特性，像类型化数组以及即将成为现实的ES中类型化数组的扩展、二进制数据，等等来说，JS都是一个比汇编更加强大的编程语言。当然了，内存安全是首要的区别。 Douglas Crockford： 就这个问题来说，我觉得说JavaScript是Web的虚拟机更接近一些。过去我们一直都把Java的JVM看成是Web的虚拟机，但结果呢，JavaScript才是。 从提供代码安全的角度说，JavaScript的解析器比JVM的字节码验证器更有效。就兑现“编写一次，到处运行”这个诺言来看，JavaScript更出色；这或许正是因为它是在较高层次上运行，才得以避免触及一些底层的棘手问题。因为它把剩下的事儿都交给图灵去解决了。 当然啦，也有不少人始终不肯承认JavaScript能把一切都处理好，我过去就是这样一个人。而现在我则不断地被眼前这种百花齐放的景象震撼着。 Brendan Eich，补充： Doug说源代码强过字节码，说得太好了。很早以前，我的朋友，加州大学欧文分校的Michael Franz教授就指出了Java验证器O(n^4)级别的复杂性（计算机时钟周期失控，拒绝服务）。精简之后的JS呢，确实传输更便捷，而且词法/语法分析也相当快。 （JS）源代码作为“字节码”同样也避免了Java字节码的一个很傻的问题：冻结设计不良的Java低级形式，导致高级形式的源代码也无法解决这个问题。换句话说，Java唯恐破坏其字节码的兼容性。而这严重影响了Java内部类及其泛型的设计。——不管怎么说，Sun最后还是破坏了字节码的兼容性。 以下是前一段时间Nick Thompson在YCombinator说过的话： 这只是我个人的看法：我花了自己两年时间，想尽可能让JVM能够与JavaScript互通。当时的Netscape有不少人认为字节码作为移动代码的基础比较好。但Sun从头搭建了自己大而全的软件体系，把问题搞得很复杂。他们没有想让Java与其他语言沟通，更别提让Java能嵌入其他软件中了。他们的字符串处理代码都是用一种解释型语言来写，而不肯用C来玷污自己！有什么我就说什么，Netscape，这个当时Java唯一的一个大客户，在Sun眼里无非就是一个用来实现他们取代Windows梦想的工具而已。所有想用Java的人都是自己给自己找罪受。 在此期间，Brendan一个人干了10个工程师，外加3个客户服务人员的活儿，同时还要关注Web作者在乎的一些事，比如把JS代码混合到HTML中、即时加载、与浏览器其他模块的集成等等，此外还要协调其他浏览器厂商，以便让JS成为一个开放标准。 因此，今天的JS作为Web的x86汇编程序，并没有像它本该的那样完美，但你通过它真能把事情稿定（GWT就是一个最明显的例子）。这可以说是一个经典的“更差就是更好”的例子，只不过Java也就是从下往上那么看起来更好罢了。而JS则在此期间取得了相当不错的成就。要想取代它的地位可没那么容易。 当然，这个比喻不一定准确。JavaScript代码无论从外观到行为，肯定不像ASM。但作为一个比喻，至少可以说明： JavaScript无所不在； 它速度快而且越来越快； JavaScript酷似低级的Web编程语言； 它可以通过手工编写，也可以从另一种语言编译而来。 诸如此类的话题也经常在Hacker News中出现： “现在的JavaScript其实就是客户端的汇编语言。想改它太难了，所以得想办法开发一些工具来解决这个问题。”—— jonnycat 好啦，能听到如此有见地、有深度，而又详尽的讨论，该满足了吧。亲爱的读者，你们太棒了。]]></description>
			<content:encoded><![CDATA[<p style="text-align: right;">原文地址：<a href="http://www.hanselman.com/blog/JavaScriptIsAssemblyLanguageForTheWebPart2MadnessOrJustInsanity.aspx">JavaScript is Assembly Language for the Web: Part 2 &#8211; Madness or just Insanity?</a></p>
<p><strong>有些人认为“<a href="http://www.cn-cuckoo.com/2011/07/20/javascript-is-assembly-language-for-the-web-i-2511.html">JavaScript是Web的汇编语言</a>”完全是精神病说的话。</strong>为此，我询问了几位JavaScript权威，比如Brendan Eich（JavaScript之父）、Douglas Crockford（JSON之父），还有Mike Shaver（Mozilla技术副总裁）。以下都是从个人邮件里摘过来的，得到了以上几位的许可。</p>
<p><a href="http://en.wikipedia.org/wiki/Mike_Shaver" target="_blank">Mike Shaver</a>：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em;">以前我就听说过这种比较，我认为很大程度上确实如此。但是，这种说法忽视了JS开发人员与机器之间的人机工程学方面的大量努力，因为汇编语法设计得没有那么人性化（特别是现代的汇编语言）。</div>
<p><a href="http://en.wikipedia.org/wiki/Brendan_Eich" target="_blank">Brendan Eich</a>：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em;">
<p>几年前，我曾说过“JS是Web的x86”（好像是在一次JSConf上），不过我不敢说我是第一个这么说的。（Nick Thompson今年也在Hacker News中这么说过。）</p>
<p>关键在于，JS确实在按照我们想的，越来越往低级方向发展了。但它也具备高级的特性。</p>
<p>Shaver说得没错，汇编缺少可靠的宏处理器，因此不适合程序员，也不够安全。但JS可不是这样。所以，这个比喻需要加点限制条件，不然就要闹出笑话来了。</p>
<p>无论从高级函数式编程还是内存安全角这个角度看，还是从低级特性，像类型化数组以及即将成为现实的ES中类型化数组的扩展、二进制数据，等等来说，JS都是一个比汇编更加强大的编程语言。当然了，内存安全是首要的区别。</p>
</div>
<p><a href="http://en.wikipedia.org/wiki/Douglas_Crockford" target="_blank"><span id="more-2531"></span>Douglas Crockford</a>：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em;">
<p>就这个问题来说，我觉得说JavaScript是Web的虚拟机更接近一些。过去我们一直都把Java的JVM看成是Web的虚拟机，但结果呢，JavaScript才是。</p>
<p>从提供代码安全的角度说，JavaScript的解析器比JVM的字节码验证器更有效。就兑现“编写一次，到处运行”这个诺言来看，JavaScript更出色；这或许正是因为它是在较高层次上运行，才得以避免触及一些底层的棘手问题。因为它把剩下的事儿都交给图灵去解决了。</p>
<p>当然啦，也有不少人始终不肯承认JavaScript能把一切都处理好，我过去就是这样一个人。而现在我则不断地被眼前这种百花齐放的景象震撼着。</p>
</div>
<p>Brendan Eich，补充：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em;">
<p>Doug说源代码强过字节码，说得太好了。很早以前，我的朋友，加州大学欧文分校的Michael Franz教授就指出了Java验证器O(n^4)级别的复杂性（计算机时钟周期失控，拒绝服务）。精简之后的JS呢，确实传输更便捷，而且词法/语法分析也相当快。</p>
<p>（JS）源代码作为“字节码”同样也避免了Java字节码的一个很傻的问题：冻结设计不良的Java低级形式，导致高级形式的源代码也无法解决这个问题。换句话说，Java唯恐破坏其字节码的兼容性。而这严重影响了Java内部类及其泛型的设计。——不管怎么说，Sun最后还是破坏了字节码的兼容性。
</p></div>
<p>以下是前一段时间<a href="http://nixweb.com/" target="_blank">Nick Thompson</a>在YCombinator说过的话：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em;">
<p>这只是我个人的看法：我花了自己两年时间，想尽可能让JVM能够与JavaScript互通。当时的Netscape有不少人认为字节码作为移动代码的基础比较好。但Sun从头搭建了自己大而全的软件体系，把问题搞得很复杂。他们没有想让Java与其他语言沟通，更别提让Java能嵌入其他软件中了。他们的字符串处理代码都是用一种解释型语言来写，而不肯用C来玷污自己！有什么我就说什么，Netscape，这个当时Java唯一的一个大客户，在Sun眼里无非就是一个用来实现他们取代Windows梦想的工具而已。所有想用Java的人都是自己给自己找罪受。</p>
<p>在此期间，Brendan一个人干了10个工程师，外加3个客户服务人员的活儿，同时还要关注Web作者在乎的一些事，比如把JS代码混合到HTML中、即时加载、与浏览器其他模块的集成等等，此外还要协调其他浏览器厂商，以便让JS成为一个开放标准。</p>
<p>因此，今天的JS作为Web的x86汇编程序，并没有像它本该的那样完美，但你通过它真能把事情稿定（GWT就是一个最明显的例子）。这可以说是一个经典的“更差就是更好”的例子，只不过Java也就是从下往上那么看起来更好罢了。而JS则在此期间取得了相当不错的成就。要想取代它的地位可没那么容易。</p>
</div>
<p>当然，这个比喻不一定准确。JavaScript代码无论从外观到行为，肯定不像ASM。但作为一个比喻，至少可以说明：</p>
<ul>
<li>JavaScript无所不在；</li>
<li>它速度快而且越来越快；</li>
<li>JavaScript酷似低级的Web编程语言；</li>
<li>它可以通过手工编写，也可以从另一种语言编译而来。</li>
</ul>
<p>诸如此类的话题也经常在Hacker News中出现：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em;">
“现在的JavaScript其实就是客户端的汇编语言。想改它太难了，所以得想办法开发一些工具来解决这个问题。”——<br />
<a href="http://news.ycombinator.com/item?id=2451594">jonnycat</a></div>
<p>好啦，能听到如此有见地、有深度，而又详尽的讨论，该满足了吧。亲爱的读者，你们太棒了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/07/21/javascript-is-assembly-language-for-the-web-ii-2531.html/feed</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
		<item>
		<title>JavaScript是Web的汇编语言（一）：语义Web已死！</title>
		<link>http://www.cn-cuckoo.com/2011/07/20/javascript-is-assembly-language-for-the-web-i-2511.html</link>
		<comments>http://www.cn-cuckoo.com/2011/07/20/javascript-is-assembly-language-for-the-web-i-2511.html#comments</comments>
		<pubDate>Wed, 20 Jul 2011 12:59:04 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2511</guid>
		<description><![CDATA[原文地址：JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML （更新）有些人认为“JavaScript是Web的汇编语言”完全是精神病说的话。为此，我询问了几位JavaScript权威，比如Brendan Eich（JavaScript之父）、Douglas Crockford（JSON之父），还有Mike Shaver（Mozilla技术副总裁）。他们的评论发表在下一篇文章里。 昨天我跟Erik Meijer聊天，他说： JavaScript就是一个汇编语言。JavaScript加上生成的HTML就像是.NET汇编一样。浏览器可以执行这些代码，但没人真的关心里面到底写的是什么。 ——Erik Meijer 怎么会说起这件事儿呢？当时我正在试用Google+，就跟上大多数让我印象深刻的网站一样，我立即就查看它的源代码。不看不要紧，一看吓一跳： 咱就将就说吧，我看到了1300行代码，密密麻麻的，大约90KB。上面图片显示的只是最前面的一小部分，基本上都是“瘦身后”的JavaScript代码。再往下看，页面中间呢，全都是像下面这样的span、div以及生成的类和id： 我勒个去，满篇都是GUID（Globally Unique Identifier，全局唯一标识符）。 话又说回来了，http://msn.com、http://www.bing.com、http://.www.facebook.com，全都这样啊。就连http://www.twitter.com也都开始有点“瘦身”的迹象了。所有大点的网站好像都丝毫不在乎什么标记之美。这是为什么呢？ 这样有效率啊，性能高啊。Google很多最出色的网站背后都依赖GWT呢。在这种情况下，要是这一类网站的里头和外头全都一样漂亮，你反而会觉得不可思议了。 不能不说这可真有点讽刺的意味。曾几何时，ASP.NET的开发人员对ViewState可是怨声载道啊。“简直太笨了”的意思就是“我看不懂它都干什么了。”ViewState曾经（现在也）是一项让Web开发效率提高很多倍的技术。它跟Google Web Toolkit（GWT）不一样，但GWT与WebForms的出发点也并非完全没有相似之处。看看GWT网站自己怎么说： Google Web Toolkit（GWT）是一个开发工具包，用于构建和优化基于浏览器的复杂应用。GWT的目标是提高高性能Web应用开发的效率，而且无需开发人员熟悉浏览器的各种怪癖，以及XMLHttpRequest，还有JavaScript。 这个出发点可真是值得赞美，不对？难道不可以这样说（抱歉，开个玩笑而已）： “ASP.NET WebForms”是一个开发工具包，用于构建和优化基于浏览器的复杂应用。它的目标是提高高性能Web应用开发的效率，而且无需开发人员熟悉浏览器的各种怪癖，以及XMLHttpRequest，还有JavaScript。 本文的目的不是想夸奖WebForms，也不是给WebForms正名。WebForms对于某些应用是不二之选，正如GWT对其他一些应用那样。我真正想说的是，使用服务器端的工具包，没有办法像使用jQuery写出清晰的JavaScript，或者使用Razor或HAML写出清晰、清楚的标记一样，给Web开发带来真正的快乐。归根结底，其实就是你选择的抽象级别的问题。 所谓的语义标记在这种情况下仍然是被隐藏的，而诸如http://schema.org之类的站点也仍然非常重要，只不过可别指望你能在心仪的站点里看到缩进得像俳句一样整齐的源代码。 大家知道，精简和压缩属于正交优化。而我要说的是，一点也不在乎标记和脚本发送到客户端之后是否美观，确实太草率了。假如谁都不在乎发送到浏览器的标记，只在乎结果，那谁的标记和JS就那么不值钱啊，谁还愿意主动公开自己的源代码呢？反正，网站不是运行得挺好嘛，谁还在乎其他的？ 现在我要给亲爱的读者提个问题，你觉得自己为什么那么在乎点击“查看网页源代码”之后的结果呢？难道HTML5和JavaScript是Web的新汇编语言不成？ （更新）声明一下： 当然，这个比喻不一定准确。JavaScript代码无论从外观到行为，肯定不像ASM。但作为一个比喻，至少可以说明： JavaScript无所不在； 它速度快而且越来越快； JavaScript酷似低级的Web编程语言； 它可以通过手工编写，也可以从另一种语言编译而来。 作为开发人员或者设计人员，如果有工具提供了你需要的控制和你需要的结果，你最关心哪一个？我认为Rails、ASP.NET，甚至GWT，都没有100%做到这一点。它们都有自己的问题，但我认为将来的Web不会再专注于清晰的标记，而是夺目的用户体验和语言、工具的天下，开发人员会很享受，效率也会更高。 亲爱的读者，你愿意HTML和JavaScript再多抽象一点吗？还是希望它少抽象一点？ （再更新）为了让大家明白，我得再说一遍。本文讨论了两个独立的问题。一个当然就是源代码经过了精简和通常的混淆。但这只是第一个问题。真正的问题在于，JavaScript已经成了其他多种语言的目标语言。GWT是一个用JAVA来写Web应用的框架，它产生的字节码是“JavaScript”。GWT为原来天然的语言（HTML+JS）选择了一个设计好的高级语言，并将整个浏览器当成了一个VM。好，问题来了：我们是在写汇编呢，还是在写某种更高级点的代码？而且，我刚知道Google+是用Closure来写的，但这不影响前面的问题。]]></description>
			<content:encoded><![CDATA[<p style="text-align: right;">原文地址：<a href="http://www.hanselman.com/blog/JavaScriptIsAssemblyLanguageForTheWebSematicMarkupIsDeadCleanVsMachinecodedHTML.aspx">JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML</a></p>
<p><strong><span style="color:red;">（更新）</span>有些人认为“JavaScript是Web的汇编语言”完全是精神病说的话。为此，我询问了几位JavaScript权威，比如Brendan Eich（JavaScript之父）、Douglas Crockford（JSON之父），还有Mike Shaver（Mozilla技术副总裁）。<a href="http://http://www.cn-cuckoo.com/2011/07/21/javascript-is-assembly-language-for-the-web-ii-2531.html">他们的评论发表在下一篇文章里</a>。</strong></p>
<p>昨天我跟Erik Meijer聊天，他说：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em; font-weight: bold;">JavaScript就是一个汇编语言。JavaScript加上生成的HTML就像是.NET汇编一样。浏览器可以执行这些代码，但没人真的关心里面到底写的是什么。<br />
——Erik Meijer</div>
<p>怎么会说起这件事儿呢？当时我正在<strong>试用Google+</strong>，就跟上大多数让我印象深刻的网站一样，我立即就查看它的源代码。不看不要紧，一看吓一跳：<br />
<img class="colorbox-2511"  src="http://www.cn-cuckoo.com/cache/img/2511_image_1.png" alt="" /></p>
<p>咱就将就说吧，我看到了1300行代码，密密麻麻的，大约90KB。上面图片显示的只是最前面的一小部分，基本上都是“瘦身后”的JavaScript代码。再往下看，页面中间呢，全都是像下面这样的span、div以及生成的类和id：<span id="more-2511"></span></p>
<p><img class="colorbox-2511"  src="http://www.cn-cuckoo.com/cache/img/2511_image_2.png" alt="" /></p>
<p><strong>我勒个去，满篇都是GUID（Globally Unique Identifier，全局唯一标识符）。</strong></p>
<p>话又说回来了，<strong><a title="http://msn.com" href="http://msn.com" target="_blank">http://msn.com</a>、<a title="http://www.bing.com" href="http://www.bing.com" target="_blank">http://www.bing.com</a>、<a title="http://.www.facebook.com" href="http://.www.facebook.com" target="_blank">http://.www.facebook.com</a></strong>，全都这样啊。就连<strong><a title="http://www.twitter.com" href="http://www.twitter.com" target="_blank">http://www.twitter.com</a></strong>也都开始有点“瘦身”的迹象了。所有大点的网站好像都丝毫不在乎什么标记之美。这是为什么呢？</p>
<p>这样有效率啊，性能高啊。Google很多最出色的网站背后都依赖GWT呢。<strong>在这种情况下，要是这一类网站的里头和外头全都一样漂亮，你反而会觉得不可思议了。</strong></p>
<p>不能不说这可真有点讽刺的意味。曾几何时，ASP.NET的开发人员对ViewState可是怨声载道啊。“简直太笨了”的意思就是“我看不懂它都干什么了。”ViewState曾经（现在也）是一项让Web开发效率提高很多倍的技术。它跟Google Web Toolkit（GWT）不一样，但GWT与WebForms的出发点也并非完全没有相似之处。看看GWT网站自己怎么说：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em;">Google Web Toolkit（GWT）是一个开发工具包，用于构建和优化基于浏览器的复杂应用。GWT的目标是提高高性能Web应用开发的效率，而且无需开发人员熟悉浏览器的各种怪癖，以及XMLHttpRequest，还有JavaScript。</div>
<p>这个出发点可真是值得赞美，不对？难道不可以这样说（抱歉，开个玩笑而已）：</p>
<div style="border-left: 2px solid #ddd; margin-left: 1em; padding-left: 1em;">“<strong>ASP.NET WebForms</strong>”是一个开发工具包，用于构建和优化基于浏览器的复杂应用。它的目标是提高高性能Web应用开发的效率，而且无需开发人员熟悉浏览器的各种怪癖，以及XMLHttpRequest，还有JavaScript。</div>
<p>本文的目的不是想夸奖WebForms，也不是给WebForms正名。WebForms对于某些应用是不二之选，正如GWT对其他一些应用那样。我真正想说的是，使用服务器端的工具包，没有办法像使用jQuery写出清晰的JavaScript，或者使用Razor或HAML写出清晰、清楚的标记一样，给Web开发带来真正的快乐。归根结底，其实就是你选择的抽象级别的问题。</p>
<p>所谓的语义标记在这种情况下仍然是被隐藏的，而诸如<strong><a title="http://schema.org" href="http://schema.org" target="_blank">http://schema.org</a></strong>之类的站点也仍然非常重要，只不过可别指望你能在心仪的站点里看到缩进得像俳句一样整齐的源代码。</p>
<p>大家知道，精简和压缩属于正交优化。而我要说的是，一点也不在乎标记和脚本发送到客户端之后是否美观，确实太草率了。<strong>假如谁都不在乎发送到浏览器的标记，只在乎结果，那谁的标记和JS就那么不值钱啊，谁还愿意主动公开自己的源代码呢？</strong>反正，网站不是运行得挺好嘛，谁还在乎其他的？</p>
<p>现在我要给亲爱的读者提个问题，你觉得自己为什么那么在乎点击“查看网页源代码”之后的结果呢？难道HTML5和JavaScript是Web的新汇编语言不成？</p>
<p><strong>（更新）声明一下：</strong><br />
当然，这个比喻不一定准确。JavaScript代码无论从外观到行为，肯定不像ASM。但作为一个比喻，至少可以说明：</p>
<ul>
<li>JavaScript无所不在；</li>
<li>它速度快而且越来越快；</li>
<li>JavaScript酷似低级的Web编程语言；</li>
<li>它可以通过手工编写，也可以从另一种语言编译而来。</li>
</ul>
<p>作为开发人员或者设计人员，如果有工具提供了你需要的控制和你需要的结果，你最关心哪一个？我认为Rails、ASP.NET，甚至GWT，都没有100%做到这一点。它们都有自己的问题，但<strong>我认为将来的Web不会再专注于清晰的标记，而是夺目的用户体验和语言、工具的天下，开发人员会很享受，效率也会更高。</strong></p>
<p>亲爱的读者，你愿意HTML和JavaScript再多抽象一点吗？还是希望它少抽象一点？</p>
<p><strong>（再更新）</strong>为了让大家明白，我得再说一遍。本文讨论了两个独立的问题。一个当然就是源代码经过了精简和通常的混淆。但这只是第一个问题。真正的问题在于，JavaScript已经成了其他多种语言的目标语言。GWT是一个用JAVA来写Web应用的框架，它产生的字节码是“JavaScript”。GWT为原来天然的语言（HTML+JS）选择了一个设计好的高级语言，并将整个浏览器当成了一个VM。好，问题来了：我们是在写汇编呢，还是在写某种更高级点的代码？而且，我刚知道Google+是用Closure来写的，但这不影响前面的问题。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/07/20/javascript-is-assembly-language-for-the-web-i-2511.html/feed</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>松本行弘说：我想让Ruby更快地发展</title>
		<link>http://www.cn-cuckoo.com/2011/07/13/matz-qa-after-joining-heroku-2504.html</link>
		<comments>http://www.cn-cuckoo.com/2011/07/13/matz-qa-after-joining-heroku-2504.html#comments</comments>
		<pubDate>Wed, 13 Jul 2011 06:22:11 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2504</guid>
		<description><![CDATA[日文版：Mr Junichi Niino，Rubyの進歩がより速くなることを期待している 英文版：I am looking forward to accelerating Ruby&#8217;s progress 我在前一篇文章里已经提到过了，松本行弘（Yukihiro Matsumoto，或Matz）已经加入了Heroku，成为该公司的首席Ruby架构师。我通过电子邮件询问了Matz对自己未来的设想。 “让Ruby核心功能更丰富、品质更高是我的使命。” PublicKey（以下称Q）：能不能介绍一下你怎么就换工作了呢？ 松本行弘先生（以下称Matz）：上一次跟Marc Benioff先生（Salesforce.com的CEO）碰面时，他就问我，怎么才可以支持Ruby的开发。 于是，我就说了，我说我想要改变当前大多数Ruby核心开发人员面临的窘境：这些人有的是牺牲自己的闲暇时间来完成自己的工作，有的还在为自己的职业前途担心。 然后，他就说他可以为我们提供一些支持，而这也是我通过Heroku加入Salesforce.com的原因（注意：目前还有几位Ruby核心开发人员也正协商加入Heroku）。 也就是说，我们工作的核心内容没有变。我们的使命依旧还是开发Ruby核心，把它变得功能更丰富、品质更高。如此说来，我期待着我们提供的职业前景，以及来自包括Heroku在内的大量Ruby用户的反馈，能够加速Ruby开发的进程。要不然，我换这次工作就没有什么意义了。 不过，加入Heroku并担任他们的首席架构师，并不意味着我只关心Heroku和Salesforce.com的发展。我加入他们不会改变我与NaCI还有Rakuten的合作关系，后两者仍然会继续支持我，而且我还将继续担任Ruby协会的会长一职。 我为什么选这个头衔？因为我觉得“首席架构师”这个名字听起来显得最容易与业务撇清关系。我不打算将来在Heroku参与任何业务上的决策。 Q：你对自己在Heroku的角色有什么期许？ Matz：刚才不是已经说了嘛，我的工作没有什么变化，但我们的开发进度会加快。除此之外，我还能与Heroku进行更加密切的沟通，以便尽早解除Ruby对云计算平台的限制（如果有限制的话）。 而且，我还会继续与Engine Yard、VMWare等公司保持良好的关系，即使我加入了Heroku，我仍然还是那个“Ruby的Matz”，不会辜负他们的信任。 Q：此时此刻，你对Ruby和云计算有什么看法？ Matz：说实话，很多Ruby核心开发人员（包括我自己）都对Web没有多大的兴趣，但云计算会变得越来越重要，这一点是显而易见的。所以，我希望能从这些云计算运营商那里得到更多的需求，以便将其反映到未来的Ruby中。 Q：你会去硅谷上班吗，还是在松江远程工作？ Matz：我的生活方式不会变（但工作方式会变），所以多数时候我会在松江的家里远程参与开发。 就我自己而言，搬到硅谷去工作不一定会比现在这个环境更好。 不过，我想我每年都会跟旧金山/硅谷（包括Heroku）的那些人直接沟通几次。 （注意：大家别误会，Heroku的总部在旧金山，不在硅谷，但他们在新闻稿里自称是“硅谷公司”。） Q：还有别的要说的吗？ Matz：我听说很多（日本之外国家的）人并不知道，Ruby核心团队迄今还没有一个可持续的发展模式。我希望我这次换工作能为代表日本发布消息的日本软件开发人员树立一个榜样。 Q：谢谢你接受采访。]]></description>
			<content:encoded><![CDATA[<p style="text-align: right;">日文版：Mr Junichi Niino，<a href="http://www.publickey1.jp/blog/11/ruby_heroku.html">Rubyの進歩がより速くなることを期待している</a><br />
英文版：<a href="http://blog.new-bamboo.co.uk/2011/7/12/translation-of-matz-q-a-article-after-joining-heroku">I am looking forward to accelerating Ruby&#8217;s progress</a></p>
<p>我在前一篇文章里已经提到过了，松本行弘（Yukihiro Matsumoto，或Matz）已经加入了Heroku，成为该公司的首席Ruby架构师。我通过电子邮件询问了Matz对自己未来的设想。</p>
<p style="text-align: center;padding:1em;border:1px dashed #ddd;">“让Ruby核心功能更丰富、品质更高是我的使命。”</p>
<p><strong>PublicKey（以下称Q）：能不能介绍一下你怎么就换工作了呢？</strong></p>
<p><strong>松本行弘先生（以下称Matz）</strong>：上一次跟Marc Benioff先生（Salesforce.com的CEO）碰面时，他就问我，怎么才可以支持Ruby的开发。</p>
<p>于是，我就说了，我说我想要改变当前大多数Ruby核心开发人员面临的窘境：这些人有的是牺牲自己的闲暇时间来完成自己的工作，有的还在为自己的职业前途担心。</p>
<p>然后，他就说他可以为我们提供一些支持，而这也是我通过Heroku加入Salesforce.com的原因（注意：目前还有几位Ruby核心开发人员也正协商加入Heroku）。</p>
<p>也就是说，我们工作的核心内容没有变。我们的使命依旧还是开发Ruby核心，把它变得功能更丰富、品质更高。如此说来，我期待着我们提供的职业前景，以及来自包括Heroku在内的大量Ruby用户的反馈，能够加速Ruby开发的进程。要不然，我换这次工作就没有什么意义了。</p>
<p>不过，加入Heroku并担任他们的首席架构师，并不意味着我只关心Heroku和Salesforce.com的发展。我加入他们不会改变我与NaCI还有Rakuten的合作关系，后两者仍然会继续支持我，而且我还将继续担任Ruby协会的会长一职。</p>
<p>我为什么选这个头衔？因为我觉得“首席架构师”这个名字听起来显得最容易与业务撇清关系。我不打算将来在Heroku参与任何业务上的决策。</p>
<p><strong>Q：你对自己在Heroku的角色有什么期许？</strong></p>
<p><strong>Matz：</strong>刚才不是已经说了嘛，我的工作没有什么变化，但我们的开发进度会加快。除此之外，我还能与Heroku进行更加密切的沟通，以便尽早解除Ruby对云计算平台的限制（如果有限制的话）。</p>
<p>而且，我还会继续与Engine Yard、VMWare等公司保持良好的关系，即使我加入了Heroku，我仍然还是那个“Ruby的Matz”，不会辜负他们的信任。</p>
<p><strong>Q：此时此刻，你对Ruby和云计算有什么看法？</strong></p>
<p><strong>Matz：</strong>说实话，很多Ruby核心开发人员（包括我自己）都对Web没有多大的兴趣，但云计算会变得越来越重要，这一点是显而易见的。所以，我希望能从这些云计算运营商那里得到更多的需求，以便将其反映到未来的Ruby中。</p>
<p><strong>Q：你会去硅谷上班吗，还是在松江远程工作？</strong></p>
<p><strong>Matz：</strong>我的生活方式不会变（但工作方式会变），所以多数时候我会在松江的家里远程参与开发。</p>
<p>就我自己而言，搬到硅谷去工作不一定会比现在这个环境更好。</p>
<p>不过，我想我每年都会跟旧金山/硅谷（包括Heroku）的那些人直接沟通几次。</p>
<p>（注意：大家别误会，Heroku的总部在旧金山，不在硅谷，但他们在新闻稿里自称是“硅谷公司”。）</p>
<p><strong>Q：还有别的要说的吗？</strong></p>
<p><strong>Matz：</strong>我听说很多（日本之外国家的）人并不知道，Ruby核心团队迄今还没有一个可持续的发展模式。我希望我这次换工作能为代表日本发布消息的日本软件开发人员树立一个榜样。</p>
<p><strong>Q：谢谢你接受采访。</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/07/13/matz-qa-after-joining-heroku-2504.html/feed</wfw:commentRss>
		<slash:comments>41</slash:comments>
		</item>
		<item>
		<title>JavaScript，只有你想不到</title>
		<link>http://www.cn-cuckoo.com/2011/06/22/time-to-learn-javascript-2463.html</link>
		<comments>http://www.cn-cuckoo.com/2011/06/22/time-to-learn-javascript-2463.html#comments</comments>
		<pubDate>Wed, 22 Jun 2011 13:59:27 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2463</guid>
		<description><![CDATA[原文地址：http://radar.oreilly.com/2011/06/time-to-learn-javascript.html作者简介：Mike Loukides 很长时间以来，JavaScript在我眼里都是编程语言中的二等公民。早先，它经常是很多安全问题的发源地，就像是胶水一样，它能把HTML应用与样式粘到一块，可没有人拿它来正正规规地写程序；这样的情形太普遍了。而Java、Ruby、Python，这些才是真正能用来写程序的语言。 过去几年间，我对JavaScript的态度有了彻底的改变。JavaScript已经“长大成人”了。我敢保证很多JavaScript开发人员都不会认同我前面的说法，他们会说JavaScript一直都是一个十分强大、成熟、深得人心的语言。或许他们说得没错，事实上只要是一门完整的编程语言，就能拿来写程序，也包括BASIC这种滥东西。而一门语言真正有用，必须一方面自身具备很强的表达能力，另一方面还要有众多的库和开发工具。显然，JavaScript的表达能力早就没有问题了，即便是创建对象的方式有点不好让人接受，其实问题也不大。直到最近，一些极其重要的扭转局面的技术出现了：jQuery、JSON、Node.js和HTML5。或许JavaScript以前就是一门完善的语言了，但却是这些重要的相关技术（以及其他一些没有在这里提及的），让JavaScript成为了每一个开发人员都知道的语言。如果明年你要学一门新语言的话，那一定就是JavaScript。 潜力无限的Node.js 说Node.js潜力无限的意思，就是它有可能引发Web开发的革命。Node.js是一个框架，用于构建高性能Web应用——即使是巨量的请求也能应对如流。虽然Node本身作为一个底层框架，能够用于构建任何应用，但它还是最适合构建Web服务器。它的异步事件驱动模式与传统的请求-响应模式相比，无疑更适合Web应用。 有两方面因素更让人看好Node。首先，Google在提升JavaScript性能方面掀起了一场革命。这句话的意思并不是说你随时随地都可以用上最好的JavaScript引擎（尽管这也是我们一个美好的期望）。但可以肯定的是，Google在其他竞争对手还没有上心的情况下，真的把JavaScript性能当成了一回事儿。如此一来，就把Mozilla、Apple、Microsoft、Opera，还有其他浏览器开发商逼到了性能竞赛的跑道上。结果导致我们现在使用的JavaScript引擎较之几年前快了不知道有多少倍，完全有能力运行复杂的大型Web应用。 其次，Node有着庞大的开发人员基础。不管大家在服务器端使用的是什么语言，但在客户端却鲜有不使用JavaScript的。有的人可能是“剪刀加浆糊”式的东拼西凑，有的人则可能用JavaScript做出了高超的Ajax应用，而有的人甚至实现了全功能的应用程序，像Twitter或Gmail。可不管怎么说，JavaScript开发人员的数量无疑是非常庞大的。而Doug Crockford等作者更是极力宣传所有人都应该把JavaScript当成一门严肃正经的编程语言来看待——尽管它还有不少缺点。 当时当下，编写Node应用相对还是个“粗”活儿，毕竟它只是一个底层库。想象一下单纯使用JavaScript写代码，对，就是这种感觉，Node当前还是一个beta版的格局，与Rails或Django这样成熟的Web开发框架还没法比。这种状况无疑会改变。一些轻量级的框架，比如Express，已经出现了；我坚信更多基于Node的全功能框架将继续不断涌现。 前面提到过一些几乎完全在浏览器中运行的高级Web应用。那些都已经不算什么新鲜事儿了，Gmail多大了？Google Maps贵庚了？不过，用JavaScript编写在浏览器中运行的应用的客户端无疑是越来越有吸引力了。HTML5则继续推高了人们对这一趋势的期许。 HTML5就是JavaScript 我不知道已经说过多少次了，HTML5实际上并没有多少与HTML有关，它其实就是JavaScript。HTML本身有什么变化？不过一些新标签而已，况且哪个新标签都不难理解。HTML5的威力在于让你能用JavaScript来创建这些标签。假如没有后台代码通过Canvas来创建动画、游戏，或者通过它来实现一些数据的可视化，这个标签也没有大用处。从浏览器开始支持Canvas开始，我已经看到了Asteroids（行星游戏）的上百个实现，那都是开发人员为熟悉这个新特性所做的练习。有的比较粗糙一些，而有的则极其精美。这些完全都要归功于JavaScript。 由此可见，HTML5并不是以尖括号为特征的标签语言的一次大的改进，其实质是赋予了JavaScript更强大的能力。WebGL库（当前还羽翼未丰）支持在HTML5的画布中绘制实时的3D图形。HTML5的地理位置支持在浏览器中实现LBS（Location Based Service）应用——这都是手机的基本配置。而持久存储以及离线功能则为开发能与桌面应用媲美，但却在浏览器中运行的全功能应用奠定了基础。目前，就连增加多点触摸事件的实验性的库也已经出现了。凡此种种，无一不是实实在在的JavaScript特性。HTML5只是为这些高级功能的发挥提供了舞台。 退一步讲，不依赖于HTML5的浏览器端开发库也取得了长足的进步。长久以来，JavaScript一直都是在HTML中实现动态效果的不二之选。可两个问题迟迟得不到解决：一是浏览器兼容性问题，二是直接操作DOM太麻烦。jQuery让这两个问题霎那间消失得无影无踪，这个库已经成为现代基于浏览器的客户端开发的基本配置。不过，并非只有jQuery。Protovis、还有D3，都可以让你直接在浏览器中创建复杂的交互性数据可视化效果，有史以来第一次让浏览器成为了展示数据的一个重要媒介。 JavaScript与数据库，编译器与语言 就连数据库里都开始广泛使用JavaScript了！当前如火如荼的NoSQL运动的三只领头羊：CouchDB、MongoDB和Riak，都是“文档数据库”。它们保存的不是表，而是文档。这几个数据库所谓的“文档”，其实就是JSON文档，而不是Word或Excel。（Riak除了JSON文档，还支持XML和纯文本。）JSON已经成为一种被广泛采用的数据交换格式（所有现代的编程语言几乎全都有解析JSON的库），不过请注意，JSON实际上不就是一种序列化JavaScript对象的格式嘛！因此，虽然你可以在任何语言中使用JSON，但在JavaScript开发中使用它则是再自然不过的事了。况且，JSON 这个格式成为一种跨语言的标准，而不是Python、Ruby或Java等语言的序列化格式，这个事实本身足以说明JavaScript将在更加广阔的舞台上大显身手。还不仅仅如此，上述三个数据库都内置了支持JavaScript查询的能力。未来几年，更多的人都将会惊讶地发现，JavaScript和JSON还会内置到其他应用程序中！ JavaScript时代的大幕才刚刚拉开。在今年的JSConf上，一个核心主题就是“JavaScript到JavaScript的编译器”，也被人们看成是未来的一个主要趋势。Google在“编译生成JavaScript代码”方面是首开先河者。据我所知，GWT（Google Web Toolkit）应该是通过编译（从Java代码）生成JavaScript代码的第一个框架。以前我对GWT并没有太重视，只是觉得它是一个致力于拯救那些Java程序员的框架，好让他们不必因为（学习）编写JavaScript而浪费时间。可是，GWT在编译过程中对JavaScript做了那么多的优化，简直是太神了。Closure就是一个“JavaScript到JavaScript的编译器”，能够实现同样级别的优化。Traceur，这是几个星期前才冒出来的一个框架，通过它能够试验JavaScript的新特性，换句话说，它可以把带有实验性语言特性的JavaScript代码编译成可以在所有现代平台中运行的JavaScript代码。 最后，我们也开始看到了当初Java大旗下JVM语言的蓬勃景象：很多语言都在致力于编译成JavaScript！其中有一些语言比较有意思，像Coffeescript和Kaffeine，它们在风格上酷似JavaScript，但更关注弥补JavaScript的一些不够完善的地方。是不是觉得JavaScript的对象模型特有意思，可怎么看怎么有点笨笨滴，有木有？是不是一想到基于原型创建一个实际的对象都需要反反复复地定义这定义那，就望而却步了，有木有？Coffeescript对此作了明显的改进。除了完善对象模型，Coffeescript 还添加了类似列表解析（list comprehensions）的新特性，去掉了大部分花括号。就像在Python中一样，要使用缩进来区分代码块。 未来的Web服务器、取之不尽的客户端库、HTML5、数据库，乃至基于JavaScript的语言——我现在一睁眼看到的就是JavaScript！假如你曾经对JavaScript敬而远之，今年可是该学习它了。没有任何理由，真的，再不学，恐怕你就没机会跟上时代了！]]></description>
			<content:encoded><![CDATA[<p style="text-align: right;">原文地址：<a href="http://radar.oreilly.com/2011/06/time-to-learn-javascript.html">http://radar.oreilly.com/2011/06/time-to-learn-javascript.html</a><br />作者简介：<a href="http://radar.oreilly.com.cn/user/michael_loukides">Mike Loukides </a></p>
<p><a href="http://radar.oreilly.com.cn/user/michael_loukides"><img class="colorbox-2463"  src="/images/loukides.jpg" style="float:left;margin:0 1em 1em 0;" /></a>很长时间以来，JavaScript在我眼里都是编程语言中的二等公民。早先，它经常是很多安全问题的发源地，就像是胶水一样，它能把HTML应用与样式粘到一块，可没有人拿它来正正规规地写程序；这样的情形太普遍了。而Java、Ruby、Python，这些才是真正能用来写程序的语言。</p>
<p>过去几年间，我对JavaScript的态度有了彻底的改变。JavaScript已经“长大成人”了。我敢保证很多JavaScript开发人员都不会认同我前面的说法，他们会说JavaScript一直都是一个十分强大、成熟、深得人心的语言。或许他们说得没错，事实上只要是一门完整的编程语言，就能拿来写程序，也包括BASIC这种滥东西。而一门语言真正有用，必须一方面自身具备很强的表达能力，另一方面还要有众多的库和开发工具。显然，JavaScript的表达能力早就没有问题了，即便是创建对象的方式有点不好让人接受，其实问题也不大。直到最近，一些极其重要的扭转局面的技术出现了：jQuery、JSON、Node.js和HTML5。或许JavaScript以前就是一门完善的语言了，但却是这些重要的相关技术（以及其他一些没有在这里提及的），让JavaScript成为了每一个开发人员都知道的语言。如果明年你要学一门新语言的话，那一定就是JavaScript。</p>
<h2>潜力无限的Node.js</h2>
<p>说<a href="http://nodejs.org/">Node.js</a>潜力无限的意思，就是它有可能引发Web开发的革命。Node.js是一个框架，用于构建高性能Web应用——即使是巨量的请求也能应对如流。虽然Node本身作为一个底层框架，能够用于构建任何应用，但它还是最适合构建Web服务器。它的异步事件驱动模式与传统的请求-响应模式相比，无疑更适合Web应用。<span id="more-2463"></span></p>
<p>有两方面因素更让人看好Node。首先，Google在提升JavaScript性能方面掀起了一场革命。这句话的意思并不是说你随时随地都可以用上最好的JavaScript引擎（尽管这也是我们一个美好的期望）。但可以肯定的是，Google在其他竞争对手还没有上心的情况下，真的把JavaScript性能当成了一回事儿。如此一来，就把Mozilla、Apple、Microsoft、Opera，还有其他浏览器开发商逼到了性能竞赛的跑道上。结果导致我们现在使用的JavaScript引擎较之几年前快了不知道有多少倍，完全有能力运行复杂的大型Web应用。</p>
<p>其次，Node有着庞大的开发人员基础。不管大家在服务器端使用的是什么语言，但在客户端却鲜有不使用JavaScript的。有的人可能是“剪刀加浆糊”式的东拼西凑，有的人则可能用JavaScript做出了高超的Ajax应用，而有的人甚至实现了全功能的应用程序，像Twitter或Gmail。可不管怎么说，JavaScript开发人员的数量无疑是非常庞大的。而<a href="http://www.crockford.com/">Doug Crockford</a>等作者更是极力宣传所有人都应该把JavaScript当成一门严肃正经的编程语言来看待——尽管它还有不少缺点。</p>
<p>当时当下，编写Node应用相对还是个“粗”活儿，毕竟它只是一个底层库。想象一下单纯使用JavaScript写代码，对，就是这种感觉，Node当前还是一个beta版的格局，与Rails或Django这样成熟的Web开发框架还没法比。这种状况无疑会改变。一些轻量级的框架，比如<a href="http://expressjs.com/">Express</a>，已经出现了；我坚信更多基于Node的全功能框架将继续不断涌现。</p>
<p>前面提到过一些几乎完全在浏览器中运行的高级Web应用。那些都已经不算什么新鲜事儿了，<a href="http://en.wikipedia.org/wiki/History_of_Gmail">Gmail多大了</a>？<a href="http://en.wikipedia.org/wiki/Google_Maps#History">Google Maps贵庚了</a>？不过，用JavaScript编写在浏览器中运行的应用的客户端无疑是越来越有吸引力了。HTML5则继续推高了人们对这一趋势的期许。</p>
<h2>HTML5就是JavaScript</h2>
<p>我不知道已经说过多少次了，HTML5实际上并没有多少与HTML有关，它其实就是JavaScript。HTML本身有什么变化？不过一些新标签而已，况且哪个新标签都不难理解。HTML5的威力在于让你能用JavaScript来创建这些标签。假如没有后台代码通过Canvas来创建动画、游戏，或者通过它来实现一些数据的可视化，这个标签也没有大用处。从浏览器开始支持Canvas开始，我已经看到了<a href="http://www.kevs3d.co.uk/dev/asteroids/">Asteroids</a>（行星游戏）的上百个实现，那都是开发人员为熟悉这个新特性所做的练习。有的比较粗糙一些，而有的则极其精美。这些完全都要归功于JavaScript。</p>
<p>由此可见，HTML5并不是以尖括号为特征的标签语言的一次大的改进，其实质是赋予了JavaScript更强大的能力。<a href="http://en.wikipedia.org/wiki/WebGL">WebGL</a>库（当前还羽翼未丰）支持在HTML5的画布中绘制实时的3D图形。HTML5的地理位置支持在浏览器中实现LBS（Location Based Service）应用——这都是手机的基本配置。而持久存储以及离线功能则为开发能与桌面应用媲美，但却在浏览器中运行的全功能应用奠定了基础。目前，就连<a href="http://ajaxian.com/archives/iphone-web-multitouch-javascript-virtual-light-table">增加多点触摸事件的实验性的库</a>也已经出现了。凡此种种，无一不是实实在在的JavaScript特性。HTML5只是为这些高级功能的发挥提供了舞台。</p>
<p>退一步讲，不依赖于HTML5的浏览器端开发库也取得了长足的进步。长久以来，JavaScript一直都是在HTML中实现动态效果的不二之选。可两个问题迟迟得不到解决：一是浏览器兼容性问题，二是直接操作DOM太麻烦。<a href="http://jquery.com/">jQuery</a>让这两个问题霎那间消失得无影无踪，这个库已经成为现代基于浏览器的客户端开发的基本配置。不过，并非只有jQuery。<a href="http://vis.stanford.edu/protovis/">Protovis</a>、<a href="https://github.com/mbostock/d3">还有D3</a>，都可以让你直接在浏览器中创建复杂的交互性数据可视化效果，有史以来第一次让浏览器成为了展示数据的一个重要媒介。</p>
<h2>JavaScript与数据库，编译器与语言</h2>
<p>就连数据库里都开始广泛使用JavaScript了！当前如火如荼的NoSQL运动的三只领头羊：<a href="http://couchdb.apache.org/">CouchDB</a>、<a href="http://www.mongodb.org/">MongoDB</a>和<a href="http://wiki.basho.com/">Riak</a>，都是“文档数据库”。它们保存的不是表，而是文档。这几个数据库所谓的“文档”，其实就是<a href="http://www.json.org/">JSON</a>文档，而不是Word或Excel。（Riak除了JSON文档，还支持XML和纯文本。）JSON已经成为一种被广泛采用的数据交换格式（所有现代的编程语言几乎全都有解析JSON的库），不过请注意，JSON实际上不就是一种序列化JavaScript对象的格式嘛！因此，虽然你可以在任何语言中使用JSON，但在JavaScript开发中使用它则是再自然不过的事了。况且，JSON 这个格式成为一种跨语言的标准，而不是Python、Ruby或Java等语言的序列化格式，这个事实本身足以说明JavaScript将在更加广阔的舞台上大显身手。还不仅仅如此，上述三个数据库都内置了支持JavaScript查询的能力。未来几年，更多的人都将会惊讶地发现，JavaScript和JSON还会内置到其他应用程序中！</p>
<p>JavaScript时代的大幕才刚刚拉开。在今年的<a href="http://2011.jsconf.us/">JSConf</a>上，一个核心主题就是“JavaScript到JavaScript的编译器”，也被人们看成是未来的一个主要趋势。Google在“编译生成JavaScript代码”方面是首开先河者。据我所知，<a href="http://code.google.com/webtoolkit/">GWT（Google Web Toolkit）</a>应该是通过编译（从Java代码）生成JavaScript代码的第一个框架。以前我对GWT并没有太重视，只是觉得它是一个致力于拯救那些Java程序员的框架，好让他们不必因为（学习）编写JavaScript而浪费时间。可是，GWT在编译过程中对JavaScript做了那么多的优化，简直是太神了。<a href="http://code.google.com/closure/compiler/">Closure</a>就是一个“JavaScript到JavaScript的编译器”，能够实现同样级别的优化。<a href="http://code.google.com/p/traceur-compiler/">Traceur</a>，这是几个星期前才冒出来的一个框架，通过它能够试验JavaScript的新特性，换句话说，它可以把带有实验性语言特性的JavaScript代码编译成可以在所有现代平台中运行的JavaScript代码。</p>
<p>最后，我们也开始看到了当初Java大旗下JVM语言的蓬勃景象：<a href="https://github.com/jashkenas/coffee-script/wiki/List-of-languages-that-compile-to-JS">很多语言都在致力于编译成JavaScript</a>！其中有一些语言比较有意思，像<a href="http://jashkenas.github.com/coffee-script/">Coffeescript</a>和<a href="http://weepy.github.com/kaffeine/">Kaffeine</a>，它们在风格上酷似JavaScript，但更关注弥补JavaScript的一些不够完善的地方。是不是觉得JavaScript的对象模型特有意思，可怎么看怎么有点笨笨滴，有木有？是不是一想到基于原型创建一个实际的对象都需要反反复复地定义这定义那，就望而却步了，有木有？Coffeescript对此作了明显的改进。除了完善对象模型，Coffeescript 还添加了类似列表解析（list comprehensions）的新特性，去掉了大部分花括号。就像在Python中一样，要使用缩进来区分代码块。</p>
<p>未来的Web服务器、取之不尽的客户端库、HTML5、数据库，乃至基于JavaScript的语言——我现在一睁眼看到的就是JavaScript！假如你曾经对JavaScript敬而远之，今年可是该学习它了。没有任何理由，真的，再不学，恐怕你就没机会跟上时代了！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/06/22/time-to-learn-javascript-2463.html/feed</wfw:commentRss>
		<slash:comments>63</slash:comments>
		</item>
		<item>
		<title>我的创业故事：从灵光一现到事业有成</title>
		<link>http://www.cn-cuckoo.com/2011/04/23/a-startup-story-2409.html</link>
		<comments>http://www.cn-cuckoo.com/2011/04/23/a-startup-story-2409.html#comments</comments>
		<pubDate>Sat, 23 Apr 2011 03:22:50 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[创业]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2409</guid>
		<description><![CDATA[原文地址：My startup story: from big idea to thriving business in 8 short years 2003年夏天，我还在打理自己第一个小公司的时候，突然想到一个“好主意”：社交新闻阅读器。有点类似后来的Google阅读器加智能收件箱（Priority Inbox）和社会化推荐。我没日没夜地想着这件事，觉也睡不着了。每天人虽然躺在床上，脑子里一直在苦思冥想怎么把这个东西做出来。我可能是得了“痴心妄想症”了。“这个阅读器一定会大受欢迎！”，我想。这个想法太好了，要是你的话，恐怕你早就拿着它去招徕风险投资了。可我在温莎（Windsor），加拿大呀，硅谷对我来说太远了，再说我那时候还没听说过VC啊投资啊什么的。我只能靠自己，什么事都从头来。 2003年还没有Google阅读器，Fackbook还在娘胎里，RSS新闻源也少得可怜。找不着RSS源？不要紧，我自己来，我可以自己写一个程序，一个网站一个网站地分析，自动采集新闻或者新内容。没有Faccbook提供社交用户资料？这还真是个问题。没有这些用户，还做什么社交新闻阅读器呢。对，没有用户不行，不仅如此，用户太少也不行。看来我这个“好主意”一时半会是不太可能有结果了，为了吸引用户，我决定先做个简单的东西，免费供人们下载。我对计算机通话还有些经验，开发个小桌面应用，显示来电者的ID对我来说不过举手之劳。两个月后，PhoneTray Free呱呱坠地。 2003年我可真是熬过来的。我的第一家创业公司本来还小有成就，但很快就下滑到了破产的边缘。由于我签证上的身份，我也不能给别人做任何咨询。照直说吧，这一年我没挣多少钱，我差一点就把自己的公司扔掉，然后去找一份全职工作。幸好我妻子还有工作，我们也还有点存款，合计了一下，我们决心一块挺过去，我还接着做自己想做的事。 一方面是继续设法实现我的那个“好主意”，另一方面就是我第一家公司的一些琐事。我的PhoneTray Free也有人赏脸，肯下载了。后来，我就陆续收到PhoneTray用户的一些反馈邮件，看得出来，他们都很喜欢我这个小应用。“嘿，我们单位的头儿老给我打骚扰电话”，一封邮件里这么说，“你的程序能帮我把他屏蔽掉吗？”我想“这有什么难的？”于是就在程序里加了屏蔽特定号码的功能。没想到所有人都喜欢这个功能。PhoneTray Free一下子火了。到2004年年底那会儿，每天的下载量达到了几百次，反馈邮件也越来越多。 “我喜欢你的程序，”有一封邮件说，“可我们怎么办，我们是拔号上网用户？我们上网的时候有可能错过重要的电话！”“嗯，这是真的吗？”我想，“还有人拔号上网吗？”我用Google一搜，结果显示美国当时的互联网用户确实还有65%通过拔号的方式上网。等等，呼叫等待（modem-on-hold）和V.92标准不就是解决这个问题的吗？很明显，不是，因为我很快就找到了答案。V.92标准只是规定了如何在硬件上实现来电检测和呼叫等待，并没有对软件或者API作出规定。Windows操作系统也没有内置对呼叫等待的支持。虽然有几家调制解调器制造商也提供了支持呼叫等待的软件，但那些软件都只能在特定的调制解调器上使用。大多数调制解调器还是没有随机附送的呼叫等待软件。可不可以在PhoneTray中增加这个功能，让所有拔号用户都能在上网的时候接听电话？ 噢，这件事可不容易。要实现呼叫等待功能，没有任何标准可以参照。现有的几款软件都是使用内部API直接调用“猫”的驱动程序，当然也没有文档可查。而且不同调制解调器芯片提供商又各自有各自的API。更有甚者，某些提供商不愿自找麻烦，干脆连API都没有。这对我而言，不啻为一个挑战，但是谁不喜欢挑战呢？于是，我翻出了以前学过的讲x86汇编程序的旧书，打开自己一直不离不弃的IDA，开始从头研究调制解调器驱动程序的工作原理。我反汇编了几个驱动程序，弄明白了开发驱动程序的步骤，也知道了自己下一步该怎么做。下一步，我要自己开发一个核心驱动程序，以调制解调器的驱动程序为依托，来监控调制解调器驱动程序的行为。这样，PhoneTray就可以与我自己的驱动程序通信，进而控制调制解调器。 我说过我喜欢挑战，是吗？要是你曾经给哪个调制解调器开发过内核驱动程序，你就能体会到我要为自己这个爱好付出多大代价了。远程内核调试器、BSOD/重启、核心内存转储……，这些事儿忙得我是不亦乐乎，而我的那个“好主意”呢，早被我给抛到爪哇国里去了——至少暂时没时间想它了。为了让我的驱动程序兼容所有调制解调器，我花了5个月时间。但不管怎么说，2004年5月，PhoneTray Dialup的第一个版本终于整装待发了。这可是一个能真正解决人们问题的产品，我打算通过它收点钱。一开始，卖得并不好，而且PhoneTray Dialup本身还有一些bug，但到了2.10版，这个产品就已经非常完善了，销量也随之直线上升。 2004年底的时候，PhoneTray Dialup达到了每月几千的销量，而且还在增长。而我呢，也成了加拿大的永久居民，可以通过开展一些咨询业务挣钱了。我感觉到自己的前途真是一片光明！我接着思考自己那个“好主意”，给别人提供一些咨询，时不时地还升级一下PhoneTray Free和PhoneTray Dialup。就这样，突然有一天，一个小ISP所有人给我发了一封邮件，说是希望为他们的用户提供PhoneTray Dialup。“就是啊！为什么我就没有想到呢？”我心里暗自思忖，“这可是一个全新的市场啊！”很快，我就专门针对ISP定制开发了一个新版本，让他们可以把它当作自己的软件来提供给用户。同时，我也拉来了一个人，帮我一起推销这个新版本。两年后，这个新版本已经授权给了几十家ISP，其中最大一家是沙特阿拉伯的，有30万用户。 我们的生意越做越好，而我要处理的事也越来越多。为此，我妻子辞掉了自己的工作，加入Traysoft来帮我。我的那个好主意呢，我又忘了。眼瞅着拔号上网的经营模式渐渐日薄西山，我还得赶紧想点别的办法好继续赚钱。这些年来，我收到过不少公司发来的邮件，想请我帮他们开发PhoneTray Free的定制版。我没有打算那么做，我想让开发人员能够自己根据需求去自己开发。我把PhoneTray的核心电话功能拿出来，移植到C#上面，又添加了一些必要的东西，最后发布了一个基于.NET的电话功能库（AddTapi.NET）。这个新产品很成功，它的收入占到了我们公司收入的一半以上。PhoneTray呢，也越来越有声色，用不了多久，我们先进的电话管理软件PhoneTray Pro也要问世了。 这就是我8年以来的创业故事，现在我的小公司生机勃勃，养活我和我的家人已经没有问题了。而这个公司最早只不过是一个免费的小应用而已。什么，我的那个“好主意”怎么办？也许有一天我会实现那个梦想的，也许。]]></description>
			<content:encoded><![CDATA[<p style="text-align: right;">原文地址：<a href="http://blog.traysoft.com/2011/04/my_startup_story/">My startup story: from big idea to thriving business in 8 short years</a></p>
<p>2003年夏天，我还在打理自己第一个小公司的时候，突然想到一个“好主意”：社交新闻阅读器。有点类似后来的Google阅读器加智能收件箱（Priority Inbox）和社会化推荐。我没日没夜地想着这件事，觉也睡不着了。每天人虽然躺在床上，脑子里一直在苦思冥想怎么把这个东西做出来。我可能是得了“痴心妄想症”了。“这个阅读器一定会大受欢迎！”，我想。这个想法太好了，要是你的话，恐怕你早就拿着它去招徕风险投资了。可我在温莎（Windsor），加拿大呀，硅谷对我来说太远了，再说我那时候还没听说过VC啊投资啊什么的。我只能靠自己，什么事都从头来。</p>
<p>2003年还没有Google阅读器，Fackbook还在娘胎里，RSS新闻源也少得可怜。找不着RSS源？不要紧，我自己来，我可以自己写一个程序，一个网站一个网站地分析，自动采集新闻或者新内容。没有Faccbook提供社交用户资料？这还真是个问题。没有这些用户，还做什么社交新闻阅读器呢。对，没有用户不行，不仅如此，用户太少也不行。看来我这个“好主意”一时半会是不太可能有结果了，为了吸引用户，我决定先做个简单的东西，免费供人们下载。我对计算机通话还有些经验，开发个小桌面应用，显示来电者的ID对我来说不过举手之劳。两个月后，PhoneTray Free呱呱坠地。</p>
<p>2003年我可真是熬过来的。我的第一家创业公司本来还小有成就，但很快就下滑到了破产的边缘。由于我签证上的身份，我也不能给别人做任何咨询。照直说吧，这一年我没挣多少钱，我差一点就把自己的公司扔掉，然后去找一份全职工作。幸好我妻子还有工作，我们也还有点存款，合计了一下，我们决心一块挺过去，我还接着做自己想做的事。<span id="more-2409"></span></p>
<p>一方面是继续设法实现我的那个“好主意”，另一方面就是我第一家公司的一些琐事。我的PhoneTray Free也有人赏脸，肯下载了。后来，我就陆续收到PhoneTray用户的一些反馈邮件，看得出来，他们都很喜欢我这个小应用。“嘿，我们单位的头儿老给我打骚扰电话”，一封邮件里这么说，“你的程序能帮我把他屏蔽掉吗？”我想“这有什么难的？”于是就在程序里加了屏蔽特定号码的功能。没想到所有人都喜欢这个功能。PhoneTray Free一下子火了。到2004年年底那会儿，每天的下载量达到了几百次，反馈邮件也越来越多。</p>
<p>“我喜欢你的程序，”有一封邮件说，“可我们怎么办，我们是拔号上网用户？我们上网的时候有可能错过重要的电话！”“嗯，这是真的吗？”我想，“还有人拔号上网吗？”我用Google一搜，结果显示美国当时的互联网用户确实还有65%通过拔号的方式上网。等等，呼叫等待（modem-on-hold）和V.92标准不就是解决这个问题的吗？很明显，不是，因为我很快就找到了答案。V.92标准只是规定了如何在硬件上实现来电检测和呼叫等待，并没有对软件或者API作出规定。Windows操作系统也没有内置对呼叫等待的支持。虽然有几家调制解调器制造商也提供了支持呼叫等待的软件，但那些软件都只能在特定的调制解调器上使用。大多数调制解调器还是没有随机附送的呼叫等待软件。可不可以在PhoneTray中增加这个功能，让所有拔号用户都能在上网的时候接听电话？</p>
<p>噢，这件事可不容易。要实现呼叫等待功能，没有任何标准可以参照。现有的几款软件都是使用内部API直接调用“猫”的驱动程序，当然也没有文档可查。而且不同调制解调器芯片提供商又各自有各自的API。更有甚者，某些提供商不愿自找麻烦，干脆连API都没有。这对我而言，不啻为一个挑战，但是谁不喜欢挑战呢？于是，我翻出了以前学过的讲x86汇编程序的旧书，打开自己一直不离不弃的IDA，开始从头研究调制解调器驱动程序的工作原理。我反汇编了几个驱动程序，弄明白了开发驱动程序的步骤，也知道了自己下一步该怎么做。下一步，我要自己开发一个核心驱动程序，以调制解调器的驱动程序为依托，来监控调制解调器驱动程序的行为。这样，PhoneTray就可以与我自己的驱动程序通信，进而控制调制解调器。</p>
<p>我说过我喜欢挑战，是吗？要是你曾经给哪个调制解调器开发过内核驱动程序，你就能体会到我要为自己这个爱好付出多大代价了。远程内核调试器、BSOD/重启、核心内存转储……，这些事儿忙得我是不亦乐乎，而我的那个“好主意”呢，早被我给抛到爪哇国里去了——至少暂时没时间想它了。为了让我的驱动程序兼容所有调制解调器，我花了5个月时间。但不管怎么说，2004年5月，PhoneTray Dialup的第一个版本终于整装待发了。这可是一个能真正解决人们问题的产品，我打算通过它收点钱。一开始，卖得并不好，而且PhoneTray Dialup本身还有一些bug，但到了2.10版，这个产品就已经非常完善了，销量也随之直线上升。</p>
<p>2004年底的时候，PhoneTray Dialup达到了每月几千的销量，而且还在增长。而我呢，也成了加拿大的永久居民，可以通过开展一些咨询业务挣钱了。我感觉到自己的前途真是一片光明！我接着思考自己那个“好主意”，给别人提供一些咨询，时不时地还升级一下PhoneTray Free和PhoneTray Dialup。就这样，突然有一天，一个小ISP所有人给我发了一封邮件，说是希望为他们的用户提供PhoneTray Dialup。“就是啊！为什么我就没有想到呢？”我心里暗自思忖，“这可是一个全新的市场啊！”很快，我就专门针对ISP定制开发了一个新版本，让他们可以把它当作自己的软件来提供给用户。同时，我也拉来了一个人，帮我一起推销这个新版本。两年后，这个新版本已经授权给了几十家ISP，其中最大一家是沙特阿拉伯的，有30万用户。</p>
<p>我们的生意越做越好，而我要处理的事也越来越多。为此，我妻子辞掉了自己的工作，加入Traysoft来帮我。我的那个好主意呢，我又忘了。眼瞅着拔号上网的经营模式渐渐日薄西山，我还得赶紧想点别的办法好继续赚钱。这些年来，我收到过不少公司发来的邮件，想请我帮他们开发PhoneTray Free的定制版。我没有打算那么做，我想让开发人员能够自己根据需求去自己开发。我把PhoneTray的核心电话功能拿出来，移植到C#上面，又添加了一些必要的东西，最后发布了一个基于.NET的电话功能库（AddTapi.NET）。这个新产品很成功，它的收入占到了我们公司收入的一半以上。PhoneTray呢，也越来越有声色，用不了多久，我们先进的电话管理软件PhoneTray Pro也要问世了。</p>
<p>这就是我8年以来的创业故事，现在我的小公司生机勃勃，养活我和我的家人已经没有问题了。而这个公司最早只不过是一个免费的小应用而已。什么，我的那个“好主意”怎么办？也许有一天我会实现那个梦想的，也许。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/04/23/a-startup-story-2409.html/feed</wfw:commentRss>
		<slash:comments>34</slash:comments>
		</item>
		<item>
		<title>2010年美国计算机图书市场报告五：完结篇及数字出版</title>
		<link>http://www.cn-cuckoo.com/2011/02/26/2010-book-market-of-usa-5-2279.html</link>
		<comments>http://www.cn-cuckoo.com/2011/02/26/2010-book-market-of-usa-5-2279.html#comments</comments>
		<pubDate>Sat, 26 Feb 2011 12:43:00 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[出版]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2279</guid>
		<description><![CDATA[http://radar.oreilly.com/2011/02/2010-book-market-5.html Mike Hendrickson 2011-2-23 报告内容 第一部分：市场综述 第二部分：分类市场 第三部分：出版商 第四部分：编程语言 第五部分：完结篇及数字出版 下载完整版（PDF） 在最后这一部分中，我们将对前四部分作一番总结，深入了解一些畅销书作者，同时展示一些电子图书销售的数据，分析一下为什么部分电子图书的市场表现已经超过纸版图书。 首先来对本报告的前四部分作个简单的总结。 总体来看，2010年美国图书市场的总销量比2009年下降了4.54%。技术图书市场下降了6.46%，技术书出版商比其他同行承受了更多的压力。然而，与2009年技术图书市场下降13.05%相比，这已经是一个进步了。市场趋势的季节性特征仍然十分明显，2010年初的市场快速启动，到了6月份开始骤降，到了秋季又恢复正常。在Bookscan Top 3000中，2010年计算机类图书比2009年增加了268种（包括不同版权年份出版的图书），而2009年比2008年增加了260种，相当于年增长率为3.5%。单品平均销量从2009年的39.64册下降到2010年的37.92册。根据数据统计，2010年少出版图书104种，但单品平均销量增加了6册。注意，这些图书的出版年份都是2010年。 移动开发类图书成为市场主力军。Android编程和Android设备（面向用户）类图书都有极大的增长，而iOS和Objective-C的市场份额进一步扩大。Windows 7的表现也相当不俗，重现了原来Windows XP曾经达到过的市场份额（Vista从未达到过）。平板电脑异军突起，成为2010年底的一大亮点。在多款Android平板电脑即将上市的背景下，有理由相信平板电脑图书市场一定会继续高歌猛进。另一方面，网页制作、Web设计工具以及Web编程类图书都明显下滑。由于Snow Leopard的突破没有前几个版本的OS那么大，Mac OS图书市场多年来首次出现下滑。Flash和Silverlight的市场也有较大幅度的下滑，原因是HTML5能够提供类似的功能，导致了它们的市场关注度下降。 从出版商的角度看，O&#8217;Reilly在2010年底爬升至第二位，位居Wiley之后，以微弱优势领先于Pearson。O&#8217;Reilly和Dummies这两家出版公司涉足的出版门类最为多样，而它们在6大技术门类中的表现也非常令人瞩目。至于最受欢迎的图书，从码洋来看是PMP Exam Prep, Sixth Edition: Rita&#8217;s Course in a Book for Passing the PMP Exam，从销量来看是Windows 7 For Dummies。2009和2010年的最热门语言都是Java，JavaScript和VBA在2010年也增长了不少。简单的回顾到此为止。 下面我们把注意力转移到出版背后的最重要因素——作者上面。作者是实际创造各种内容的人。作者的类型也很多。有的实际上很像小型的出版工作室，让其他“合著者”完成大部分写书工作；有的则从头到尾大包大揽，从编辑、写作、测试到编写代码，全都自己干，等书出版之后，还会参与推广、活动和销售。后一类作者的活动决定了我们所说的“作者平台”。有些作者凭自己的名气或自己的全职工作，拥有一个稳定的平台。而另外一些作者则不得不苦心经营，不断培养自己的平台。在我们数据库中，最成功的作者都解决了前期内容生产和后期发行销售的问题。在对前15位作者的数据（实际上，他们书的销量和码洋还会更多）进行分析的基础上，可以得到下列两张柱形图，其中展示的是2004-2010年的图书总销量和总码洋情况。 总销量 总码洋 2010年，Paul McFedries的名字出现在了58本不同的图书上（出版日期从2001年到2010年），反映在我们的数据中，这些书的总销量为112,152册。他的书在2010年卖得最好，2010年他有12本新书出版，这是我们的数据。他的总销量比David Pogue多20,000册，后者在我们的数据中有13本书出版，总销量为92,000册。从单品平均销量看，Pogue是7,000册，而McFedries只有1,900册。销量前5名中的其他三位作者是：Andy Rathbone、Greg Harvey和Dan Gookin。如果从码洋的角度来看，前5名的作者从多到少分别是：Paul McFedries、David Pogue、Andy Rathbone、Rita Mulcahy和Scott Kelby。 电子书发行与销售 好了，说完了2010年纸质书的销售情况，接下来就看一看至少已经部分脱离传统发行渠道的电子书的发行情况。下面这张表显示的是2008年的电子书销售情况，数据来源于AAP（Association [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://radar.oreilly.com/2011/02/2010-book-market-5.html">http://radar.oreilly.com/2011/02/2010-book-market-5.html</a><br />
Mike Hendrickson<br />
2011-2-23</p>
<div style="float:right; border:1px solid #ddd;border-width:0 0 1px 1px;padding:0 .5em;margin:0 0 0 .5em;">
<p style="font-weight:bold;">报告内容</p>
<ol>
<li><a href="http://www.cn-cuckoo.com/2011/02/26/2010-book-market-of-usa-1-2287.html">第一部分：市场综述</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-2-2329.html">第二部分：分类市场</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-3-2270.html">第三部分：出版商</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-4-2265.html">第四部分：编程语言</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/25/2010-book-market-of-usa-5-2279.html">第五部分：完结篇及数字出版</a></li>
</ol>
<div style="text-align:center;">
<a href="http://u.115.com/file/f661fc6f86"><br />
<img class="colorbox-2279"  style="border:1px solid #eee;" src="http://ww1.sinaimg.cn/large/61baa48djw6deqves2ryvj.jpg"/><br />
</a><br />
<a href="http://u.115.com/file/f661fc6f86">下载完整版（PDF）</a>
</div>
</div>
<p>在最后这一部分中，我们将对前四部分作一番总结，深入了解一些畅销书作者，同时展示一些电子图书销售的数据，分析一下为什么部分电子图书的市场表现已经超过纸版图书。</p>
<p>首先来对本报告的前四部分作个简单的总结。</p>
<p>总体来看，2010年美国图书市场的总销量比2009年下降了4.54%。技术图书市场下降了6.46%，技术书出版商比其他同行承受了更多的压力。然而，与2009年技术图书市场下降13.05%相比，这已经是一个进步了。市场趋势的季节性特征仍然十分明显，2010年初的市场快速启动，到了6月份开始骤降，到了秋季又恢复正常。在Bookscan Top 3000中，2010年计算机类图书比2009年增加了268种（包括不同版权年份出版的图书），而2009年比2008年增加了260种，相当于年增长率为3.5%。单品平均销量从2009年的39.64册下降到2010年的37.92册。根据数据统计，2010年少出版图书104种，但单品平均销量增加了6册。注意，这些图书的出版年份都是2010年。</p>
<p>移动开发类图书成为市场主力军。Android编程和Android设备（面向用户）类图书都有极大的增长，而iOS和Objective-C的市场份额进一步扩大。Windows 7的表现也相当不俗，重现了原来Windows XP曾经达到过的市场份额（Vista从未达到过）。平板电脑异军突起，成为2010年底的一大亮点。在多款Android平板电脑即将上市的背景下，有理由相信平板电脑图书市场一定会继续高歌猛进。另一方面，网页制作、Web设计工具以及Web编程类图书都明显下滑。由于Snow Leopard的突破没有前几个版本的OS那么大，Mac OS图书市场多年来首次出现下滑。Flash和Silverlight的市场也有较大幅度的下滑，原因是HTML5能够提供类似的功能，导致了它们的市场关注度下降。</p>
<p>从出版商的角度看，O&#8217;Reilly在2010年底爬升至第二位，位居Wiley之后，以微弱优势领先于Pearson。O&#8217;Reilly和Dummies这两家出版公司涉足的出版门类最为多样，而它们在6大技术门类中的表现也非常令人瞩目。至于最受欢迎的图书，从码洋来看是<em>PMP Exam Prep, Sixth Edition: Rita&#8217;s Course in a Book for Passing the PMP Exam</em>，从销量来看是<em>Windows 7 For Dummies</em>。2009和2010年的最热门语言都是Java，JavaScript和VBA在2010年也增长了不少。简单的回顾到此为止。<br />
<span id="more-2279"></span><br />
下面我们把注意力转移到出版背后的最重要因素——作者上面。作者是实际创造各种内容的人。作者的类型也很多。有的实际上很像小型的出版工作室，让其他“合著者”完成大部分写书工作；有的则从头到尾大包大揽，从编辑、写作、测试到编写代码，全都自己干，等书出版之后，还会参与推广、活动和销售。后一类作者的活动决定了我们所说的“作者平台”。有些作者凭自己的名气或自己的全职工作，拥有一个稳定的平台。而另外一些作者则不得不苦心经营，不断培养自己的平台。在我们数据库中，最成功的作者都解决了前期内容生产和后期发行销售的问题。在对前15位作者的数据（实际上，他们书的销量和码洋还会更多）进行分析的基础上，可以得到下列两张柱形图，其中展示的是2004-2010年的图书总销量和总码洋情况。</p>
<h3>总销量</h3>
<p><img class="colorbox-2279"  src="http://radar.oreilly.com/upload/2011/02/Author2004_2010Units.jpg"/></p>
<h3>总码洋</h3>
<p><img class="colorbox-2279"  src="http://radar.oreilly.com/upload/2011/02/Author2004_2010Dollars.jpg"/></p>
<p>2010年，Paul McFedries的名字出现在了58本不同的图书上（出版日期从2001年到2010年），反映在我们的数据中，这些书的总销量为112,152册。他的书在2010年卖得最好，2010年他有12本新书出版，这是我们的数据。他的总销量比David Pogue多20,000册，后者在我们的数据中有13本书出版，总销量为92,000册。从单品平均销量看，Pogue是7,000册，而McFedries只有1,900册。销量前5名中的其他三位作者是：Andy Rathbone、Greg Harvey和Dan Gookin。如果从码洋的角度来看，前5名的作者从多到少分别是：Paul McFedries、David Pogue、Andy Rathbone、Rita Mulcahy和Scott Kelby。</p>
<h2>电子书发行与销售</h2>
<p>好了，说完了2010年纸质书的销售情况，接下来就看一看至少已经部分脱离传统发行渠道的电子书的发行情况。下面这张表显示的是2008年的电子书销售情况，数据来源于AAP（Association of American Publishers，美国出版商协会）。最令人惊讶的是增长规模，从2008年1800万美元，到2010年1.2亿美元，增长了近10倍。随着各种平板阅读设备的纷纷上市，电子图书将会迎来其黄金时代。即使市场增速不变，2013的电子图书市场规模也将到达10亿美元。</p>
<h3>2008年电子书市场</h3>
<p><img class="colorbox-2279"  src="http://radar.oreilly.com/Trade%20Stats_04_08.jpg"/></p>
<h3>2010年电子书市场</h3>
<p><img class="colorbox-2279"  src="http://radar.oreilly.com/upload/2011/02/Trade%20Stats_Master_10Q3_fnl.jpg"/></p>
<p>AAP对自己发布的数据给出了如下说明。</p>
<ul>
<li>以上数据仅代表美国的收入。</li>
<li>以上数据仅代表通过批发渠道交易的电子书销售额。考虑到批发折扣，零售渠道的销售额可能会是以上数字的两倍左右。</li>
<li>以上数据仅代表12到15家出版商提供的交易额数据。</li>
<li>以上数据不包含图书馆、教育及专业类电子书销售。</li>
<li>以上数据反映的是出版商的批发收入（出版实洋）。</li>
<li>报告中所称的电子书是指“所有以电子形式通过Internet交付或传输到手持设备的图书”。</li>
<li>相关数据由IDPF和AAP从2006年第一季度开始共同收集。</li>
</ul>
<p>根据以上说明，我个人认为，电子书交易到2013年达到10亿美元的规模还是保守的估计。</p>
<p>下面的两张图展示了Safari收入的增长情况及oreilly.com直接销售的情况。展示这两张图的原因是，纸质图书的内容会以不同的数字形式出现。Safari本身是一个订阅服务，拥有50多万用户，其主要业务是B2B服务，定位于让世界各大公司的开发人员能够访问Safari Books Online。为此，一个显著的区别就是那些以普通读者为对象的书，以及很多数字媒体类图书，在Safari中的表现都不佳。从下图可以看出，我们Safari中的内容销售每年都能实现稳定的增长。</p>
<h3>Safari中O&#8217;Reilly图书的增长情况</h3>
<p><img class="colorbox-2279"  src="http://radar.oreilly.com/upload/2011/02/safariRevenueGrowth.jpg"/></p>
<p>下面这张图更有意思一些，因为它反映了真实的市场情况。其中显示的是2010年通过oreilly.com卖出的纸质书、电子书和视频的数据。这三类出版物的内容大部分都是相同的，其中电子书无非就是我们纸版书的数字版而已。但从图中可以看出，我们电子书的销量占到总销量的88%，占到了总码洋的79%。真正值得注意的是，我们数字产品的市场增长速度超过了纸质书的市场下滑速度。这表明我们不同形式产品的销售并没有此消彼涨，而是出现了互为补充的良好局面。</p>
<h3>oreilly.com销售多种形式产品的情况</h3>
<p><img class="colorbox-2279"  src="http://radar.oreilly.com/upload/2011/02/oreilly_com.jpg"/></p>
<p>第三张图展示了出版物形态变迁的本质，其中的百分比值表示每种特定出版物形态，以及相应年度的销售量。我个人认为，这张图显示了的出版业转型的一个趋势。纸质书慢慢减少，EPUB、Mobi及Ebooks的快速上升速度比纸质书下降的速度更快。PDF的销量在下降，想一想，也在情理之中。O&#8217;Reilly以前只提供两种图书产品形式：纸版书和PDF版。而现在，我们提供读者需要的任何一种形式。因此，在提供Mobi、EPUB和Ebook的情况下，不那么有用的PDF在显著下降。</p>
<h3>O&#8217;Reilly各类产品的销售情况</h3>
<p><img class="colorbox-2279"  src="http://radar.oreilly.com/upload/2011/02/ormDistro.jpg"/></p>
<p>再强调一下，这些数据来自O&#8217;Reilly和oreilly.com，不一定能反映整个计算机图书市场。我从其他出版商那里了解过，比如Prags的Dave Thomas曾告诉我，以上分布情况与他们的出版模式大致相同，有时候可能会比他们落后一些。一些成长型的小出版商致力于数字出版的积极性明显高于纸质出版，而对于我们这种具有相当规模的传统纸质出版模式的出版商来说，数字出版的增长速度也会超过纸质出版。在纸质出版前面冠以“传统”两个字好像有点新鲜，但这种说法的确是名副其实。难道这表明计算机图书市场真的要发生质的变化了吗？</p>
<p>感谢您看完这几篇报告。如果您想再看到某些细节（或者弄得更明白一点），请跟我联系，我一定尽力提供帮助。我打算今年在Twitter上摘录对这几篇报告中内容的更新信息。这些消息将通过@mikehatora发布，然后可能会由@oreillymedia转发，敬请关注。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/02/26/2010-book-market-of-usa-5-2279.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>2010年美国计算机图书市场报告四：编程语言</title>
		<link>http://www.cn-cuckoo.com/2011/02/25/2010-book-market-of-usa-4-2265.html</link>
		<comments>http://www.cn-cuckoo.com/2011/02/25/2010-book-market-of-usa-4-2265.html#comments</comments>
		<pubDate>Fri, 25 Feb 2011 07:53:45 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[出版]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2265</guid>
		<description><![CDATA[http://radar.oreilly.com/2011/02/2010-book-market-4.html Mike Hendrickson 2011-2-21 报告内容 第一部分：市场综述 第二部分：分类市场 第三部分：出版商 第四部分：编程语言 第五部分：完结篇及数字出版 下载完整版（PDF） 这是《2010年计算机图书市场报告》的第四部分，我们来看一看编程语言市场，对各种编程语言作一备盘点。 与2009年相比，2010年编程语言市场总体下滑，幅度为-6.27%。从销售总量看，2009年图书共售出6,303,125册，而2010年共售出5,931,452册，减少了371,673册。其中，Java语言类图书销量增长最多，2010年比2009年多售出28,633册，而PHP语言类图书销量降低最多，比上一年减少了38,614册。 在盘点各种语言之前，有必要先明确一下“语言维度”这个概念。语言维度是我们对语言类图书进行分类的一个标准，这个标准就是看图书中的代码示例是使用什么语言编写的。比如，Flash Programming with Java这本书，它的基本分类是Flash，但按语言维度分，则属于Java类。同样，Head First Design Patterns中的示例是用Java写的，因此按语言维度分，这本书也属于Java类。 综合来看，2009和2010年编程语言类图书的市场表现是最糟糕的。下面这张图中不包括谈方法论的、项目管理的、消费者操作系统的，以及其他不涉及具体语言的图书。因此，我们现在对编程语言类图书的分析，与本报告第一部分的总体评价出发点是不一样的。这张图显示的是以周为单位的所有语言类图书的市场表现情况，其中2009年和2010年与其他年份比，依旧是最差的。 2008年，我们在报告中分析C#超过Java成为了最热门的语言。但时隔不久，Java图书就在2009年发力反弹，终于在2010年王者归来，重新登上编程语言图书第一位的宝座。通过下面这张2010年最畅销语言的柱状图可以看出，Java在这些语言里遥遥领先，而Objective-C则迅速攀升至第三位，仅次于C#。 2010年市场份额 再看看下面这张图，哪些语言的图书对2004年至2010年的码洋贡献最大可以一目了然。比较新的语言，或者说“时尚”的语言，由于时间太短，它们市场表现如何一时可能还看不出来。这张表基本上根据这些语言在相应时间段内的实际销量绘制的。其中，前10位的图书7年总共销售7,655,365册，后10位的图书7年总共销售1,919,691册。从份额来看，前10位大约占80%。从这些语言7年来的趋势看，C#在2009年之前一直是稳步增长的，Java则正好相反，2009年之前它的市场表现是越来越差的。2009到2010年，除了Java、VBA、VBScript、SAS、JavaScript、C++和C呈现上升态势，其他13门语言的销量都减少了。 编程语言类图书市场表现的Treemap 在上面这张Treemp图中，我们对比的是2010年第四季度和2009年第四季度，两年的同期相比，可以看到不少亮绿色区域、几块深绿色区域，黑色及红色区域也占相当面积。Objective-C之所以会下降12%，因为它在2009年的表现实在太好，很难维持。但由于它的在图中面积很可观，结果就让这张图显得有些压抑了。 深入数据分析之前，我们先了解一下这些语言的总体情况。首先，对它们进行了分组，分组依据是它们在2004-2010年间的总销量。通过下面这张表就可以看出来，只有Mid-Majo组的销量在2010年是增长的，其他组的销量都在减少。而Mid-Major组中销售增长幅度最大的是R。有意思的是，像R这样的统计分析语言基本上都在增长（和Strata大会的结论类似）。实际上，R、SAS、Matlab、Labview、Mathematica以及SPSS这些语言加在一起，总共增长了49,504册，增幅达到了102.87%。也许是因为Hal Varian关于统计将成为“最有前途的工作”的论调，刺激了开发人员都去学习这些语言。 Group Unit Range Y2010 Units Y2009 Units Y2010 # Y2009 # 10MketShar 9MketShar Large 50,000 &#151 200,000 1,051,945 1,069,762 1,590 1,433 75.96% 75.00% Major 10,000 &#151 49,000 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://radar.oreilly.com/2011/02/2010-book-market-4.html">http://radar.oreilly.com/2011/02/2010-book-market-4.html</a><br />
Mike Hendrickson<br />
2011-2-21</p>
<div style="float:right; border:1px solid #ddd;border-width:0 0 1px 1px;padding:0 .5em;margin:0 0 0 .5em;">
<p style="font-weight:bold;">报告内容</p>
<ol>
<li><a href="http://www.cn-cuckoo.com/2011/02/26/2010-book-market-of-usa-1-2287.html">第一部分：市场综述</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-2-2329.html">第二部分：分类市场</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-3-2270.html">第三部分：出版商</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-4-2265.html">第四部分：编程语言</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/25/2010-book-market-of-usa-5-2279.html">第五部分：完结篇及数字出版</a></li>
</ol>
<div style="text-align:center;">
<a href="http://u.115.com/file/f661fc6f86"><br />
<img class="colorbox-2265"  style="border:1px solid #eee;" src="http://ww1.sinaimg.cn/large/61baa48djw6deqves2ryvj.jpg"/><br />
</a><br />
<a href="http://u.115.com/file/f661fc6f86">下载完整版（PDF）</a>
</div>
</div>
<p>这是《2010年计算机图书市场报告》的第四部分，我们来看一看编程语言市场，对各种编程语言作一备盘点。</p>
<p>与2009年相比，2010年编程语言市场总体下滑，幅度为-6.27%。从销售总量看，2009年图书共售出6,303,125册，而2010年共售出5,931,452册，减少了371,673册。其中，Java语言类图书销量增长最多，2010年比2009年多售出28,633册，而PHP语言类图书销量降低最多，比上一年减少了38,614册。</p>
<p>在盘点各种语言之前，有必要先明确一下“语言维度”这个概念。语言维度是我们对语言类图书进行分类的一个标准，这个标准就是看图书中的代码示例是使用什么语言编写的。比如，Flash Programming with Java这本书，它的基本分类是Flash，但按语言维度分，则属于Java类。同样，Head First Design Patterns中的示例是用Java写的，因此按语言维度分，这本书也属于Java类。</p>
<p>综合来看，2009和2010年编程语言类图书的市场表现是最糟糕的。下面这张图中不包括谈方法论的、项目管理的、消费者操作系统的，以及其他不涉及具体语言的图书。因此，我们现在对编程语言类图书的分析，与本报告第一部分的总体评价出发点是不一样的。这张图显示的是以周为单位的所有语言类图书的市场表现情况，其中2009年和2010年与其他年份比，依旧是最差的。</p>
<p><img class="colorbox-2265"  src="http://radar.oreilly.com/upload/2011/02/AllYearsLanguages.jpg"/></p>
<p>2008年，我们在报告中分析C#超过Java成为了最热门的语言。但时隔不久，Java图书就在2009年发力反弹，终于在2010年王者归来，重新登上编程语言图书第一位的宝座。通过下面这张2010年最畅销语言的柱状图可以看出，Java在这些语言里遥遥领先，而Objective-C则迅速攀升至第三位，仅次于C#。<br />
<span id="more-2265"></span></p>
<h2>2010年市场份额</h2>
<p><img class="colorbox-2265"  src="http://radar.oreilly.com/upload/2011/02/Top20_langs_2010.jpg"/></p>
<p>再看看下面这张图，哪些语言的图书对2004年至2010年的码洋贡献最大可以一目了然。比较新的语言，或者说“时尚”的语言，由于时间太短，它们市场表现如何一时可能还看不出来。这张表基本上根据这些语言在相应时间段内的实际销量绘制的。其中，前10位的图书7年总共销售7,655,365册，后10位的图书7年总共销售1,919,691册。从份额来看，前10位大约占80%。从这些语言7年来的趋势看，C#在2009年之前一直是稳步增长的，Java则正好相反，2009年之前它的市场表现是越来越差的。2009到2010年，除了Java、VBA、VBScript、SAS、JavaScript、C++和C呈现上升态势，其他13门语言的销量都减少了。</p>
<p><img class="colorbox-2265"  src="http://radar.oreilly.com/upload/2011/02/AllYearsT20Langs.jpg"/></p>
<h2>编程语言类图书市场表现的Treemap</h2>
<p><img class="colorbox-2265"  src="http://radar.oreilly.com/upload/2011/02/prog_lang_tree.jpg"/></p>
<p>在上面这张Treemp图中，我们对比的是2010年第四季度和2009年第四季度，两年的同期相比，可以看到不少亮绿色区域、几块深绿色区域，黑色及红色区域也占相当面积。Objective-C之所以会下降12%，因为它在2009年的表现实在太好，很难维持。但由于它的在图中面积很可观，结果就让这张图显得有些压抑了。</p>
<p>深入数据分析之前，我们先了解一下这些语言的总体情况。首先，对它们进行了分组，分组依据是它们在2004-2010年间的总销量。通过下面这张表就可以看出来，只有Mid-Majo组的销量在2010年是增长的，其他组的销量都在减少。而Mid-Major组中销售增长幅度最大的是R。有意思的是，像R这样的统计分析语言基本上都在增长（和Strata大会的结论类似）。实际上，R、SAS、Matlab、Labview、Mathematica以及SPSS这些语言加在一起，总共增长了49,504册，增幅达到了102.87%。也许是因为Hal Varian关于统计将成为“最有前途的工作”的论调，刺激了开发人员都去学习这些语言。</p>
<table border=1 cellpadding=1 cellspacing=1>
<tr>
<td>Group</td>
<td>Unit Range</td>
<td>Y2010 Units</td>
<td>Y2009 Units</td>
<td>Y2010 #</td>
<td>Y2009 #</td>
<td>10MketShar</td>
<td>9MketShar</td>
</tr>
<tr>
<td>Large</td>
<td>50,000 &#151 200,000</td>
<td align=right>1,051,945</td>
<td align=right>1,069,762</td>
<td align=right>1,590</td>
<td align=right>1,433</td>
<td align=right>75.96%</td>
<td align=right>75.00%</td>
</tr>
<tr>
<td>Major</td>
<td>10,000 &#151 49,000</td>
<td align=right>227,306</td>
<td align=right>254,587</td>
<td align=right>450</td>
<td align=right>456</td>
<td align=right>16.41%</td>
<td align=right>17.85%</td>
</tr>
<tr>
<td>Mid-Major</td>
<td>3,000 &#151 9,999</td>
<td align=right>53,152</td>
<td align=right>44,909</td>
<td align=right>104</td>
<td align=right>85</td>
<td align=right>3.84%</td>
<td align=right>3.15%</td>
</tr>
<tr>
<td>Mid-Minor</td>
<td>1,682 &#151 2,999</td>
<td align=right>20,818</td>
<td align=right>20,965</td>
<td align=right>61</td>
<td align=right>58</td>
<td align=right>1.50%</td>
<td align=right>1.47%</td>
</tr>
<tr>
<td>Minor</td>
<td>1,000 &#151 1,680</td>
<td align=right>13,000</td>
<td align=right>15,517</td>
<td align=right>46</td>
<td align=right>31</td>
<td align=right>0.94%</td>
<td align=right>1.09%</td>
</tr>
<tr>
<td>Linelist</td>
<td>399 &#151 999</td>
<td align=right>6,299</td>
<td align=right>6,350</td>
<td align=right>25</td>
<td align=right>19</td>
<td align=right>0.45%</td>
<td align=right>0.45%</td>
</tr>
<tr>
<td>TheRest</td>
<td>< 399</td>
<td align=right>3,370</td>
<td align=right>6,368</td>
<td align=right>49</td>
<td align=right>43</td>
<td align=right>0.24%</td>
<td align=right>0.45%</td>
</tr>
</table>
<p>为了以分组方式更清楚地展示这些信息，我们使用了这些表头对这些语言进行了分类。</p>
<table border="1">
<tr>
<td align="center">*<strong>Large</strong>*</td>
<td colspan="2" align="center"><b>U N I T S</b></td>
<td colspan="2" align="center"><b>T I T L E S</b></td>
<td colspan="2" align="center"><b>M A R K E T S H A R E</b></td>
</tr>
<tr>
<td height="13" align="center"><b>1.</b> Language</td>
<td align="center"><b>2.</b> 2010 Units</td>
<td align="center"><b>3.</b> 2009 Units</td>
<td align="center"><b>4.</b> 2010 Titles</td>
<td align="center"><b>5.</b> 2009 Titles</td>
<td align="center"><b>6.</b> 10Mkt Share</td>
<td align="center"><b>7.</b> 09Mkt Share</td>
</tr>
</table>
<ol>
<li>语言的名字或简写形式</li>
<li>2010销售量</li>
<li>2009销售量</li>
<li>2010年Bookscan监控销量前3000名中的品种数</li>
<li>2009年Bookscan监控销量前3000名中的品种数</li>
<li>2010年市场份额</li>
<li>2009年市场份额</li>
</ol>
<p>下面这个表展示的是Large语言组的数据。排名前10位的语言中，在Java止跌回涨的带领下，有5种在2010年是增长的。本报告前几部分也介绍过，Java语言类图书的销量一直在持续减少，到2009年甚至延续到2010年都是如此。难道是Android开发为Java的复活注射了强心剂？而Objective-C虽然在2010年销量减少，但依旧跻身前10之列。此前Objective-C的排名差不多是在第20位左右。JavaScript保持了稳步增长态势，作为Web编程中最常用也是最重要的一门语言，它的地位如今已经不可动摇了。</p>
<h3>Large组的编程语言（2010年销量在50,000-195,000之间）</h3>
<table border="1">
<tr>
<td align="center"><b>*Large*</b></td>
<td colspan="2" align="center"><b>U N I T S</b></td>
<td colspan="2" align="center"><b>T I T L E S</b></td>
<td colspan="2" align="center"><b>M A R K E T S H A R E</b></td>
</tr>
<tr>
<td align="center"><b>Language</b></td>
<td align="center"><b>2010 Units</b></td>
<td align="center"><b>2009 Units</b></td>
<td align="center"><b>2010 Titles</b></td>
<td align="center"><b>2009 Titles</b></td>
<td align="center"><b>10Mkt Share</b></td>
<td align="center"><b>09Mkt Share</b></td>
</tr>
<tr>
<td>Java</td>
<td align="right">194,520</td>
<td align="right">165,887</td>
<td align="right">361</td>
<td align="right">332</td>
<td align="right">13.90%</td>
<td align="right">11.54%</td>
</tr>
<tr>
<td>C#</td>
<td align="right">153,469</td>
<td align="right">156,043</td>
<td align="right">263</td>
<td align="right">230</td>
<td align="right">10.97%</td>
<td align="right">10.86%</td>
</tr>
<tr>
<td>Objective C</td>
<td align="right">136,711</td>
<td align="right">141,608</td>
<td align="right">89</td>
<td align="right">51</td>
<td align="right">9.77%</td>
<td align="right">9.85%</td>
</tr>
<tr>
<td>JavaScript</td>
<td align="right">131,850</td>
<td align="right">115,107</td>
<td align="right">169</td>
<td align="right">157</td>
<td align="right">9.42%</td>
<td align="right">8.01%</td>
</tr>
<tr>
<td>PHP</td>
<td align="right">106,952</td>
<td align="right">145,566</td>
<td align="right">163</td>
<td align="right">152</td>
<td align="right">7.64%</td>
<td align="right">10.13%</td>
</tr>
<td>C/C++</td>
<td align="right">94,268</td>
<td align="right">93,067</td>
<td align="right">192</td>
<td align="right">184</td>
<td align="right">6.74%</td>
<td align="right">6.48%</td>
</tr>
<tr>
<td>VBA</td>
<td align="right">61,108</td>
<td align="right">48,507</td>
<td align="right">68</td>
<td align="right">58</td>
<td align="right">4.37%</td>
<td align="right">3.38%</td>
</tr>
<tr>
<td>ActionScript</td>
<td align="right">60,578</td>
<td align="right">83,017</td>
<td align="right">96</td>
<td align="right">85</td>
<td align="right">4.33%</td>
<td align="right">5.78%</td>
</tr>
<tr>
<td>Python</td>
<td align="right">58,905</td>
<td align="right">60,700</td>
<td align="right">94</td>
<td align="right">84</td>
<td align="right">4.21%</td>
<td align="right">4.22%</td>
</tr>
<tr>
<td>SQL</td>
<td align="right">53,584</td>
<td align="right">60,260</td>
<td align="right">95</td>
<td align="right">100</td>
<td align="right">3.83%</td>
<td align="right">4.19%</td>
</tr>
</table>
<p>下面这个表里列出Large组语言中卖得最好的几本书。而且，不管从销量看，还是从码洋看，这几本书的排名都是相同的，只不过按码洋排名，WordPress第5的位置会被Addison-Wesley的PHP and MySQL Web Development取代。</p>
<table border="1">
<tr>
<td>O&#8217;Reilly</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="bit.ly/egQEmN" title="Learning PHP, MySQL, and JavaScript, First Edition">Learning PHP, MySQL, and JavaScript, First Edition</a></span></td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/eTKrN9" title="Head First Java, Second Edition">Head First Java, Second Edition</a></span></td>
</tr>
<tr>
<td>Wrox</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/hAcCfA">Professional Android 2 Application Development</a></span></td>
</tr>
<tr>
<td>Addison-Wesley</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/eP4pmm">Programming in Objective-C 2.0</a></span></td>
</tr>
<tr>
<td>Dummies</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/dVCrAF">WordPress for Dummies (covers PHP)</a></span></td>
</tr>
</table>
<p>在Major语言组中，C、Powershell、ShellScript和VBScript都在增长。总体来看，这些语言的图书2010年比2009年少销售27,000册，相当于Major组语言总体减少量的12%。</p>
<h3>Major组的编程语言（2010年销量在10,000-49,999之间）</h3>
<table border="1">
<tr>
<td align="center"><b>*Major*</b></td>
<td colspan="2" align="center"><b>U N I T S</b></td>
<td colspan="2" align="center"><b>T I T L E S</b></td>
<td colspan="2" align="center"><b>M A R K E T S H A R E</b></td>
</tr>
<tr>
<td align="center"><b>Language</b></td>
<td align="center"><b>2010 Units</b></td>
<td align="center"><b>2009 Units</b></td>
<td align="center"><b>2010 Titles</b></td>
<td align="center"><b>2009 Titles</b></td>
<td align="center"><b>10Mkt Share</b></td>
<td align="center"><b>09Mkt Share</b></td>
</tr>
<tr>
<td>.NET Languages</td>
<td align="right">44,958</td>
<td align="right">57,286</td>
<td align="right">82</td>
<td align="right">78</td>
<td align="right">3.25%</td>
<td align="right">4.02%</td>
</tr>
<tr>
<td>Visual Basic</td>
<td align="right">42,225</td>
<td align="right">55,574</td>
<td align="right">88</td>
<td align="right">94</td>
<td align="right">3.05%</td>
<td align="right">3.90%</td>
</tr>
<tr>
<td>C</td>
<td align="right">36,638</td>
<td align="right">34,820</td>
<td align="right">91</td>
<td align="right">83</td>
<td align="right">2.65%</td>
<td align="right">2.44%</td>
</tr>
<tr>
<td>Ruby</td>
<td align="right">20,004</td>
<td align="right">29,977</td>
<td align="right">48</td>
<td align="right">63</td>
<td align="right">1.44%</td>
<td align="right">2.10%</td>
</tr>
<tr>
<td>Powershell</td>
<td align="right">18,652</td>
<td align="right">12,124</td>
<td align="right">26</td>
<td align="right">19</td>
<td align="right">1.35%</td>
<td align="right">0.85%</td>
</tr>
<tr>
<td>Transact SQL</td>
<td align="right">17,507</td>
<td align="right">17,601</td>
<td align="right">28</td>
<td align="right">29</td>
<td align="right">1.26%</td>
<td align="right">1.23%</td>
</tr>
<tr>
<td>Perl</td>
<td align="right">15,606</td>
<td align="right">20,030</td>
<td align="right">32</td>
<td align="right">34</td>
<td align="right">1.13%</td>
<td align="right">1.40%</td>
</tr>
<tr>
<td>Pl/Sql</td>
<td align="right">10,670</td>
<td align="right">10,974</td>
<td align="right">24</td>
<td align="right">26</td>
<td align="right">0.77%</td>
<td align="right">0.77%</td>
</tr>
<tr>
<td>Shell Script</td>
<td align="right">10,720</td>
<td align="right">7,482</td>
<td align="right">20</td>
<td align="right">17</td>
<td align="right">0.77%</td>
<td align="right">0.52%</td>
</tr>
<tr>
<td>VBScript</td>
<td align="right">10,326</td>
<td align="right">8,719</td>
<td align="right">11</td>
<td align="right">13</td>
<td align="right">0.74%</td>
<td align="right">0.61%</td>
</tr>
</table>
<p>以下是Major组语言中销量前5名的图书。</p>
<table border="1">
<tr>
<td>Prentice Hall</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/dXep8h">C Programming Language</a></span></td>
</tr>
<tr>
<td>Prentice Hall</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/g7mC4r">Practical Guide to Linux Commands, Editors, and Shell Programming</a></span></td>
<tr>
<td>O&#8217;Reilly</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/iesPCX">Learning Perl, 5th Edition</a></span></td>
</tr>
<tr>
<td>Morgan Kaufman</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/fTMRsp">Programming Massively Parallel Processors: A Hands-on Approach (C language)</a></span></td>
</tr>
<td>Pragmatic</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/eSQSBP">Agile Web Development with Rails, Third Edition</a></span></td>
</tr>
</table>
<h3>Mid-Major组的编程语言（2010年销量在3,000-9,999之间）</h3>
<p>在这个组中，统计分析语言的表现非常出色。如前所述，这些语言2010年比2009年增长了102.87%。其中，最为突出的是以R in a Nutshell为首的8本R语言图书。</p>
<table border="1">
<tr>
<td align="center"><b>*Mid-Major*</b></td>
<td colspan="2" align="center"><b>U N I T S</b></td>
<td colspan="2" align="center"><b>T I T L E S</b></td>
<td colspan="2" align="center"><b>M A R K E T S H A R E</b></td>
</tr>
<tr class="xl29">
<td align="center"><b>Language</b></td>
<td align="center"><b>2010 Units</b></td>
<td align="center"><b>2009 Units</b></td>
<td align="center"><b>2010 Titles</b></td>
<td align="center"><b>2009 Titles</b></td>
<td align="center"><b>10Mkt Share</b></td>
<td align="center"><b>09Mkt Share</b></td>
</tr>
<tr>
<td>SAS</td>
<td align="right">9,035</td>
<td align="right">7,974</td>
<td align="right">27</td>
<td align="right">21</td>
<td align="right">0.65%</td>
<td align="right">0.56%</td>
<tr>
<td>SPSS</td>
<td align="right">8,973</td>
<td align="right">6,818</td>
<td align="right">16</td>
<td align="right">10</td>
<td align="right">0.65%</td>
<td align="right">0.48%</td>
</tr>
<tr>
<td>MatLab</td>
<td align="right">7,857</td>
<td align="right">6,752</td>
<td align="right">22</td>
<td align="right">17</td>
<td align="right">0.57%</td>
<td align="right">0.47%</td>
</tr>
<tr>
<td>R</td>
<td align="right">7,800</td>
<td align="right">2,817</td>
<td align="right">15</td>
<td align="right">12</td>
<td align="right">0.56%</td>
<td align="right">0.20%</td>
</tr>
<tr>
<td>Processing</td>
<td align="right">6,996</td>
<td align="right">6,038</td>
<td align="right">8</td>
<td align="right">6</td>
<td align="right">.51%</td>
<td align="right">.42%</td>
</tr>
<tr>
<td>Shell Script</td>
<td align="right">6,073</td>
<td align="right">7,116</td>
<td align="right">19</td>
<td align="right">16</td>
<td align="right">.44%</td>
<td align="right">.50%</td>
</tr>
<tr>
<td>Basic</td>
<td align="right">5,540</td>
<td align="right">5,277</td>
<td align="right">7</td>
<td align="right">9</td>
<td align="right">.40%</td>
<td align="right">.37%</td>
</tr>
<tr>
<td>Lua</td>
<td align="right">4,677</td>
<td align="right">5,570</td>
<td align="right">7</td>
<td align="right">6</td>
<td align="right">.34%</td>
<td align="right">.39%</td>
</tr>
<tr>
<td>Assembly</td>
<td align="right">4,391</td>
<td align="right">4,359</td>
<td align="right">18</td>
<td align="right">14</td>
<td align="right">.32%</td>
<td align="right">.31%</td>
</tr>
<tr>
<td>MDX</td>
<td align="right">3,890</td>
<td align="right">4,838</td>
<td align="right">8</td>
<td align="right">8</td>
<td align="right">0.28%</td>
<td align="right">0.34%</td>
</tr>
<tr>
<td>UnrealScript</td>
<td align="right">3,028</td>
<td align="right">2,440</td>
<td align="right">3</td>
<td align="right">3</td>
<td align="right">.22%</td>
<td align="right">.17%</td>
</tr>
</table>
<p>Mid-Major组销量前5名如下。</p>
<table border="1">
<tr>
<td>O&#8217;Reilly</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/fG6e6X">R in a Nutshell: A Desktop Quick Reference</a></span></td>
</tr>
<tr>
<td>Prentice Hall</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/ieoRUn">Using SPSS for Windows and Macintosh: Analyzing and Understanding Data</a></span></td>
</tr>
<tr>
<td>SAS Press</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/hmOu0D">The Little SAS Book: A Primer, Fourth Edition</a></span></td>
</tr>
<tr>
<td>Open University Press</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/eJieRq">SPSS Survival Manual: A Step by Step Guide to Data Analysis Using SPSS for Windows</a></span></td>
</tr>
<tr>
<td>Sams</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/gYfwfO">Mastering Unreal Technology, Volume I: Introduction to Level Design with Unreal Engine 3</a></span></td>
</tr>
</table>
<h3>Mid-Minor组的编程语言（2010年销量在1,682-2,999之间）</h3>
<p>函数式编程语言是这个组中的亮点，比如F#、Scals和Lisp等。函数式编程语言类图书的年增长率达到了喜人的51.38%，2010年共销售了7,648册，而2009年这个数字只有3,718册。</p>
<table border="1">
<tr>
<td align="center"><b>*Mid-Minor*</b></td>
<td colspan="2" align="center"><b>U N I T S</b></td>
<td colspan="2" align="center"><b>T I T L E S</b></td>
<td colspan="2" align="center"><b>M A R K E T S H A R E</b></td>
</tr>
<tr>
<td align="center"><b>Language</b></td>
<td align="center"><b>2010 Units</b></td>
<td align="center"><b>2009 Units</b></td>
<td align="center"><b>2010 Titles</b></td>
<td align="center"><b>2009 Titles</b></td>
<td align="center"><b>10Mkt Share</b></td>
<td align="center"><b>09Mkt Share</b></td>
</tr>
<tr>
<td>F#</td>
<td align=right>2,905</td>
<td align=right>1,095</td>
<td align=right>6</td>
<td align=right>5</td>
<td align=right>0.21%</td>
<td align=right>0.08%</td>
</tr>
<tr>
<td>Scala</td>
<td align=right>2,531</td>
<td align=right>3,946</td>
<td align=right>5</td>
<td align=right>5</td>
<td align=right>0.18%</td>
<td align=right>0.28%</td>
</tr>
<tr>
<td>Groovy</td>
<td align=right>2,452</td>
<td align=right>3,972</td>
<td align=right>7</td>
<td align=right>8</td>
<td align=right>0.18%</td>
<td align=right>0.28%</td>
</tr>
<tr>
<td>Alice</td>
<td align=right>2,441</td>
<td align=right>2,472</td>
<td align=right>10</td>
<td align=right>9</td>
<td align=right>0.18%</td>
<td align=right>0.17%</td>
</tr>
<tr>
<td>Blitzmax</td>
<td align=right>1,836</td>
<td align=right>2,603</td>
<td align=right>2</td>
<td align=right>2</td>
<td align=right>0.13%</td>
<td align=right>0.18%</td>
</tr>
<tr>
<td>AppleScript</td>
<td align=right>1,787</td>
<td align=right>3,994</td>
<td align=right>4</td>
<td align=right>6</td>
<td align=right>0.13%</td>
<td align=right>0.28%</td>
</tr>
<tr>
<td>VHDL</td>
<td align=right>1,785</td>
<td align=right>1,733</td>
<td align=right>18</td>
<td align=right>15</td>
<td align=right>0.13%</td>
<td align=right>0.12%</td>
</tr>
<tr>
<td>Bash</td>
<td align=right>1,715</td>
<td align=right>183</td>
<td align=right>2</td>
<td align=right>1</td>
<td align=right>0.12%</td>
<td align=right>0.01%</td>
</tr>
<tr>
<td>Lisp</td>
<td align=right>1,684</td>
<td align=right>309</td>
<td align=right>4</td>
<td align=right>6</td>
<td align=right>0.12%</td>
<td align=right>0.02%</td>
</tr>
<tr>
<td>LabView</td>
<td align=right>1,682</td>
<td align=right>658</td>
<td align=right>3</td>
<td align=right>1</td>
<td align=right>0.12%</td>
<td align=right>0.05%</td>
</tr>
</table>
<p>Mid-Minor组中销量前5名如下。</p>
<table border="1">
<tr>
<td>Prentice-Hall</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/hfmOnc">Learning To Program with Alice</a></span></td>
</tr>
<tr>
<td>Artima</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/gWvAii">Programming in Scala: A Comprehensive Step-by-step Guide</a></span></td>
</tr>
<tr>
<td>No Starch Press</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/huBOGn">Land of Lisp: Learn to Program in Lisp, One Game at a Time!</a></span></td>
</tr>
<tr>
<td>Prentice-Hall</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/gzP5tU">LabVIEW 2009 Student Edition</a></span></td>
</tr>
<tr>
<td>Manning</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://amzn.to/gsTu72">Real World Functional Programming: With Examples in F# and C#</a></span></td>
</tr>
</table>
<h3>Minor组的编程语言（2010年销量在1,000-1,680之间）</h3>
<p>这个组10门语言中有6种在2010年的销量减少了。总体来看，销量比上一年下降了约20%。其中，最吸引眼球的是Mathematica，很大程度归功于Mathematica Cookbook这本书。与Mid-Minor组一样，这个组仍然是函数式编程语言的主战场，但它们在这个组里表现平平。</p>
<table border="1">
<tr>
<td align="center"><b>*Minor*</b></td>
<td colspan="2" align="center"><b>U N I T S</b></td>
<td colspan="2" align="center"><b>T I T L E S</b></td>
<td colspan="2" align="center"><b>M A R K E T S H A R E</b></td>
</tr>
<tr>
<td align="center"><b>Language</b></td>
<td align="center"><b>2010 Units</b></td>
<td align="center"><b>2009 Units</b></td>
<td align="center"><b>2010 Titles</b></td>
<td align="center"><b>2009 Titles</b></td>
<td align="center"><b>10Mkt Share</b></td>
<td align="center"><b>09Mkt Share</b></td>
</tr>
<tr>
<td>Mathematica</td>
<td align=right>1,675</td>
<td align=right>900</td>
<td align=right>9</td>
<td align=right>4</td>
<td align=right>0.12%</td>
<td align=right>0.06%</td>
</tr>
<tr>
<td>Erlang</td>
<td align=right>1,513</td>
<td align=right>2,276</td>
<td align=right>3</td>
<td align=right>2</td>
<td align=right>0.11%</td>
<td align=right>0.16%</td>
</tr>
<tr>
<td>Scheme</td>
<td align=right>1,479</td>
<td align=right>1,364</td>
<td align=right>8</td>
<td align=right>7</td>
<td align=right>0.11%</td>
<td align=right>0.10%</td>
</tr>
<tr>
<td>FBML</td>
<td align=right>1,367</td>
<td align=right>2,335</td>
<td align=right>5</td>
<td align=right>4</td>
<td align=right>0.10%</td>
<td align=right>0.16%</td>
</tr>
<tr>
<td>Clojure</td>
<td align=right>1,332</td>
<td align=right>1,460</td>
<td align=right>2</td>
<td align=right>1</td>
<td align=right>0.10%</td>
<td align=right>0.10%</td>
</tr>
<tr>
<td>AWK</td>
<td align=right>1,200</td>
<td align=right>1,642</td>
<td align=right>2</td>
<td align=right>2</td>
<td align=right>0.09%</td>
<td align=right>0.12%</td>
</tr>
<tr>
<td>Nxt-g</td>
<td align=right>1,172</td>
<td align=right>969</td>
<td align=right>4</td>
<td align=right>1</td>
<td align=right>0.08%</td>
<td align=right>0.07%</td>
</tr>
<tr>
<td>Scratch</td>
<td align=right>1,112</td>
<td align=right>674</td>
<td align=right>2</td>
<td align=right>2</td>
<td align=right>0.08%</td>
<td align=right>0.05%</td>
</tr>
<tr>
<td>Latex</td>
<td align=right>1,099</td>
<td align=right>1,623</td>
<td align=right>6</td>
<td align=right>5</td>
<td align=right>0.08%</td>
<td align=right>0.11%</td>
</tr>
<tr>
<td>Haskell</td>
<td align=right>1,051</td>
<td align=right>2,274</td>
<td align=right>5</td>
<td align=right>3</td>
<td align=right>0.08%</td>
<td align=right>0.16%</td>
</tr>
</table>
<p>Minor组语言中销售排名前5的图书如下。</p>
<table border="1">
<tr>
<td>O&#8217;Reilly</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/hULgvs">Mathematica Cookbook</a></span></td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/eQBjua">ERLANG Programming</a></span></td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/hLGsKr">Real World Haskell</a></span></td>
</tr>
<tr>
<td>Pragmatic</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/ecRcnT">Programming Clojure</a></span></td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td><span style="color: #0000EE; font-style: italic; text-decoration: underline;"><a href="http://oreil.ly/eQwc9L">sed &#038; awk</a></span></td>
</tr>
</table>
<h3>Linelist 组的编程语言（2010年销量在399-999之间）</h3>
<p>虽然销售总量很少，但这个组里的10门语言中仍然有6门在2010年实现了增长。这个组的年销售下降幅度大约为-0.81%。后面并没有列出畅销书的名录，因为这个组整体都还谈不上畅销。</p>
<h3>TheRest组的编程语言（2010年销量少于400）</h3>
<p>最后是2010年销量低于400册的语言。这些语言按降序排列如下：autolisp、unity、x++、cfml、inform、mysql spl、blitz3d、q、nxt、gml、pure data、javafx、rpg、cobol、nxc、minitab、ml、boo、ada、fortran、octave、jcl、racket、jsl、idl、cfscript、abap、verilog、m、smalltalk、mumps、go、windows script、egl、c/al、realbasic、bondi、cl、cs2、eiffel、ocaml、xquery。</p>
<p>接下来，第五部分将关注电子图书销售。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/02/25/2010-book-market-of-usa-4-2265.html/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>2010年美国计算机图书市场报告三：出版商</title>
		<link>http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-3-2270.html</link>
		<comments>http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-3-2270.html#comments</comments>
		<pubDate>Wed, 23 Feb 2011 14:46:39 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[出版]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2270</guid>
		<description><![CDATA[http://radar.oreilly.com/2011/02/2010-book-market-3.html Mike Hendrickson 2011-2-17 报告内容 第一部分：市场综述 第二部分：分类市场 第三部分：出版商 第四部分：编程语言 第五部分：完结篇及数字出版 下载完整版（PDF） 这一部分主要以2009年作为对比分析2010年出版商的经营情况。下面两张饼图展示了2009年和2010年各大出版商的经营业绩。最值得注意的是，Wiley继续稳坐最大出版商的位置（占图书销售量份额的32%），而Pearson和O&#8217;Reilly各掉了1%，分别被Cengage和McGraw Hill拿走。（后面将会分析收入份额。） 2009 Pub Share 2010 Pub Share 可能有人不太熟悉这些排名靠前的出版商，那是因为这些大型出版集团几年来收购、成立、拆分出了很多小出版公司。而往往这些小出版公司才是读者心目中最熟悉的品牌。比如说，你买了一本Peachpit或Sams出的书，书脊上印的都是Peachpit和Sams，而非Pearson，但后者拥有前两家出版公司。对O&#8217;Relly来说，任何不打“O&#8217;Reilly”标志的出版公司都是他们的发行合作伙伴，而不属于O&#8217;Reilly。至于构成各大出版商的小出版公司的市场份额，也会在本文后面以饼图的形式详细给出。 下面来看一下各大出版商两年的市场表现情况。以下表格中包含了几组有趣的对比性数据。 Publisher 2010 Units 2009 Units 2010 Title Count 2009 Title Count 2010 Efficiency 2009 Efficiency Wiley 1,887,493 1,904,859 1,538 1,468 1.65 1.61 O&#8217;Reilly 1,404,607 1,577,838 1,145 1,185 1.65 1.65 Pearson 1,386,301 1,511,855 1,934 1,936 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://radar.oreilly.com/2011/02/2010-book-market-3.html">http://radar.oreilly.com/2011/02/2010-book-market-3.html</a><br />
Mike Hendrickson<br />
2011-2-17</p>
<div style="float:right; border:1px solid #ddd;border-width:0 0 1px 1px;padding:0 .5em;margin:0 0 0 .5em;">
<p style="font-weight:bold;">报告内容</p>
<ol>
<li><a href="http://www.cn-cuckoo.com/2011/02/26/2010-book-market-of-usa-1-2287.html">第一部分：市场综述</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-2-2329.html">第二部分：分类市场</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-3-2270.html">第三部分：出版商</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-4-2265.html">第四部分：编程语言</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/25/2010-book-market-of-usa-5-2279.html">第五部分：完结篇及数字出版</a></li>
</ol>
<div style="text-align:center;">
<a href="http://u.115.com/file/f661fc6f86"><br />
<img class="colorbox-2270"  style="border:1px solid #eee;" src="http://ww1.sinaimg.cn/large/61baa48djw6deqves2ryvj.jpg"/><br />
</a><br />
<a href="http://u.115.com/file/f661fc6f86">下载完整版（PDF）</a>
</div>
</div>
<p>这一部分主要以2009年作为对比分析2010年出版商的经营情况。下面两张饼图展示了2009年和2010年各大出版商的经营业绩。最值得注意的是，Wiley继续稳坐最大出版商的位置（占图书销售量份额的32%），而Pearson和O&#8217;Reilly各掉了1%，分别被Cengage和McGraw Hill拿走。（后面将会分析收入份额。）</p>
<h3>2009 Pub Share</h3>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/Pub2009.png"/></p>
<h3>2010 Pub Share</h3>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/Pub2010.png"/></p>
<p>可能有人不太熟悉这些排名靠前的出版商，那是因为这些大型出版集团几年来收购、成立、拆分出了很多小出版公司。而往往这些小出版公司才是读者心目中最熟悉的品牌。比如说，你买了一本Peachpit或Sams出的书，书脊上印的都是Peachpit和Sams，而非Pearson，但后者拥有前两家出版公司。对O&#8217;Relly来说，任何不打“O&#8217;Reilly”标志的出版公司都是他们的发行合作伙伴，而不属于O&#8217;Reilly。至于构成各大出版商的小出版公司的市场份额，也会在本文后面以饼图的形式详细给出。<br />
<span id="more-2270"></span><br />
下面来看一下各大出版商两年的市场表现情况。以下表格中包含了几组有趣的对比性数据。</p>
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td align=center><b>Publisher</b></td>
<td align=center><b>2010 Units</b></td>
<td align=center><b>2009 Units</b></td>
<td align=center><b>2010 Title Count</b></td>
<td align=center><b>2009 Title Count</b></td>
<td align=center><b>2010 Efficiency</b></td>
<td align=center><b>2009 Efficiency</b></td>
</tr>
<tr>
<td>Wiley</td>
<td align=right>1,887,493</td>
<td align=right>1,904,859</td>
<td align=right>1,538</td>
<td align=right>1,468</td>
<td align=right>1.65</td>
<td align=right>1.61</td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td align=right>1,404,607</td>
<td align=right>1,577,838</td>
<td align=right>1,145</td>
<td align=right>1,185</td>
<td align=right>1.65</td>
<td align=right>1.65</td>
</tr>
<tr>
<td>Pearson</td>
<td align=right>1,386,301</td>
<td align=right>1,511,855</td>
<td align=right>1,934</td>
<td align=right>1,936</td>
<td align=right>0.97</td>
<td align=right>0.97</td>
</tr>
<tr>
<td>McGrawHill</td>
<td align=right>276,439</td>
<td align=right>255,667</td>
<td align=right>466</td>
<td align=right>454</td>
<td align=right>0.80</td>
<td align=right>0.70</td>
</tr>
<tr>
<td>Apress</td>
<td align=right>200,267</td>
<td align=right>212,614</td>
<td align=right>423</td>
<td align=right>389</td>
<td align=right>0.64</td>
<td align=right>0.68</td>
</tr>
<tr>
<td>Cengage</td>
<td align=right>167,020</td>
<td align=right>143,521</td>
<td align=right>676</td>
<td align=right>644</td>
<td align=right>0.33</td>
<td align=right>0.28</td>
</tr>
<tr>
<td>Reed Elsevier</td>
<td align=right>140,708</td>
<td align=right>128,657</td>
<td align=right>384</td>
<td align=right>355</td>
<td align=right>0.49</td>
<td align=right>0.45</td>
</tr>
<tr>
<td>Lightning Source</td>
<td align=right>67,620</td>
<td align=right>61,676</td>
<td align=right>412</td>
<td align=right>325</td>
<td align=right>0.22</td>
<td align=right>0.23</td>
</tr>
<tr>
<td><b>Sum/Avg</b></td>
<td align=right><strong>5,530,455</strong></td>
<td align=right><b>5,796,687</b></td>
<td align=right><b>6,978</b></td>
<td align=right><b>6,756</b></td>
<td align=right><strong>0.84</strong></td>
<td align=right><b>.82</b></td>
</tr>
</table>
<p>这些数据说明什么问题？首先，最大的三家出版商（每年销售册数100万以上）业绩全部下滑。其次，前8名里有4家业绩攀升：McGraw Hill、Cengage、Reed Elsevier和Lightning Source在2010年都有适度增长。总体上看，2010年前8家出版商的出书品种虽然增加了222个，但图书销售量却减少了266,232册。</p>
<h2>关于市场份额与单品效率</h2>
<p>衡量出版商经营水平的一个典型指标，就是图书销售量的市场份额，这也是我们一直在讨论的问题。但是，还有一个更好的度量指标，就是出版商获得的可比较的销售份额是出了多少本书换来的。这个比例是用出书品种份额比上销量份额。解释一下：如果某出版商出版的图书在Bookscan Top 3000中占15%，而该出版商的销量份额也是15%，那么这个比例就是1:1，其单品效率为1.0。如果品种份额为20%，而销量份额是10%，单品效率就是0.5。单品效率1.0代表市场平均水平：100%的出版品种带来了100%的图书销售。以较少的品种实现其销量份额的出版商具有较高的效率。只有两个出版商的效率超过了1：Wiley和O&#8217;Reilly。</p>
<p>单品效率小于1.0的出版商的表现就是在Bookscan监控数据中拥有很多图书品种，但实际卖出的图书却不够多。但需要注意一点，有些出版商拥有不少长销不衰的书，这些书会影响数据。一般来说，老书的销量会逐年递减，但也不是所有书都这样。不少书后续年份的销量并不亚于刚上市的年份。Head First Design Patterns就是一个例子，其销量仍然位居大多数新书之上。为此，可以把单品效率看一种频率比，而不是真正用它去衡量出版效率，毕竟出一本书能卖好几年才是真正有效率。真正的效率指标应该考虑所有出版商出版的所有图书，以及这些图书中有多少进了前3000名。有的出版商还有一些书根本就没有进入过前3000名，数据库里没有这些书的数据，因而我们就无法统计它们（很难说它们对效率的贡献）。</p>
<h2>关于长销书</h2>
<p>下面表格中展示了一些出版公司拥有的长销书比例。我们是怎么统计长销书的呢？我们会按照版权日期给一本书打分，分为16年以上（最高分）、10-16年、5-10年以及5年以内（零分，因为还没有证明其长销）。在为每本书打分之后，就可以统计出2004到2010年每家公司的长销书比例。统计发现，当然也是意料之中的，拥有长销书最多的前三家出版公司（Wiley、Addison-Wesley和Prentice Hall）都有稳定的学术遗产（a strong academic heritage ？）。</p>
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td><b>Imprint</b></td>
<td><b>% Evergreen</b></td>
</tr>
<tr>
<td>John Wiley</td>
<td align="right">11.47%</td>
</tr>
<tr>
<td>Addison-Wesley</td>
<td align="right">11.07%</td>
</tr>
<tr>
<td>Prentice Hall</td>
<td align="right">10.85%</td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td align="right">9.63%</td>
</tr>
<tr>
<td>For Dummies</td>
<td align="right">8.43%</td>
</tr>
<tr>
<td>Sams</td>
<td align="right">7.79%</td>
</tr>
<tr>
<td>Que</td>
<td align="right">7.01%</td>
</tr>
<tr>
<td>Peachpit Press</td>
<td align="right">5.62%</td>
</tr>
<tr>
<td>McGraw-Hill/Osborne</td>
<td align="right">5.50%</td>
</tr>
<tr>
<td>Wrox</td>
<td align="right">3.66%</td>
</tr>
<tr>
<td>APress</td>
<td align="right">2.92%</td>
</tr>
</table>
<p>现在我们对单品效率和长销书的情况有了基本的了解，下面我们就深入分析一下这些出版公司2010年的经营业绩，先从Wiley、Pearson和O&#8217;Reilly这三大出版商入手。这一点很重要，因为你买书的时候通常只会看书上印的出版公司的名字，至少背后的出版商是谁，恐怕你不会在意。（如果想知道出版商是谁，可以翻到版权页找一找，但O&#8217;Reilly除外，因为我们其他出版公司都是发行合作伙伴。换句话说，O&#8217;Reilly会替这些合作伙伴提供一些销售和发行服务，但并不拥有这些公司，不像Pearson和Wiley那样。）</p>
<p>#1 Wiley<br />
<img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/Wiley2010.jpg"/></p>
<p>#2 O&#8217;Reilly Media<br />
<img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/ORM2010.jpg"/></p>
<p>#3 Pearson<br />
<img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/pearson2010.jpg"/></p>
<p>2010年，O&#8217;Reilly Media携所有出版和发行伙伴成为第二大出版商。我们的数据中包含了来自所有合作伙伴的贡献，以及它们加入各自出版集团的大致年份。很大程度上，我们与Microsoft Press达成的协议是让O&#8217;Reilly Media一跃成为第二大技术出版商的根本原因。当然，这个第二名领先第三名Pearson的优势几乎是微不足道的（只有不到15,000册）。Wiley仍然是第一大出版商，而且应对市场下滑的能力好像更强一些，这主要归功于Dummies品牌及其广泛的领域。下面的趋势图展示了三大技术图书出版商几年来各自的增长情况。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/BigThree_Years.jpg"/></p>
<p>了解了构成三大出版商的出版公司之后，我们接下来再看一看所有这些出版公司各自的市场份额情况。下面的柱形图展示了前20家出版公司，以及它们之间的业绩对比。其中前10家2010年的图书销量减少了422,814册，而2009年技术图书的市场表现也并不好。当然，还有一些图中没有列出的中小型出版公司同样占有相当的市场份额。从出版公司的角度来看，O&#8217;Reilly的市场份额居于Dummies之后。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/top20Units.jpg"/></p>
<p>这张图能够说明什么问题呢？第一，处于市场前10的出版公司基本上没变。换句话说，排名在前的出版公司仍然排名在前。实际上，在这前20家出版公司中，只有7家的图书销量比2009年略有增加，只有8家的销售码洋比2009年有所增长（见下面的柱形图）。唯一从前10名中下降的是Apress，它从第10变成了第11。其中，Sybex、Wrox和Course Technology依次是销量增长最多的三家公司，而Wrox、Course Technology和Sybex依次是百分比提高最多的三家公司。应该说，Sybex上一年的基数最大，因此市场份额百分比增长并不突出。而另外两家的码洋规模都少了一半，因此它们的销量增长对百分比增长的拉动更为明显。</p>
<p>在分类剖析各家出版公司之前，我们再从码洋角度看看这些出版公司的业绩。计算数据的公式很简单：销量×定价 = 码洋。就算有折扣、促销及其他因素影响计算结果的精确度，但应该也是相当接近实际的。再不然，你可以把码洋看成零售价值。因此，这一次是从收入角度排名，而且，是从出版公司层面，按照码洋来排序的。显然，与上面按销量排名的图一比，你马上就能发现非常大的变化。Microsoft Press这次成了产值最高的出版公司，其次是O&#8217;Reilly，然后是Dummies。前20名中变动最大的是Addison-Wesley，按码洋计算一下子让它从第8跃升至第4。相反，Wiley的Visual出版公司则从按销量排名的第10跌落至按码洋排名的第17位。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/top20Dollars.jpg"/></p>
<h2>出版门类分析</h2>
<p>在了解了这些出版公司在2010年的经营情况之后，下面我们就来看看这些出版公司涉足的出版门类，以及它们的优势所在。Dummies和O&#8217;Reilly拥有的出版门类是最多样化的，而且他们在涉足的任何门类中都取得了不错的成绩。在商业应用和消费者操作系统类别中，Dummies无疑是王者，而O&#8217;Reilly则在系统与程序设计类别中超越了Microsoft Press。下面这张图也说明，Addison-Wesley基本上只出版系统与程序设计类图书。而这也正是某些出版商的策略：他们自己的某家出版公司会专注于一或两个门类。这一策略是好是坏尚无定论，但Addison-Welsey在2010年码洋的增长可能是要归功于这种专注的。可以得出一个结论，在某个领域没有太多技术拉动的情况下，经营门类不够多样化会导致出版商/出版公司更容易受到市场下滑的冲击。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/CF_Imprint_top10Units.jpg"/></p>
<h3>出版门类及位列第一的出版商</h3>
<p>以下出版门类的数据图都是2010年的，表格中列出了每家出版商的出书品种及总销量。每个门类也分别给出了2010年度最畅销的图书书目。</p>
<h2>门类：系统与程序设计</h2>
<p>在这个门类中，O&#8217;Reilly在各大出版商中占有最多份额，Pearson以微弱差距位居第二。如果按照出版公司再细分一下，就很容易看谁是真正的市场主导者了。排名前6的出版公司分别是O&#8217;Reilly占13.66%、Microsoft Press占10.26%、Addison-Wesley占9.65%、For Dummies占7.04%、Apress占6.67%、Prentice Hall占5.48%。通过这张图看不出来的是，前几大出版商的市场份额都下降了两三个百分点，这说明市场的增长源自排名居中的出版公司。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/2010_SysProg.png"/></p>
<p>从下面的表格中可以看出，O&#8217;Reilly的图书销量最多，单品效率也最高。这两个指标的组合是比较良性的。换句话说，虽然我们出版的图书品种并不少，但我们效率也明显高于市场平均值。</p>
<h3>系统与程序设计类：出版商市场份额 （2010.1.1-2010.12.31）</h3>
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td><b>Publisher</b></td>
<td><b>Units</b></td>
<td><b>Title Count</b></td>
<td><b>Units/Title</b></td>
<td><b>Efficiency</b></td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td align="right">557,876</td>
<td align="right">637</td>
<td align="right">876</td>
<td align="right">1.63</td>
</tr>
<tr>
<td>Pearson</td>
<td align="right">525,897</td>
<td align="right">960</td>
<td align="right">548</td>
<td align="right">1.02</td>
</tr>
<tr>
<td>Wiley</td>
<td align="right">386,809</td>
<td align="right">553</td>
<td align="right">699</td>
<td align="right">1.30</td>
</tr>
<tr>
<td>Apress</td>
<td align="right">114,705</td>
<td align="right">236</td>
<td align="right">512</td>
<td align="right">0.95</td>
</tr>
<tr>
<td>McGraw Hill</td>
<td align="right">111,980</td>
<td align="right">236</td>
<td align="right">474</td>
<td align="right">0.88</td>
</tr>
<tr>
<td>Cengage</td>
<td align="right">64,580</td>
<td align="right">252</td>
<td align="right">256</td>
<td align="right">0.48</td>
</tr>
<tr>
<td>Lightning Source</td>
<td align="right">34,674</td>
<td align="right">188</td>
<td align="right">184</td>
<td align="right">.34</td>
</tr>
<tr>
<td>Reed Elsevier</td>
<td align="right">31,957</td>
<td align="right">188</td>
<td align="right">170</td>
<td align="right">0.32</td>
</tr>
</table>
<p>注意：这个门类中包含“编程语言”和“程序设计”，这两个子类别2010年销量超过了2009年。2010年系统与程序设计类领先的图书及出版商如下：</p>
<ol>
<li><i>PMP Exam Prep: Rita&#8217;s Course in a Book for Passing the PMP Exam</i> （这本书位居榜首已经好几年了)(RMC)</li>
<li><i><a href="http://oreil.ly/e0iDsN">MCTS Self-Paced Training Kit : Configuring Windows 7</a></i>(Microsoft Press)</li>
<li><i>CISSP Certification All-in-One Exam Guide, 5th Ed.</i>(McGraw Hill)</li>
<li><i>CCNA Official Exam Certification Library, 3rd Ed.</i>(Cisco Press)</li>
<li><i><a href="http://oreil.ly/eTKrN9">Head First Java, 2nd Edition</a></i>(O&#8217;Reilly)</li>
</ol>
<h2>门类：Web设计与开发</h2>
<p>在这个门类中，Wiley的市场份额是各家出版商中最大的，Pearson紧随其后。如果再细化到出版公司的层面，这张图的序列会发生一些变化。位于前6名的出版公司变成了O&#8217;Reilly占21.11%、Dummies占13.30%、Sams占6.25%、Wiley占5.92%、New Riders占5.82%、Peachpit占5.40%。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/01/2010_WebDev.png"/></p>
<p>从下面这张表中可以看出，Pearson出书品种最多，其业绩在这个门类中也相当突出。在Web设计与开发这个类别中，前几大出版商的单品效率通常都高于平均值1.0。但这里只有前三家出版商的效率超过了1.0这个临界值。（这说明还有很多二线出版商的效率低于1.0，但这张表并没有体现出现来。）2010年，这个门类的销量减少了25,000册，但出书品种多了21种。市场下滑也影响了这个门类及相应出版商的效率。</p>
<h3>Web设计与开发：出版商市场份额 （2010.1.1-2010.12.31）</h3>
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td>Publisher</td>
<td>Units</td>
<td>Title Count</td>
<td>Units/Title</td>
<td>Efficiency</td>
</tr>
<tr>
<td>Wiley</td>
<td align="right">234,853</td>
<td align="right">219</td>
<td align="right">1,072</td>
<td align="right">1.48</td>
</tr>
<tr>
<td>Pearson</td>
<td align="right">232,547</td>
<td align="right">287</td>
<td align="right">810</td>
<td align="right">1.11</td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td align="right">221,876</td>
<td align="right">211</td>
<td align="right">1,052</td>
<td align="right">1.45</td>
</tr>
<tr>
<td>Apress</td>
<td align="right">48,812</td>
<td align="right">124</td>
<td align="right">394</td>
<td align="right">0.54</td>
</tr>
<tr>
<td>Lightning Source</td>
<td align="right">16,345</td>
<td align="right">104</td>
<td align="right">157</td>
<td align="right">.22</td>
</tr>
</table>
<p>Web设计与开发类中领先的图书和出版商如下：</p>
<ol>
<li><i>Don&#8217;t Make Me Think: A Common Sense Approach to Web Usability, 2nd Ed.</i>(New Riders&#8217; )</li>
<li><i><a href="http://oreil.ly/hPDptg">Head First HTML with CSS &amp; XHTML</a></i>(O&#8217;Reilly)</li>
<li><i><a href="http://oreil.ly/fE8dpT">CSS: The Missing Manual</a></i>(O&#8217;Reilly)</li>
<li><i>HTML, XHTML, and CSS: Visual Quickstart, 6th Ed.</i>(Peachpit)</li>
<li><i>WordPress For Dummies: 2nd Ed.</i>(Wiley)</li>
</ol>
<h2>门类：商业应用</h2>
<p>在这个类别中，拥有最大市场份额的是Wiley，而O&#8217;Reilly（抱歉，图中没有显示出O&#8217;Reilly的21%）已经超过Pearson，位居第二。从出版公司的层面上看，这张图的排名也会发生变化。前6名的出版公司分别是Dummies占28.34%、Microsoft Press占14.72%、McGraw Hill/Osborne占7.06%、O&#8217;Reilly占6.29、John Wiley占5.74%、Que占4.18%。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/02/2010_BisApps.png"/></p>
<h3>商业应用：出版商市场份额 （2010.1.1-2010.12.31）</h3>
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td><b>Publisher</b></td>
<td><b>Units</b></td>
<td><b>Title Count</b></td>
<td><b>Units/Title</b></td>
<td><b>Efficiency</b></td>
</tr>
<tr>
<td>Wiley</td>
<td align="right">566,391</td>
<td align="right">386</td>
<td align="right">1,467</td>
<td align="right">1.74</td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td align="right">278,058</td>
<td align="right">162</td>
<td align="right">1,716</td>
<td align="right">2.04</td>
</tr>
<tr>
<td>Pearson</td>
<td align="right">199,261</td>
<td align="right">296</td>
<td align="right">673</td>
<td align="right">0.80</td>
</tr>
<tr>
<td>McGraw Hill</td>
<td align="right">96,806</td>
<td align="right">121</td>
<td align="right">800</td>
<td align="right">0.95</td>
</tr>
<tr>
<td>Cengage</td>
<td align="right">102,034</td>
<td align="right">75</td>
<td align="right">1,360</td>
<td align="right">1.2</td>
</tr>
<tr>
<td>Cengage</td>
<td align="right">35,625</td>
<td align="right">200</td>
<td align="right">178</td>
<td align="right">0.21</td>
</tr>
<tr>
<td>Apress</td>
<td align="right">16,370</td>
<td align="right">38</td>
<td align="right">431</td>
<td align="right">0.51</td>
</tr>
</table>
<p>商业应用类中领先的图书及出版商如下：</p>
<ol>
<li><i>Facebook For Dummies</i>(Wiley)</li>
<li><i>Office 2007 All-in-One Desk Reference For Dummies</i>(Wiley)</li>
<li><i>Excel 2007 for Dummies</i>(Wiley)</li>
<li><i><a href="http://oreil.ly/dSIGDp">Microsoft Office Excel 2007 Step by Step</a></i>(Microsoft)</li>
<li><i>QuickBooks 2010 The Official Guide </i>(McGraw Hill)</li>
<li><i>Excel 2007 All-In-One Desk Reference For Dummies</i>(Wiley)</li>
</ol>
<h2>门类：消费者操作系统</h2>
<p>在这个门类中，我们看到Wiley占有最多的市场份额（达到了46%），其他出版商中，O&#8217;Reilly以22%居第二位，Pearson以17%稳稳坐在第三把交椅上。（抱歉，O&#8217;Reilly的百分比在图中被切掉了一点。）按照出版公司来看，这张图又会有变化。前6名的出版公司分别是Dummies占31.00%、O&#8217;Reilly占13.15%、Que占9.39%、Microsoft Press占8.52%、Wiley的Visual占7.19%。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/02/ConOps.png"/></p>
<p>从下面表格中可以看出，Wiley出的书最多，单品效率也相对不错。O&#8217;Reilly的单品效率更高。这个门类中最突出的一点是，前6家出版公司中有4家单品销售量超过1,000册。在我看来，它意味着这个门类支撑着很多大销售商的出货量，而绝不仅仅是零售渠道偶然的成功。从下面畅销书的排行可以看出，这个类别的突出表现很大程度上得益于Windows 7，而iPad: The Missing Manual出现在2010年畅销榜中实属意外。</p>
<h3>消费者操作系统：出版商市场份额 （2010.1.1-2010.12.31）</h3>
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td><b>Publisher</b></td>
<td><b>Units</b></td>
<td><b>Title Count</b></td>
<td><b>Units/Title</b></td>
<td><b>Efficiency</b></td>
</tr>
<tr>
<td>Wiley</td>
<td align="right">490,682</td>
<td align="right">182</td>
<td align="right">2,696</td>
<td align="right">1.48</td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td align="right">237,508</td>
<td align="right">62</td>
<td align="right">3,831</td>
<td align="right">2.11</td>
</tr>
<tr>
<td>Pearson</td>
<td align="right">184,497</td>
<td align="right">129</td>
<td align="right">1,430</td>
<td align="right">0.79</td>
</tr>
<tr>
<td>McGraw Hill</td>
<td align="right">48,684</td>
<td align="right">50</td>
<td align="right">974</td>
<td align="right">0.54</td>
</tr>
<tr>
<td>Cengage</td>
<td align="right">37,679</td>
<td align="right">86</td>
<td align="right">438</td>
<td align="right">0.24</td>
</tr>
<tr>
<td>Computer Step </td>
<td align="right">37,528</td>
<td align="right">24</td>
<td align="right">1,564</td>
<td align="right">.86</td>
</tr>
</table>
<p>消费者操作系统领域的畅销图书及其出版商分别是：</p>
<ol>
<li><i>Windows 7 For Dummies</i>(Wiley)</li>
<li><i>Windows 7 For Dummies Book + DVD Bundle</i>(Wiley)</li>
<li><i><a href="http://oreil.ly/fC1foB">Windows 7 Plain &#038; Simple</a></i>(Microsoft Press)</li>
<li><i><a href="http://oreilly.com/catalog/9780596529529/index.html">Mac OS X Leopard: The Missing Manual</a></i>(O&#8217;Reilly) </li>
<li><i><a href="http://oreil.ly/hYuUBK">Windows 7 Step by Step</a></i>(Microsoft Press)</li>
<li><i><a href="http://oreil.ly/hkAXOX">iPad: The Missing Manual</a></i>(O&#8217;Reilly)</li>
</ol>
<h2>门类：数字媒体</h2>
<p>在这个类别中，Pearson跃居第一，Wiley降至第二。而通过后面的表格可以看出，Pearson出版的图书最多，单品效率也相当不错。O&#8217;Reilly的单品效率和平均单品销量也都很高。这一点印证了我前面的说法，少出书多卖书才是出版之道。比如，下面的表格显示Reed Elsevier已经成为第三大出版商，但这个市场地位的获得，完全是建立在其出书量比O&#8217;Reilly高两倍的基础之上的。因此，它的单品效率比O&#8217;Reilly低了很多。如果考虑到Reed Elsvier有一本书的销量占到了其总销量的1/4，那么它的实际单品效率应该会更低。</p>
<p><img class="colorbox-2270"  src="http://radar.oreilly.com/upload/2011/02/DigigalMedia.png"/></p>
<h3>数字媒体：出版商市场份额 （2010.1.1-2010.12.31）</h3>
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td><b>Publisher</b></td>
<td><b>Units</b></td>
<td><b>Title Count</b></td>
<td><b>Units/Title</b></td>
<td><b>Efficiency</b></td>
</tr>
<tr>
<td>Pearson</td>
<td align="right">228,640</td>
<td align="right">186</td>
<td align="right">1,229</td>
<td align="right">1.27</td>
</tr>
<tr>
<td>Wiley</td>
<td align="right">173,616</td>
<td align="right">157</td>
<td align="right">1,106</td>
<td align="right">1.14</td>
</tr>
<tr>
<td>Reed Elsevier</td>
<td align="right">76,359</td>
<td align="right">110</td>
<td align="right">694</td>
<td align="right">0.72</td>
</tr>
<tr>
<td>O&#8217;Reilly</td>
<td align="right">74,235</td>
<td align="right">36</td>
<td align="right">2,062</td>
<td align="right">2.13</td>
</tr>
<tr>
<td>Cengage</td>
<td align="right">15,027</td>
<td align="right">47</td>
<td align="right">320</td>
<td align="right">0.33</td>
</tr>
</table>
<p>数字媒体领域的畅销图书及其出版商如下：</p>
<ol>
<li><i>Adobe Photoshop CS5 for Photographers</i>(Focal Press)</li>
<li><i>The Adobe Photoshop CS5 Book for Digital Photographers</i>(New Riders)</li>
<li><i>Adobe Photoshop CS4 Classroom in a Book</i>(Adobe Press)</li>
<li><i>Adobe Photoshop CS5 Classroom in a Book </i>(Adobe Press)</li>
<li><i><a href="http://oreil.ly/fSVT96">Photoshop Elements 8 for Windows: The Missing Manual</a></i>(O&#8217;Reilly)</li>
</ol>
<p>接下来，第四部分将详细分析编程语言类图书，而第五部分将重点关注电子图书销售。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-3-2270.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>2010年美国计算机图书市场报告二：分类市场</title>
		<link>http://www.cn-cuckoo.com/2011/02/22/2010-book-market-of-usa-2-2329.html</link>
		<comments>http://www.cn-cuckoo.com/2011/02/22/2010-book-market-of-usa-2-2329.html#comments</comments>
		<pubDate>Tue, 22 Feb 2011 15:27:54 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[出版]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[移动开发]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2329</guid>
		<description><![CDATA[http://radar.oreilly.com/2011/02/2010-book-market-2.html Mike Hendrickson 2011-2-14 报告内容 第一部分：市场综述 第二部分：分类市场 第三部分：出版商 第四部分：编程语言 第五部分：完结篇及数字出版 下载完整版（PDF） 在这一部分，我们按照技术门类来分析一下计算机图书的销售情况。 在上一部分，我们把数据分成六大“门类”：系统与程序设计、Web设计与开发、商业应用、数字媒体应用、消费者操作系统与设备、IT人文（Computer Topics）。 这六大门类下面，是一级分类、二级分类、三级分类、四级分类，一共5层。比如说，系统与程序设计门类下面的一级分类有编程语言、数据库、软件工程、通用程序设计、安全，等等。 本部分将对比2010年与2009年的第四季度，也会将2010年与2009年的情况进行对比。 为了方便起见，下面给出了上一部分中展示过的Treemap，其中包含各个门类与一级门类2010年与2009年第四季度的对比。 这张图中的红、绿、黑色方块基本上反映了市场的波动情况。其中，代表高度增长领域的浅绿色方块非常少。但不要忘了这是2010年第四季度与2009年第四季度的对比。两个最大、最亮的绿色区域是“Android编程”和“Android消费者应用”，这两类图书从2008年微不足道的小方块成长为2010年相当可观的大市场。 下面的两张图可以反映出各图书门类历年的增长情况。第一张图是历年构成Top 3000的图书品种，而第二张图是历年来的图书销量。从中可以看出，“商业应用/人文”和“系统与程序设计”类图书的品种在2010年都增加了，但这两类图书的销量却是双双下跌的。“消费者操作系统与设备”是唯一一个出书品种和销量在2010年双增的门类。“系统与程序设计”是最大的一个门类，但其销售业绩也更加不稳定，2010年经历了最大的一次整体下滑。“系统与程序设计”是计算机图书市场的风向标，计算机（纸质）图书市场整体也是下滑的。在随后关于数字发行的部分中，我们还会展示一些更积极的信息。 历年的图书品种 历年的图书销量 下面这张表格列出了各个门类2009年到2010年的增长情况（YoY）、2009和2010年的排名情况（09Rand/10Rank）以及2009、2010年的市场份额百分比（09Share/10Share）。 门类 YoY Growth 09Rank 10Rank 09Share 10Share 商业应用 -05.10% 2nd 2nd 20.60% 21.00% IT人文及其他 04.09% 6th 6th 02.82% 03.15% 消费者操作系统 04.22% 4th 3rd 15.44% 17.27% 数字媒体 -18.32% 5th 5th 10.66% 09.65% 系统与程序设计 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://radar.oreilly.com/2011/02/2010-book-market-2.html">http://radar.oreilly.com/2011/02/2010-book-market-2.html</a><br />
Mike Hendrickson<br />
2011-2-14</p>
<div style="float:right; border:1px solid #ddd;border-width:0 0 1px 1px;padding:0 .5em;margin:0 0 0 .5em;">
<p style="font-weight:bold;">报告内容</p>
<ol>
<li><a href="http://www.cn-cuckoo.com/2011/02/26/2010-book-market-of-usa-1-2287.html">第一部分：市场综述</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-2-2329.html">第二部分：分类市场</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-3-2270.html">第三部分：出版商</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-4-2265.html">第四部分：编程语言</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/25/2010-book-market-of-usa-5-2279.html">第五部分：完结篇及数字出版</a></li>
</ol>
<div style="text-align:center;">
<a href="http://u.115.com/file/f661fc6f86"><br />
<img class="colorbox-2329"  style="border:1px solid #eee;" src="http://ww1.sinaimg.cn/large/61baa48djw6deqves2ryvj.jpg"/><br />
</a><br />
<a href="http://u.115.com/file/f661fc6f86">下载完整版（PDF）</a>
</div>
</div>
<p>在这一部分，我们按照技术门类来分析一下计算机图书的销售情况。</p>
<p>在上一部分，我们把数据分成六大“门类”：系统与程序设计、Web设计与开发、商业应用、数字媒体应用、消费者操作系统与设备、IT人文（Computer Topics）。</p>
<p>这六大门类下面，是一级分类、二级分类、三级分类、四级分类，一共5层。比如说，系统与程序设计门类下面的一级分类有编程语言、数据库、软件工程、通用程序设计、安全，等等。</p>
<p>本部分将对比2010年与2009年的第四季度，也会将2010年与2009年的情况进行对比。</p>
<p>为了方便起见，下面给出了上一部分中展示过的Treemap，其中包含各个门类与一级门类2010年与2009年第四季度的对比。</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/02/cat_yoy_QTR4.jpg"/></p>
<p>这张图中的红、绿、黑色方块基本上反映了市场的波动情况。其中，代表高度增长领域的浅绿色方块非常少。但不要忘了这是2010年第四季度与2009年第四季度的对比。两个最大、最亮的绿色区域是“Android编程”和“Android消费者应用”，这两类图书从2008年微不足道的小方块成长为2010年相当可观的大市场。<br />
<span id="more-2329"></span><br />
下面的两张图可以反映出各图书门类历年的增长情况。第一张图是历年构成Top 3000的图书品种，而第二张图是历年来的图书销量。从中可以看出，“商业应用/人文”和“系统与程序设计”类图书的品种在2010年都增加了，但这两类图书的销量却是双双下跌的。“消费者操作系统与设备”是唯一一个出书品种和销量在2010年双增的门类。“系统与程序设计”是最大的一个门类，但其销售业绩也更加不稳定，2010年经历了最大的一次整体下滑。“系统与程序设计”是计算机图书市场的风向标，计算机（纸质）图书市场整体也是下滑的。在随后关于数字发行的部分中，我们还会展示一些更积极的信息。</p>
<h2>历年的图书品种</h2>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/Cat_family_Titles.jpg"/></p>
<h2>历年的图书销量</h2>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/Cat_family_Units.jpg"/></p>
<p>下面这张表格列出了各个门类2009年到2010年的增长情况（YoY）、2009和2010年的排名情况（09Rand/10Rank）以及2009、2010年的市场份额百分比（09Share/10Share）。</p>
<table border="1">
<tbody>
<tr>
<td align=center><strong>门类</strong></td>
<td><strong>YoY Growth</strong></td>
<td><strong>09Rank</strong></td>
<td><strong>10Rank</strong></td>
<td><strong>09Share</strong></td>
<td><strong>10Share</strong></td>
</tr>
<tr>
<td>商业应用</td>
<td align=right>-05.10%</td>
<td align=right>2nd</td>
<td align=right>2nd</td>
<td align=right>20.60%</td>
<td align=right>21.00%</td>
</tr>
<tr>
<td>IT人文及其他</td>
<td align=right>04.09%</td>
<td align=right>6th</td>
<td align=right>6th</td>
<td align=right>02.82%</td>
<td align=right>03.15%</td>
</tr>
<tr>
<td>消费者操作系统</td>
<td align=right>04.22%</td>
<td align=right>4th</td>
<td align=right>3rd</td>
<td align=right>15.44%</td>
<td align=right>17.27%</td>
</tr>
<tr>
<td>数字媒体</td>
<td align=right>-18.32%</td>
<td align=right>5th</td>
<td align=right>5th</td>
<td align=right>10.66%</td>
<td align=right>09.65%</td>
</tr>
<tr>
<td>系统与程序设计</td>
<td align=right>-03.32%</td>
<td align=right>1st</td>
<td align=right>1st</td>
<td align=right>33.39%</td>
<td align=right>34.62%</td>
</tr>
<tr>
<td>Web 设计与开发</td>
<td align=right>-28.01%</td>
<td align=right>3rd</td>
<td align=right>4th</td>
<td align=right>17.10%</td>
<td align=right>14.32%</td>
</tr>
</tbody>
</table>
<p>在继续深入分析各门类之前，我们先来看看2010年出版的所有计算机图书的书名中出现的关键词。这是一个非常有意思的角度，因为这些关键词会出现在图书封面上、在线搜索中，以及任何相关内容的元数据中。说明一点：我把the、and、it、with等停顿词都过滤掉了。此外，Microsoft这个词因为被用于描述很多种产品，重复次数太多，也被我忽略了。下面就是这张图书市场的“书名关键词”图。</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/titles.jpg"/></p>
<p>深入分析各门类图书后，我们发现在排名前10的二级分类中，有7个在2010年销量少于2009年，前20个二级分类销量净减少了244,936册。换句话说，在相对较大而且一般也较为稳定的领域，图书销量在2010年明显下降。2010年上半年，有49个二级分类销量领先于2009年上半年，而其中有6个二级分类下半年增长放缓、优势尽丧，最终导致全年销量下降。结果，2010年有43个二级分类的销量超过2009年。按照销量增长多少排序，这些二级分类分别是：平板电脑、移动编程、Windows应用、安全、硬件、社交网站、计算机与安全、云计算、信息技术和数据处理。其中平板电脑类图书2010年上半年的销量大约为15,000册，而下半年销量猛增至100,000册。图书品种的增多加快了这种增长——2010年上半年版权输出7种，而下半年底达到22种，是上半年的3倍。而图书销量下降幅度最大的领域依次是：网页制作、数字图像、Mac OS、Flash、Web程序设计、网页设计工具、个人电脑、Linux、软件项目管理和个人数据库。最让我吃惊的领域是Web程序设计。2010年上榜的Web程序设计类图书减少了16种，只有7%的图书销量过千，而这个比例在2009年是11%。</p>
<p>在市场持续下滑的同时，很多出版商的反应就是增加出书的数量，希望通过品种增加来获得市场份额。下面两张图显示的分别是Bookscan监控到的图书品种数，以及所有图书的单品平均销量。有一点似乎并不是显而易见：在没有必要多出那么多种书的情况下，居然有那么多新书说出就出了。这一切可能就要归结为上榜的门槛太低了。换句话说，有几周Top 3000中图书的销量都低到了只有6册。这个问题可以相对来看。前两年，低门槛的问题已经显现了，导致打榜的图书品种虽多，但单品的平均销量却不尽人意。如果市场良性发展，门槛自然会提高，因而只有销售业绩真好的图书才能进入Top 3000。低门槛的结果就是所有出版商的单品平均销量都在明显减少。除了22家最大的出版公司之外，2010年榜上有名的另外18家出版公司也增加了出书品种。然而，这18家出版公司中只有6家在多出书的同时也做到了单品平均销量的增长。因此，关键在于，进入Top 3000行列的图书品种可以增加，但如果进入的门槛太低，那么平均销量和总销量照样也会跟着低。</p>
<h2>图书品种</h2>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/TitlesPerYear.jpg"/></p>
<h2>平均销量</h2>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/AvgUnitsYear.jpg"/></p>
<p>下面这张表格显示了市场被稀释的结果。平均最低值意味着给定年度内每周的“低门槛”水平。平均最高值则是给定年度内每周的最高销量。品种数的含义不言自明。数据显示，平均最低值越高，对应的年度图书品种数越少。总而言之，投入市场的图书品种越多，品种多销量少的稀释效应就会越明显。</p>
<table border="1">
<tbody>
<tr>
<td align=center><strong>年份</strong></td>
<td><strong>平均最低值</strong></td>
<td><strong>平均最高值</strong></td>
<td><strong>品种数</strong></td>
</tr>
<tr>
<td>2004</td>
<td align=right>9.2</td>
<td align=right>1,133</td>
<td align=right>7,451</td>
</tr>
<tr>
<td>2005</td>
<td align=right>9.6</td>
<td align=right>1,099</td>
<td align=right>7,123</td>
</tr>
<tr>
<td>2006</td>
<td align=right>9.6</td>
<td align=right>1,315</td>
<td align=right>6,881</td>
</tr>
<tr>
<td>2007</td>
<td align=right>9.4</td>
<td align=right>1,348</td>
<td align=right>7,092</td>
</tr>
<tr>
<td>2008</td>
<td align=right>8.2</td>
<td align=right>1,534</td>
<td align=right>7,310</td>
</tr>
<tr>
<td>2009</td>
<td align=right>7.3</td>
<td align=right>1,057</td>
<td align=right>7,557</td>
</tr>
<tr>
<td>2010</td>
<td align=right>6.7</td>
<td align=right>1,112</td>
<td align=right>7,792</td>
</tr>
</table>
<p>因此，可以说我们现在正处于技术创新的低迷阶段。有什么技术、平台、方法、理论或者什么新点子，能让我们走出这个市场低迷期吗？还是说，出版商只能一味地多出书，而不管码洋是不是受损失，放任既有的市场空间被多出来的书稀释掉，难道真的就只能这样原地打转、束手无策吗？我认为是后一种情况。只能等有一天，突然大量的需求出现，才能把现在的市场托起来。我说不好这股需求到底是来自云计算、移动应用，还是社会化平台，当然也可能来自其他蓄势待发的潜力领域。未来5年哪个领域可能会有大的增长？有没有酝酿已久的将会对技术世界产生巨大影响的事物？</p>
<p>现在，我们来分析一下构成各大门类的细分类别。下面首先给出了一组趋势图，显示了从2009年1月到2010年12月24个月期间各个主要类别的市场表现。以24个月为周期，能够更清楚地看出某个特定领域是否会受到季节性因素的影响，该领域是不是在持续增长，还是持续下跌。大家在看图的时候，别忘记图中的曲线反映的是相对市场规模，因此纵轴上的市场规模绝对值不容忽视。另外，还有一种简便的判断方法，即曲线在方框图中的位置越高，说明其市场规模越大，反之市场规模越小。有意思的是，消费者操作系统、数字媒体、商业应用与设备这些类别在1月份都有一个很高的起点，这很可能是因为不少读者会因为买了新电脑、新设备或新操作系统，需要看一些入门指南类的图书。这其实就是明显的季节性特征。</p>
<table border=0>
<tr>
<td align=right>IT人文</td>
<td align=right>数字媒体</td>
<td align=right>Web开发与设计</td>
<tr>
<td>
<div class="ap_r"><a href="http://radar.oreilly.com/upload/2011/01/CT.png" class="highslide" onclick="return hs.expand(this)"><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/CT.png" alt="CT.png"/></a></div>
</td>
<td>
<div class="ap_r"><a href="http://radar.oreilly.com/upload/2011/01/DM.png" class="highslide" onclick="return hs.expand(this)"><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/DM.png" alt="DM.png"/></a></div>
</td>
<td>
<div class="ap_r"><a href="http://radar.oreilly.com/upload/2011/01/WD.png" class="highslide" onclick="return hs.expand(this)"><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/WD.png" alt="WD.png"/></a></div>
</td>
</tr>
<td align=right>消费者操作系统与设备</td>
<td align=right>商业应用</td>
<td align=right>系统与程序设计</td>
<tr>
<td>
<div class="ap_r"><a href="http://radar.oreilly.com/upload/2011/01/CO.png" class="highslide" onclick="return hs.expand(this)"><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/CO.png" alt="CO.png"/></a></div>
</td>
<td>
<div class="ap_r"><a href="http://radar.oreilly.com/upload/2011/01/ba.png" class="highslide" onclick="return hs.expand(this)"><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/ba.png" alt="ba.png"/></a></div>
</td>
<td>
<div class="ap_r"><a href="http://radar.oreilly.com/upload/2011/01/SP.png" class="highslide" onclick="return hs.expand(this)"><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/SP.png" alt="SP.png"/></a></div>
</td>
</tr>
</table>
<h1>细分门类分析（以24个月为周期，从2009年1月到2010年12月）</h1>
<p>在理解下面给出的细分门类的相关图表时，请参考上面的各大门类的趋势图。通过这些图表，可以更深入地了解细分门类的市场规模以及季节变化规律。</p>
<h2>门类：消费者操作系统与设备</h2>
<p>以下是消费者操作系统与设备门类下的4个细分门类的趋势线。</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/CO_1.jpg"/><br />
<img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/CO_2.jpg"/></p>
<p>这是一个中等规模的门类，也是两个跨年度增长的门类之一。这个门类的增长主要由Windows 7和便携设备（Port Dev）子类别驱动。便携设备子类别2010年由Android领衔，2009年则是iPhone挂帅。前面我们提到过，平板电脑子类别在2010年下半年的表现非常突出，曲线上扬得很快。Andy Rathbone的新书Windows 7 For Dummies名列榜首，另外两本Windows 7的图书排名第二和第三。婵连销售冠军的<a href="http://oreilly.com/catalog/9780596153298"><em>Mac OS X Snow Leopard: The Missing Manual</em></a>下落到第四位，由于Snow Leopard并非苹果公司的一个重要OS版本，因而相关主题的图书没有像往年一样拉动所属类别的增长。</p>
<p>虽然在Mac OS X爆炸性增长的带动下，这个市场也曾经增长过，但仍然还是不能与Windows图书相比，后者一直都稳居畅销排行榜之列，而且近两年也实现了增长。下面这张柱形图显示了Mac OS与Windows图书历年累计销量的对比情况。你使用PC还是Mac？这张图说明，还是使用PC的读者更多！</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/MacOSxWindows.jpg"/></p>
<h2>门类：商业/办公应用</h2>
<p>2009与2010年商业应用类图书相比，有8个一级分类的表现好于上一年，有23个表现不如上一年。而这23个表现不佳的分类减少的销量比8个增长的分类多卖的销量还要多，相比之下，净损失67,000册，总体增长率为-5.10%。</p>
<p>两个最健康的一级分类是电子表格（Excel）和社交网络（Facebook），分别实现了2.42%和11.49%的增长率。两个最后进的一级分类是图形应用（Graphics Applications）和电子商务，增长率分别是-1.80%和-47.00%。最让我不理解的是，CMS（Content Management Systems，内容管理系统）没有像我想象得那样增长。为此，我查了几年的数据，发现CMS图书增长最快的时期是2006至2009年。而在过去的两年，这个分类的市场保持稳定，与整体市场下滑相比表现还是不错的。下面就是CMS图书历年的销量柱形图。</p>
<h3>CMS图书历年销量</h3>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/CMS_Units_Years.jpg"/></p>
<p>以下是商业/办公门类中三个主要领域的趋势图。</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/BA_1.jpg"/></p>
<p>注意，“办公应用”这个分类的市场规模与另外两个分类（“一般商业应用”和“设计”）相比有多么大。但这个分类的市场表现略有下降，销量从2009年的196,722册下降到2010年的187,968册，增长率为-4.66%。这个增长率（下降率）与计算机整体市场的增长率（-4.54%）相差无几。办公应用类图书的王者是“阿呆系列”（Dummies）。2010年排名前三的都是阿呆系列，而且前10名的位子中阿呆系列占到了7席。想想也难怪，Excel之类的软件又不是什么高端技术，因此阿呆系列图书受到了各类读者的广泛青睐，其中既有闻技术而色变的普通老百姓，也不乏满脑子前沿技术的高端学者。对办公应用这个领域，即使称它为“阿呆帝国”恐怕也不过分。</p>
<h2>门类：Web设计与开发</h2>
<p>2010年与2009年相比，Web设计与开发类图书的销量下降了28.01%，251,000册。但是别忘了，2009年可是整体市场最差的一年了。这个门类中只有两个一级分类实现了增长：JavaScript和社交网站，增长率分别是7.81%和7.18%。我们出的<a href="http://oreil.ly/egQEmN"><em>Learning PHP, MySQL, and JavaScript</em></a>是这个门类中卖得最好的一本书。最令人吃惊的是，网页制作类图书的销量与2009相比，骤减70,000册（别忘了，2009年已经很糟糕了）。是人们的兴趣从HTML转移到PHP、JavaScript和CMS上面去了吗？还是大家都跑去开发能访问自己网页的移动应用了呢？</p>
<p>以下三张趋势图展示的是Web设计与开发门类中三个主要的分类。</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/WD_1.jpg"/></p>
<p>显然，最大的子类别是“网站建设”。这个子类别中占主导地位的图书，讨论的都是性能、可扩展性、可靠性以及调优等，都是能在我们的<a href="http://velocityconf.com/velocity2011">Velocity Conference</a>和<a href="http://bit.ly/bundles/mikehatora/3">推荐书目</a>中找到的话题。富Web界面在各子类别中位居第二，但总体是下滑趋势。在这个类别中，Flash和Silverlight的下降幅度最大。Flash下降幅度为-84.43%，Silverligth是-8.29%。不过，Flash的市场规模仍然是Silverlight的4倍。难道是HTML5让这两种技术失宠了吗？</p>
<h2>门类：系统与程序设计</h2>
<p>这是计算机图书市场中最大的一个门类。这个门类囊括了绝大多数编程语言、数据库和软件开发类的图书。这个门类的正常趋势是年初有一个不错的开端，而后到9月份（大学生开学）又会冲到一个高点。这个门类包含67个子类别，2010年有44个领域出现了负增长，只有23个领域是正增长，正负相抵，2010年整个门类的销量净减少72,024册。算下来，下降幅度只有-3.32%，因此这个大门类的实际市场表现还是好于整体市场的。在增长的二级分类中，位列前5的依次是移动编程、安全、云计算、信息技术和数据处理。业绩最差的二级分类中，前5名分别是：Linux、软件项目管理、个人数据库、Visual Basic和SQL Server。在增长率最高的移动编程领域，iPhone编程是2009年主流，而Android编程则是2010年的热门。注意，这些图书谈论的不是怎么玩iPhone或Droid手机，而是怎么开发移动应用。2009年，iOS类图书的市场规模是Android的9倍，而2010年大约是2.5倍。</p>
<p>以下是系统与程序设计门类中9个一级分类的前3名的趋势图。</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/SyPro_1.jpg"/></p>
<p>注意一下每个分类的整体规模。编程语言（prog lang）仍然是最大的一级分类，而程序设计（prog）已经从第三跃居第二。算上2010年，数据库已经连续下滑了三年了。如前所述，软件项目管理是2010年最大的失败者，但它仍然还是系统与程序设计门类中的第三大二级分类，前面的两个二级分类是移动编程和安全。不过，与SPM（系统与程序设计市场）的整体下滑相对应，移动编程和安全也都是下滑的。另一个从名不见经传成长为可观的二级分类的是数据处理。这个类别中的很多书与我们<a href="http://bit.ly/bundles/mikehatora/4"> Strata Conference及Data Science 资源</a>中的一些访谈、对话和文章是类似的。</p>
<p>第二组一级分类的趋势图与其他门类的一级分类相比，波动不是那么大。这组曲线的趋势虽然平缓，但都带有明显的季节性销售的特征。</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/SyPro_2.jpg"/></p>
<p>对比2009年与2010年全年的数据可以发现，软件工程（sw engr）是这一组中最大的一级分类，经典著作<em>The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition</em>和新经典<em>Coders at Work: Reflections on the Craft of Programming</em>是软件工程类的榜首图书。网络类图书由CompTIA（Computer Technology Industry Association，美国计算机行业协会）的资格认证图书占据，前10名中有5本书都是它们的，包括前2名。</p>
<p>第三组趋势线由CISSP（Certified Information System Security Professional，信息系统安全认证专业人员）、电脑入侵及CompTIA安全类图书主导。</p>
<p><img class="colorbox-2329"  src="http://radar.oreilly.com/upload/2011/01/SyPro_3.jpg"/></p>
<p>接下来的第三部分，将分析出版商，包括赢家和输家。第四部分将对编程语言类图书市场进行更深入的分析。而第五部分将关注数字出版。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/02/22/2010-book-market-of-usa-2-2329.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>2010年美国计算机图书市场报告一：市场综述</title>
		<link>http://www.cn-cuckoo.com/2011/02/21/2010-book-market-of-usa-1-2287.html</link>
		<comments>http://www.cn-cuckoo.com/2011/02/21/2010-book-market-of-usa-1-2287.html#comments</comments>
		<pubDate>Mon, 21 Feb 2011 04:59:35 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[交互设计]]></category>
		<category><![CDATA[出版]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2287</guid>
		<description><![CDATA[http://radar.oreilly.com/2011/02/2010-book-market-1.html Mike Hendrickson 2011-2-10 报告内容 第一部分：市场综述 第二部分：分类市场 第三部分：出版商 第四部分：编程语言 第五部分：完结篇及数字出版 下载完整版（PDF） 自从上一次计算机图书市场报告发表之后的两年来，技术图书市场经历了一些重大的变化。恐怕不少读者根据图书市场日益疲软的征兆，已经看到未来发展的某些趋势了。实际上，我们早就断言过计算机图书销售情况预示着技术发展的趋势，大家也可以搜索一下有关计算机图书市场的其他文章。 本报告的数据来自Bookscan每周监控到的Top 3000图书的销售情况。Bookscan计量的是书店收银机的实际销售数据。换句话说，只要你在美国的书店里买过一本技术书，那么你购买这本书的信息十有八九已经进入了Bookscan的数据库里。Borders、Barnes &#038; Noble以及Amazon等零售商实现了绝大多数技术图书的销售。 图书市场业绩综述 在讨论细分的计算机图书市场之前，我们先来了解一下截止到2011年1月2日这个周末，整个图书市场的表现情况。下面这张表涵盖了从The Girl with the Dragon Tatoo and Eat, Pray, Love to Decision Points到The Ugly Truth的所有图书，只要是印刷、装订并以书的形式销售的，都包含在里面。 整体图书市场表现（所有图书，数据截止日期2011年1月2日） 所有图书，所有题材 青少年非虚构 -0.44% 青少年虚构 -3.46% 青少图书整体 -2.88% 成人非虚构 -1.91% &#160;&#160;&#160;&#160;计算机及互联网 -3.99% 成人虚构 -7.20% 其他 -13.12% 整体市场 -4.54% 从表中可以看出，计算机图书市场较上年下滑了4%。需要注意的是，计算机图书的销售量只占实体和在线书店所有图书总销量的1%。下面这张图展示的整体图书市场各门类的业绩增长情况。其中，幽默（Humor）类图书的增长在普遍低迷的市场背景下显得独树一帜（14.45%）。另一个增长的领域是青少年非虚构类中的教材/教辅类（21.55%）。我很奇怪，为什么教材/教辅类图书居然有这么高的增长率。 整体图书市场增长情况 下面言归正传，看看技术图书市场。下面这张柱形图展示的是各年份累计图书销量的对比情况。从图中可以看出，每年的总销量从2007年开始逐年递减。2007年的时候，不少人认为市场会中止2001年以来一路下跌的态势，开始进入恢复期，但2009年较上一年的跌幅达到了历年最大值。 而下面这张折线图展示的是自2004年以来计算机图书市场每周的市场表现情况（根据我们从Bookscan获得的第一手数据生成）。请注意，这个图反映的是所有出版商，而不仅仅是O&#8217;Reilly。图中稍粗一点的红线表示2010年的数据。 从图中可以看出，我们提到过的明显的季节性特征依然存在。换句话说，每年开始都会强势上扬，然后一直到夏季逐步走低，到了秋天“返校”的季节开始止跌回升，到了年底又强势收尾。图中每年的趋势线都与上一年的趋势线非常接近，甚至连每周的涨跌都十分一致。在24个月来乏善可陈的市场表现中，唯一能让人感到一点慰藉的是，2009年每周销量与上一年度比有10-15%是下降的，而这个比例在2010年减少到了3-6%。但这能说明市场已经见底了吗，还是说我们也像读者思考着怎样才能学到新技术一样处于观望状态？如今，销售图书的方式更多了（后面几部分中会介绍），不少出版商通过不同数字格式来销售相同的内容实现了码洋与销量的双增长。而有些出版商纸质书发行量的降低，也通过数字发行和销售得到了补偿。 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://radar.oreilly.com/2011/02/2010-book-market-1.html">http://radar.oreilly.com/2011/02/2010-book-market-1.html</a><br />
Mike Hendrickson<br />
2011-2-10</p>
<div style="float:right; border:1px solid #ddd;border-width:0 0 1px 1px;padding:0 .5em;margin:0 0 0 .5em;">
<p style="font-weight:bold;">报告内容</p>
<ol>
<li><a href="http://www.cn-cuckoo.com/2011/02/26/2010-book-market-of-usa-1-2287.html">第一部分：市场综述</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-2-2329.html">第二部分：分类市场</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-3-2270.html">第三部分：出版商</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/23/2010-book-market-of-usa-4-2265.html">第四部分：编程语言</a></li>
<li><a href="http://www.cn-cuckoo.com/2011/02/25/2010-book-market-of-usa-5-2279.html">第五部分：完结篇及数字出版</a></li>
</ol>
<div style="text-align:center;">
<a href="http://u.115.com/file/f661fc6f86"><br />
<img class="colorbox-2287"  style="border:1px solid #eee;" src="http://ww1.sinaimg.cn/large/61baa48djw6deqves2ryvj.jpg"/><br />
</a><br />
<a href="http://u.115.com/file/f661fc6f86">下载完整版（PDF）</a>
</div>
</div>
<p>自从上一次计算机图书市场报告发表之后的两年来，技术图书市场经历了一些重大的变化。恐怕不少读者根据图书市场日益疲软的征兆，已经看到未来发展的某些趋势了。实际上，我们早就断言过<a href="http://radar.oreilly.com/archives/2005/04/book_sales_as_a.html">计算机图书销售情况预示着技术发展的趋势</a>，大家也可以搜索一下有关计算机图书市场的其他文章。</p>
<p>本报告的数据来自Bookscan每周监控到的Top 3000图书的销售情况。Bookscan计量的是书店收银机的实际销售数据。换句话说，只要你在美国的书店里买过一本技术书，那么你购买这本书的信息十有八九已经进入了Bookscan的数据库里。Borders、Barnes &#038; Noble以及Amazon等零售商实现了绝大多数技术图书的销售。</p>
<h1>图书市场业绩综述</h1>
<p>在讨论细分的计算机图书市场之前，我们先来了解一下截止到2011年1月2日这个周末，整个图书市场的表现情况。下面这张表涵盖了从<em>The Girl with the Dragon Tatoo and Eat, Pray, Love to Decision Points</em>到<em>The Ugly Truth</em>的所有图书，只要是印刷、装订并以书的形式销售的，都包含在里面。</p>
<p>整体图书市场表现（所有图书，数据截止日期2011年1月2日）</p>
<table border=1 cellpadding=1 cellspacing=3 width=245>
<tr height=13>
<td align=center><strong>所有图书，所有题材</strong></td>
</tr>
</table>
<table border=1 cellpadding=1 cellspacing=3 width=245>
<tr>
<td><strong>青少年非虚构</strong></td>
<td align=right>-0.44%</td>
<tr height=13>
<td height=13  ><strong>青少年虚构<strong></td>
<td align=right>-3.46%</td>
<tr height=13>
<td height=13><strong>青少图书整体</strong></td>
<td align=right>-2.88%</td>
</tr>
<tr height=13>
<td   height=13><strong>成人非虚构</strong></td>
<td align=right>-1.91%</td>
<tr height=13>
<td height=13><font color="red"><strong>&nbsp;&nbsp;&nbsp;&nbsp;计算机及互联网</strong></font></td>
<td align=right>-3.99%</td>
<tr height=13>
<td  ><strong>成人虚构</strong></td>
<td align=right>-7.20%</td>
</tr>
<tr height=13>
<td  ><strong>其他</strong></td>
<td align=right>-13.12%</td>
</tr>
<td align=center><strong>整体市场</strong></td>
<td align=right><strong>-4.54%</strong></td>
</tr>
</table>
<p>从表中可以看出，计算机图书市场较上年下滑了4%。需要注意的是，计算机图书的销售量只占实体和在线书店所有图书总销量的1%。下面这张图展示的整体图书市场各门类的业绩增长情况。其中，幽默（Humor）类图书的增长在普遍低迷的市场背景下显得独树一帜（14.45%）。另一个增长的领域是青少年非虚构类中的教材/教辅类（21.55%）。我很奇怪，为什么教材/教辅类图书居然有这么高的增长率。<br />
<span id="more-2287"></span></p>
<h2>整体图书市场增长情况</h2>
<p><img class="colorbox-2287"  src="http://radar.oreilly.com/WholeMarket.jpg"/></p>
<p>下面言归正传，看看技术图书市场。下面这张柱形图展示的是各年份累计图书销量的对比情况。从图中可以看出，每年的总销量从2007年开始逐年递减。2007年的时候，不少人认为市场会中止2001年以来一路下跌的态势，开始进入恢复期，但2009年较上一年的跌幅达到了历年最大值。</p>
<p><img class="colorbox-2287"  src="http://radar.oreilly.com/upload/2011/02/allYears.jpg"/></p>
<p>而下面这张折线图展示的是自2004年以来计算机图书市场每周的市场表现情况（根据我们从Bookscan获得的第一手数据生成）。请注意，这个图反映的是所有出版商，而不仅仅是O&#8217;Reilly。图中稍粗一点的红线表示2010年的数据。</p>
<p><img class="colorbox-2287"  src="http://radar.oreilly.com/upload/2011/01/sync_2010.jpg"/></p>
<p>从图中可以看出，我们提到过的明显的季节性特征依然存在。换句话说，每年开始都会强势上扬，然后一直到夏季逐步走低，到了秋天“返校”的季节开始止跌回升，到了年底又强势收尾。图中每年的趋势线都与上一年的趋势线非常接近，甚至连每周的涨跌都十分一致。在24个月来乏善可陈的市场表现中，唯一能让人感到一点慰藉的是，2009年每周销量与上一年度比有10-15%是下降的，而这个比例在2010年减少到了3-6%。但这能说明市场已经见底了吗，还是说我们也像读者思考着怎样才能学到新技术一样处于观望状态？如今，销售图书的方式更多了（后面几部分中会介绍），不少出版商通过不同数字格式来销售相同的内容实现了码洋与销量的双增长。而有些出版商纸质书发行量的降低，也通过数字发行和销售得到了补偿。</p>
<p>从上面图中看不来的是，计算机图书市场从2001年开始走下坡路，随后连续3年以20%的比例收缩，直到2004年趋于稳定，规模保持在2000年时的一半左右。（我们手中只有上溯到2004年的可靠数据。）如今，市场从2008年下半年开始又进入下跌通道，一直延续到2010年。前6年的整体市场增长率分别为：2005年1.48%、2006年3.17%、2007年-2.00%、2008年-4.27%、2009年-15.31%、2010年-4.29%。</p>
<p>那么，2010年有什么好消息吗？2010年，有11周的图书销量超过了上一年度。2009年，只有两周的销量超过了上一年度。因此，从这一点来说，似乎能看到市场复苏的某种迹象。2010年最终收关收在了2008年的销量水平上，而且整体市场降速比2009年慢了很多。持乐观态度的人认为，2010年市场已经触底；但持悲观态度的人则认为，这种情况早已有之：看起来好像市场已经跌无可跌了，但随后照样出现了大跌。因此，到底市场会再次走低，还是稳步复苏，都是没有办法确定的。</p>
<p>Treemap工具能够将数据可视化，通过它可以从另一个角度观察市场的表现。Treemap有助于迅速捕捉到市场的趋势信息，即使是几千种图书也不在话下。它的使用方法如下：</p>
<p>图中一个方块表示一个门类的市场份额和相对规模，颜色表示销量变化的速度。红色意味着下降，绿色意味着上升，而颜色的深浅表示变化的幅度（越深越大）。下面这张Treemap的屏幕截图显示了2010年第四季度与2009年第四季度相比，计算机图书市场各门类的损益情况。</p>
<p><img class="colorbox-2287"  src="http://radar.oreilly.com/upload/2011/02/cat_yoy_QTR4.jpg"/></p>
<p>从这些彩色方块中能看出什么来呢？首先别忘了它反映的是2010年第四季度与2009年第四季度的一个对比。这张屏幕截图已经不像去年那张那样“血流成河”了（去年的Treemap到处都是红色），与去年同期相比，2010年第四季度的亮点（浅绿色）不少。位于左上方的Android类图书，与2009年第四季度相比，增长了2,413%。你可能会注意到上部中间还有一个Android方块，呈浅绿色，区别在于中部上方的是面向消费者的书，左上角是Android编程的书。这两类书2010年都比2009年有很大的增长。左上角的iPad图书方块是黑色的，因为2009年还没有iPad图书。不过，方块的大小表明其发展规模一开始就令人印象深刻。</p>
<p>2010年，Windows 7图书增幅最大，其次是iPad，然后是Android（面向消费者的）和Android编程类图书。这是从销量增长来看的，而且这些技术之所以表现如此不俗，很大程度上是因为它们都是相对新的技术，没有大块的市场份额作为比较的基础。对于那些历史较长的技术，安全及网络安全、数字图像类图书的销量增长比较明显。</p>
<p>要看明白图中表示的趋势，可以按照颜色对这些门类加以分组：浅绿色代表“高度增长门类”，深绿及黑色代表“中度增长门类”，所有颜色都是“待观察门类”，红色及浅红色代表“下降门类”。除了“待观察门类”，其他都无须解释。“待观察门类”中包含那些不容易受季节性因素影响的，以及我们O&#8217;Reilly Radar关注的一些门类。您可以把希望添加到我们待观察范围的门类告诉我们。</p>
<p>下面这个表格分析并解释了上面Treemap中的某些数据，数据年份为2010年。其中Share（份额）一栏显示的是相应门类的市场份额，ROC一栏显示的是变化的速度（Rate of Change，RoC = (本期-上期)/上期）。举例来说，Mac OS类图书占整个计算机图书市场的份额为2.95%，收缩速度（RoC）为32.12%。</p>
<table border="1" cellpadding="5" cellspacing="1">
<col />
<col />
<col />
<col />
<tbody>
<tr>
<td><strong>高度增长</strong></td>
<td><strong>Share</strong></td>
<td><strong>ROC</strong></td>
<td><strong>Notes</strong></td>
</tr>
<tr>
<td>Windows 7</td>
<td align="right">05.53%</td>
<td align="right">217.38%</td>
<td>取代了XP和Vista自成一大类。Windows 7是操作系统中的大哥大。</td>
</tr>
<tr>
<td>iPad</td>
<td align="right">01.74%</td>
<td align="right">xx.yy%</td>
<td>这个类别在2009年的时候还没有，但现在已经成为市场份额排名第7的类别了。</td>
</tr>
<tr>
<td>Android编程</td>
<td align="right">00.68%</td>
<td align="right">292.65%</td>
<td>这个类别从2008年开始稳定增长，目前排名第42位，在最快RoC中排名第3位。</td>
</tr>
<tr>
<td>可用性</td>
<td align="right">00.11%</td>
<td align="right">491.50%</td>
<td>这个领域已经不像昔日那么巨大了，但高RoC加速了其增长，使其成为相当大的一个类别。</td>
</tr>
<tr>
<td>Android</td>
<td align="right">00.55%</td>
<td align="right">2493.88%</td>
<td>主要是面向Android消费者的领域，包括用户手册、最佳应用，等等。2008年起从无到有，2009年稳定增长，2010年名列前茅。</td>
</tr>
<tr>
<td><strong>中度增长</strong></td>
<td><strong>Share</strong></td>
<td><strong>ROC</strong></td>
<td><strong>Notes</strong></td>
</tr>
<tr>
<td>jQuery</td>
<td align="right">00.41%</td>
<td align="right">83.79%</td>
<td>规模相当大的一个类别，版权输出从2009年的9本降至2010年的6本，不过销量仍在稳定增长。</td>
</tr>
<tr>
<td>云计算</td>
<td align="right">00.22%</td>
<td align="right">63.58%</td>
<td>一个看涨的门类，2010年8本新书就取得如此成绩。当下出版的主要还是介绍性图书。</td>
</tr>
<tr>
<td>Windows 管理</td>
<td align="right">00.32%</td>
<td align="right">36.29%</td>
<td>中等规模的门类，2010年新书19本，2009年新书16本（2009年出版的图书也含在2010年销售数据中）。</td>
</tr>
<tr>
<td>社交网站</td>
<td align="right">00.21%</td>
<td align="right">30.27%</td>
<td>此类图书在过去两年中翻了一倍，2010年的新书增长了61%，创历年新高。</td>
</tr>
<tr>
<td>网络安全</td>
<td align="right">00.90%</td>
<td align="right">24.25%</td>
<td>安全类图书在2010年表现不错，新书版权稳定输出。</td>
</tr>
<tr>
<td><strong>待观察门类</strong></td>
<td><strong>Share</strong></td>
<td><strong>ROC</strong></td>
<td><strong>Notes</strong></td>
</tr>
<tr>
<td>Office 应用</td>
<td align="right">2.71%</td>
<td align="right">-3.54%</td>
<td>非常大的一个门类，市场一如既往地稳定。即使有所下滑，仍不像整体市场下滑得那样厉害。</td>
</tr>
<tr>
<td>图形图像</td>
<td align="right">05.97%</td>
<td align="right">-17.20%</td>
<td>非常大的一个门类（在Windows之后位居第2），有4本书销量过万；2010年新增图书10本，但销量下降64,581册。</td>
</tr>
<tr>
<td>电子表格</td>
<td align="right">02.99%</td>
<td align="right">-4.66%</td>
<td>第三大门类，有4本书销量过万；2010年新增打榜图书11种，但销量较2009年减少8,754册。</td>
</tr>
<tr>
<td>软件项目管理</td>
<td align="right">02.10%</td>
<td align="right">-15.14%</td>
<td>规模较大也较稳定的一个门类，销量略有下降。PMP及敏捷项目管理类图书最好卖。</td>
</tr>
<tr>
<td><strong>下降门类</strong></td>
<td><strong>Share</strong></td>
<td><strong>ROC</strong></td>
<td><strong>Notes</strong></td>
</tr>
<tr>
<td>Flash</td>
<td align="right">01.12%</td>
<td align="right">-84.43%</td>
<td>在Apple的冲击下，这个门类已经进退维谷，2010年销量比2009年减少59,340册，而2009年销量较2008年减少55,187册。</td>
</tr>
<tr>
<td>Mac OS</td>
<td align="right">02.95%</td>
<td align="right">-32.12%</td>
<td>很大的一个类别，2010年销量减少59,668册。2009年销量比2008年少67,642册。显然是Snow Leopard不给力带来的恶果。</td>
</tr>
<tr>
<td>Web 设计工具</td>
<td align="right">01.37%</td>
<td align="right">-53.20%</td>
<td>这个类别遭受了打击，主要因为Dreamweaver CS5没有随CS4图书的退场而及时跟上。2010年销量减少45,709册。</td>
</tr>
<tr>
<td>Web 程序设计</td>
<td align="right">02.01%</td>
<td align="right">-41.32%</td>
<td>规模较大，2010年新书品种减少了16种。2009年，有56个品种销量过千，而2010年销量过千的品种只有37种。.</td>
</tr>
<tr>
<td>网页制作</td>
<td align="right">04.09%</td>
<td align="right">-27.37%</td>
<td>拥有较大规模，2010年只有4个品种销量过万，而2009年过这个线的有9种。2010年的销量减少了70,492册。.</td>
</tr>
</tbody>
</table>
<p>本报告的第二部分将深入分析这些门类中的各种技术。第三部分将关注出版商，包括赢家和输家。第四部分将主要分析编程语言，第五部分将关注数字销售。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/02/21/2010-book-market-of-usa-1-2287.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>现代浏览器揭秘（草）</title>
		<link>http://www.cn-cuckoo.com/2011/02/18/how-broswers-work-2257.html</link>
		<comments>http://www.cn-cuckoo.com/2011/02/18/how-broswers-work-2257.html#comments</comments>
		<pubDate>Fri, 18 Feb 2011 13:53:46 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2257</guid>
		<description><![CDATA[原文地址：How browsers work 简介 Web浏览器恐怕是用户最多的软件了。本文将介绍浏览器的工作原理。想知道从你在地址栏中输入“google.com”，到窗口中显示Google主页的过程中都发生了什么？本文会为你揭开这个秘密。 要讨论的浏览器 今天，人们主要使用5种浏览器：Internet Explorer、Firefox、Safari、Chrome和Opera。这篇文章的分析源自开源浏览器——Firefox、Chrome和Safari，Safari是部分开源的。根据W3C对浏览器使用情况的统计信息，当前（2009年10）Firefox、Safari和Chrome共同的市场占有率已接近60%。因此，可以说开源浏览器已经占据了浏览器市场的半壁江山。 浏览器的主要功能 浏览器的主要功能就是呈现你选择的网络资源，换句话说，就是你向服务器请求资源，然后浏览器把它们显示在自己的窗口中。资源的格式通常是HTML，当然也有PDF、图像等等。资源的位置是使用URI（Uniform Resource Indentifier，统一资源标识符）来指定的。与此相关的内容在后面讨论网络的时候还会详细介绍。 浏览器如何解释HTML文件是由HTML和CSS规范规定的。这些规范是由W3C（World Wide Web Consortium，万维网联盟）维护的，W3C是负责制定Web标准的组织。 HTML当前的版本号是4（http://www.w3.org/TR/html401/），HTML5还在制定中。CSS当前的版本号是2（http://www.w3.org/TR/CSS2/），CSS3也正在制定过程中。 多少年来，浏览器厂商各自为战，纷纷埋头开发自己的扩展，对规范的支持始终不给力。结果就给Web开发人员带来了生死攸关的兼容性问题。而今天，大多数浏览器对规范的支持程度仍然参差不齐。 浏览器的用户界面大同小异，其中相同的界面元素包括： 用于输入URI的地址栏 后退和前进按钮 书签选项 用于刷新和停止加载当前文档的刷新及停止按钮 返回主页的主页按钮 说来也怪，并没有哪个正式公布的规范对浏览器的用户界面作出规定，浏览器目前的外观是多年来浏览器厂商之间互相模仿和不断改进的结果。HTML5规范中没有定义浏览器必须具备的UI元素，但列出了一些公共元素，其中就包括地址栏、状态栏和工具栏。当然，有些浏览器还有自己专有的一些功能，如Firefox的下载管理器。相关的更多内容将在后面讨论用户界面时介绍。 浏览器的主要构成 以下是构成浏览器的主要组件（参考1.1）。 1、用户界面——包括地址栏、后退/前进按钮、书签菜单等等，也就是除了显示所请求页面的主窗口之外的其他所有部分。 2、浏览器引擎——用于查询和操作呈现（rendering）引擎的接口。 3、呈现引擎——负责显示请求的内容，例如请求的内容是HTML，它就负责解析HTML和CSS并将解析后的内容显示到屏幕上。 4、网络模块——用于完成网络调用，如HTTP请求。具有平台中立的接口和针对不同平台的底层实现。 5、UI后端——用于绘制基本的组合选择框及对话框之类的基本部件。具有不特定于某个平台的界面样式，在底层使用的是操作系统的用户界面方法。 6、JavaScript解释器——用户解释和执行JavaScript代码。 7、数据存储模块——属于持久层；浏览器需要在硬盘中保存各种数据，如Cookie。HTML5还为客户端存储定义了新的技术。 图1 浏览器的主要组件 需要特别指出的是，Chrome会为每个新建的标签页创建一个新的呈现引擎的实例，并且每个标签页也运行在独立的进程当中，这一点与其他浏览器不一样。对构成浏览器的这些组件，我们会逐一详细讨论。 组件之间的通信 Firefox和Chroem都具有联系各个组件的组件。后面也将讨论这些组件。 呈现引擎 呈现引擎主要负责……呈现，也就是把请求的内容显示到浏览器屏幕上。 默认情况下，呈现引擎可以显示HTML和XML文档以及图像。而借助插件（一种浏览器扩展）它还可以显示其他类型的内容，比如使用PDF阅读器插件可以显示PDF。后面我们还会专门讨论插件和扩展，但这里我们只讨论呈现引擎的主要用途——显示使用CSS格式化之后的HTML及图像。 呈现引擎 前面提到的浏览器（Firefox、Chrome和Safari）是构建在两个呈现引擎之上的。Firefox使用Gecko——Mozilla自己开发的一个呈现引擎；Safari和Chrome都使用Webkit。 Webkit是一个开源的呈现引擎，最早是为Linux平台开发的，后来由苹果公司移植到Mac和Windows平台。有关内容请参考http://webkit.org。 主流程 呈现引擎首先通过网络层取得被请求文档的内容。通常是以8K分块的方式完成。 取得内容之后，呈现引擎的基本工作流如下图所示： 图2 呈现引擎的基本工作流 呈现引擎会开始解析HTML文档，并将HTML标签转换成“内容树”中的DOM节点。然后，它开始解析样式数据，包括外部CSS文件和style元素中的样式。解析后的样式信息，再加上HTML中的视觉指令，将被用于创建另一个树——呈现器树。 呈现器树中包含着各种矩形，每个矩形都有颜色和大小等属性。这些矩形都按照显示在屏幕上的顺序排列好了。 在构建完呈现器树之后，呈现引擎要完成一个“布局”过程。在这个过程中，它会精确地确定每个节点在屏幕上出现时的坐标。紧接着的一个阶段就是绘制——遍历呈现器树，并使用UI后端层将所有节点逐个绘制出来。 上述过程是逐步完成的，认识到这一点很重要。为了获得更好的用户体验，呈现引擎会尽可能早地将内容呈示到屏幕上。换句话说，它不会等到把所有HTML标签都解析完毕之后再去构建和布局呈现器树，而是解析完一部分内容，就显示一部分内容；与此同时，剩余内容可能还在通过网络不断下载的过程中。 主流程示例 [...]]]></description>
			<content:encoded><![CDATA[<p>原文地址：<a href="http://taligarsiel.com/Projects/howbrowserswork1.htm">How browsers work</a></p>
<h2>简介</h2>
<p>Web浏览器恐怕是用户最多的软件了。本文将介绍浏览器的工作原理。想知道从你在地址栏中输入“google.com”，到窗口中显示Google主页的过程中都发生了什么？本文会为你揭开这个秘密。</p>
<h4>要讨论的浏览器</h4>
<p>今天，人们主要使用5种浏览器：Internet Explorer、Firefox、Safari、Chrome和Opera。这篇文章的分析源自开源浏览器——Firefox、Chrome和Safari，Safari是部分开源的。根据W3C对浏览器使用情况的统计信息，当前（2009年10）Firefox、Safari和Chrome共同的市场占有率已接近60%。因此，可以说开源浏览器已经占据了浏览器市场的半壁江山。</p>
<h4>浏览器的主要功能</h4>
<p>浏览器的主要功能就是呈现你选择的网络资源，换句话说，就是你向服务器请求资源，然后浏览器把它们显示在自己的窗口中。资源的格式通常是HTML，当然也有PDF、图像等等。资源的位置是使用URI（Uniform Resource Indentifier，统一资源标识符）来指定的。与此相关的内容在后面讨论网络的时候还会详细介绍。</p>
<p>浏览器如何解释HTML文件是由HTML和CSS规范规定的。这些规范是由W3C（World Wide Web Consortium，万维网联盟）维护的，W3C是负责制定Web标准的组织。</p>
<p>HTML当前的版本号是4（http://www.w3.org/TR/html401/），HTML5还在制定中。CSS当前的版本号是2（http://www.w3.org/TR/CSS2/），CSS3也正在制定过程中。</p>
<p>多少年来，浏览器厂商各自为战，纷纷埋头开发自己的扩展，对规范的支持始终不给力。结果就给Web开发人员带来了生死攸关的兼容性问题。而今天，大多数浏览器对规范的支持程度仍然参差不齐。</p>
<p>浏览器的用户界面大同小异，其中相同的界面元素包括：</p>
<ul>
<li>用于输入URI的地址栏</li>
<li>后退和前进按钮</li>
<li>书签选项</li>
<li>用于刷新和停止加载当前文档的刷新及停止按钮</li>
<li>返回主页的主页按钮</li>
</ul>
<p>说来也怪，并没有哪个正式公布的规范对浏览器的用户界面作出规定，浏览器目前的外观是多年来浏览器厂商之间互相模仿和不断改进的结果。HTML5规范中没有定义浏览器必须具备的UI元素，但列出了一些公共元素，其中就包括地址栏、状态栏和工具栏。当然，有些浏览器还有自己专有的一些功能，如Firefox的下载管理器。相关的更多内容将在后面讨论用户界面时介绍。<br />
<span id="more-2257"></span></p>
<h4>浏览器的主要构成</h4>
<p>以下是构成浏览器的主要组件（参考<a href="#2257-1">1.1</a>）。<br />
1、用户界面——包括地址栏、后退/前进按钮、书签菜单等等，也就是除了显示所请求页面的主窗口之外的其他所有部分。<br />
2、浏览器引擎——用于查询和操作呈现（rendering）引擎的接口。<br />
3、呈现引擎——负责显示请求的内容，例如请求的内容是HTML，它就负责解析HTML和CSS并将解析后的内容显示到屏幕上。<br />
4、网络模块——用于完成网络调用，如HTTP请求。具有平台中立的接口和针对不同平台的底层实现。<br />
5、UI后端——用于绘制基本的组合选择框及对话框之类的基本部件。具有不特定于某个平台的界面样式，在底层使用的是操作系统的用户界面方法。<br />
6、JavaScript解释器——用户解释和执行JavaScript代码。<br />
7、数据存储模块——属于持久层；浏览器需要在硬盘中保存各种数据，如Cookie。HTML5还为客户端存储定义了新的技术。</p>
<p><img class="colorbox-2257"  src="http://cache.cn-cuckoo.com/img/2257_layers.png"/><br />
图1 浏览器的主要组件</p>
<p>需要特别指出的是，Chrome会为每个新建的标签页创建一个新的呈现引擎的实例，并且每个标签页也运行在独立的进程当中，这一点与其他浏览器不一样。对构成浏览器的这些组件，我们会逐一详细讨论。</p>
<h4>组件之间的通信</h4>
<p>Firefox和Chroem都具有联系各个组件的组件。后面也将讨论这些组件。</p>
<h2>呈现引擎</h2>
<p>呈现引擎主要负责……呈现，也就是把请求的内容显示到浏览器屏幕上。</p>
<p>默认情况下，呈现引擎可以显示HTML和XML文档以及图像。而借助插件（一种浏览器扩展）它还可以显示其他类型的内容，比如使用PDF阅读器插件可以显示PDF。后面我们还会专门讨论插件和扩展，但这里我们只讨论呈现引擎的主要用途——显示使用CSS格式化之后的HTML及图像。</p>
<h4>呈现引擎</h4>
<p>前面提到的浏览器（Firefox、Chrome和Safari）是构建在两个呈现引擎之上的。Firefox使用Gecko——Mozilla自己开发的一个呈现引擎；Safari和Chrome都使用Webkit。<br />
Webkit是一个开源的呈现引擎，最早是为Linux平台开发的，后来由苹果公司移植到Mac和Windows平台。有关内容请参考http://webkit.org。</p>
<h4>主流程</h4>
<p>呈现引擎首先通过网络层取得被请求文档的内容。通常是以8K分块的方式完成。</p>
<p>取得内容之后，呈现引擎的基本工作流如下图所示：</p>
<p><img class="colorbox-2257"  src="http://cache.cn-cuckoo.com/img/2257_flow.jpg"/><br />
图2 呈现引擎的基本工作流</p>
<p>呈现引擎会开始解析HTML文档，并将HTML标签转换成“内容树”中的<a href="#dom">DOM</a>节点。然后，它开始解析样式数据，包括外部CSS文件和style元素中的样式。解析后的样式信息，再加上HTML中的视觉指令，将被用于创建另一个树——<a href="render_tree">呈现器树</a>。</p>
<p>呈现器树中包含着各种矩形，每个矩形都有颜色和大小等属性。这些矩形都按照显示在屏幕上的顺序排列好了。</p>
<p>在构建完呈现器树之后，呈现引擎要完成一个“<a href="layout">布局</a>”过程。在这个过程中，它会精确地确定每个节点在屏幕上出现时的坐标。紧接着的一个阶段就是<a href="painting">绘制</a>——遍历呈现器树，并使用UI后端层将所有节点逐个绘制出来。</p>
<p>上述过程是逐步完成的，认识到这一点很重要。为了获得更好的用户体验，呈现引擎会尽可能早地将内容呈示到屏幕上。换句话说，它不会等到把所有HTML标签都解析完毕之后再去构建和布局呈现器树，而是解析完一部分内容，就显示一部分内容；与此同时，剩余内容可能还在通过网络不断下载的过程中。</p>
<h6>主流程示例</h6>
<p><img class="colorbox-2257"  src="http://cache.cn-cuckoo.com/img/2257_webkitflow.png" /><br />
图3　Webkit的主流程</p>
<p><img class="colorbox-2257"  src="http://cache.cn-cuckoo.com/img/2257_geckoflow.jpg" /><br />
图4　Mozilla的Gecko呈现引擎的主流程</p>
<p>从图3和图4可以看出，Webkit和Gecko使用的术语稍有不同，但整个流程基本上是相同的。</p>
<p>Gecko把可见的格式化元素的树叫做“框架树”（Frame tree），每个元素都是一个框架。而Webkit使用的则是“呈现器树”（Render tree），这个树由“呈现器对象”构成。Webkit把排列元素叫做“布局”，而Gecko则称该过程为“重排”（Reflow）。同样，“附加”（Attachment）则是Webkit对连接DOM节点与样式信息以创建呈现器树的称呼。Gecko还有一个与语义无关的小差别，它在HTML与DOM树之间加了一层，叫做“内容渗入”（Content Sink），相当于一个制造DOM元素的工厂。下面我们就来解释流程中的每个阶段。</p>
<h6>基本解析</h6>
<p>由于解析是呈现引擎的一项非常重要的工作，因此我们会讨论得比较深入一些。首先来简单地介绍一下解析。</p>
<p>所谓的解析文档，就是把文档转换成具有某种意义的结构，以便代码能够被理解和使用。解析之后的结果通常是一个节点树，与文档的结构对应。这个节点树叫做解析树或语法树。</p>
<p>举个例子，解析表达式“2+3-1”会返回下面这个树：</p>
<p><img class="colorbox-2257"  src="http://cache.cn-cuckoo.com/img/2257_nodetree.png"/><br />
图5 算术表达式的节点树</p>
<h6>文法</h6>
<p>解析是依据文档所遵循的语法规则进行的，取决于编写文档的语言和格式。每一个要解析的格式都必须具有由词汇表和语法规则构成的文法。这种文法称为<a href="#context_free_grammer">上下文无关的文法</a>。人类语言不具备上下文无关的文法，因此无法使用通常的解析技术来解析。</p>
<h6>解析器——加上词法分析器</h6>
<p>解析可以分为两个步骤：词法分析（lexical analysis）和语法分析（syntax analysis）。</p>
<p>词法分析指的是把输入分解成符号。符号来自语言的词汇表——基本有效单元的集合。对人类语言来说，符号就相当于我们字典中的那些单词。</p>
<p>语法分析就是使用语言的语法规则进行分析。</p>
<p>解析器通常把这两项工作交给两个组件来完成：词法分析器（有时也叫分词器）负责把输入分解成符号，而解析器则负责依据语言的语法规则来分析文档结构，然后构建起解析树。词法分析器知道如何剥离空格、换行等无关字符。</p>
<p><img class="colorbox-2257"  src="http://cache.cn-cuckoo.com/img/2257_parsetree.png"/><br />
图6 从源文档到解析树</p>
<p>解析过程是迭代进行的。解析器通常会要求词法分析器给出一个新的符号，然后使用该符号去匹配某种语法规则。如果匹配成功，则将与该符号对应的节点添加到解析树中，然后继续要求词法分析器再提供另一个符号。</p>
<p>如果匹配不成功，解析器会在内部保存当前符号，然后继续从词法分析器那里获取符号，直到所有内部保存的符号能够匹配一项语法规则为止。如果最终都没有找到匹配的规则，解析器就会抛出异常。这就意味着文档无效，或者说包含语法错误。</p>
<h6>转换</6><br />
（待续2011/2/20）</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/02/18/how-broswers-work-2257.html/feed</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
		<item>
		<title>解构JavaScript库：jQuery、Prototype、Mootools</title>
		<link>http://www.cn-cuckoo.com/2011/01/06/javascript-libraries-deconstructed-2231.html</link>
		<comments>http://www.cn-cuckoo.com/2011/01/06/javascript-libraries-deconstructed-2231.html#comments</comments>
		<pubDate>Thu, 06 Jan 2011 15:25:09 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=2231</guid>
		<description><![CDATA[JavaScript库“解构”系列旨在以可视化和可交互的方式剖析JavaScript库的源代码，包括 jQuery、Prototype 和 MooTools。 通过将 JavaScript 源代码以可见块元素的方式标记出来，可以更方便查找和学习。点开每个块元素，即可查看相应的代码。点击代码中的链接，即可在程序流中纵情畅游。 ——Dave Stewart 这么好的东西，还用多说吗？你懂的。（见下图） 唯一需要说明的是，Dave Stewart原创的这个JavaScript库解构系列不知何故被挡在“墙”外头了，我只是把它搬到了“墙”里面来而已。换句话说，这个解构系列并非出自我手，我只是在自己的站点上提供了它的一个镜像，以方便“墙”内的JavaScript学习者参考。 现在就开始：解构JavaScript库!]]></description>
			<content:encoded><![CDATA[<div style="padding:1em;background:#eee;">JavaScript库“解构”系列旨在以可视化和可交互的方式剖析JavaScript库的源代码，包括 jQuery、Prototype 和 MooTools。<br />
通过将 JavaScript 源代码以可见块元素的方式标记出来，可以更方便查找和学习。点开每个块元素，即可查看相应的代码。点击代码中的链接，即可在程序流中纵情畅游。</p>
<div style="text-align:right;margin-right:2em;font-family:Arial, Helvetica, sans-serif;">——Dave Stewart</div>
</div>
<p>这么好的东西，还用多说吗？你懂的。（见下图）<br />
唯一需要说明的是，Dave Stewart原创的这个JavaScript库解构系列不知何故被挡在“墙”外头了，我只是把它搬到了“墙”里面来而已。换句话说，这个解构系列并非出自我手，我只是在自己的站点上提供了它的一个镜像，以方便“墙”内的JavaScript学习者参考。</p>
<p>现在就开始：<a href="http://www.cn-cuckoo.com/deconstructed/">解构JavaScript库</a>!<br />
<a href="http://www.cn-cuckoo.com/main/wp-content/uploads/2011/01/jquery.png"><img src="http://www.cn-cuckoo.com/main/wp-content/uploads/2011/01/jquery_small.png" alt="" title="jquery" class="aligncenter size-full wp-image-2239 colorbox-2231" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2011/01/06/javascript-libraries-deconstructed-2231.html/feed</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
		<item>
		<title>在Windows平台的Apache中配置Python</title>
		<link>http://www.cn-cuckoo.com/2010/08/19/run-python-in-apache-by-use-mod_python-1960.html</link>
		<comments>http://www.cn-cuckoo.com/2010/08/19/run-python-in-apache-by-use-mod_python-1960.html#comments</comments>
		<pubDate>Thu, 19 Aug 2010 11:25:35 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程技术]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=1960</guid>
		<description><![CDATA[要在Windows平台的Apache中使用Python，当然必须得先安装Apache和Python。Apache我使用的是XAMPP，而Python则随便一搜，就可以找到下载链接。由于这个解决方案要通过安装Apache模块mod_python来实现，而mod_python的当前版本3.3.1只支持Apache 2.2和Python 2.5，所以不得不先缷载已经装好的Python 3.0，重新下载安装了Python 2.5。mod_python是一个Apache模块，它可以将Python解释器嵌入到Apache服务器中（详情可以看这里）。 让Apache支持Python的过程很简单，只要3步。 下载mod_python模块安装程序（注意文件名后面Python和Apache的版本号要与自己已经安装的版本一致；文件名前面的版本号则是mod_python的，文件名示例：mod_python-3.3.1.win32-py2.5-Apache2.2.exe），然后安装，安装向导会自动找到Python路径，但可能需要我们手工指定Apache路径，安装到最后，向导还会提示你如何修改Apache配置文件（参见下一步）并给出了后续步骤的英文说明。 让Apache加载mod_python模块。在Apache安装目录下找到其配置文件apache\conf\httpd.conf，打开，搜“LoadModule”，找到加载模块的地方，然后添加一条语句：LoadModule python_module modules/mod_python.so，重新启动Apache。 在htdocs目录下新建一个目录，如：“py”。进入py目录，新建一个文本文件，并命名为“.htaccess”，加入下列3条指令： AddHandler mod_python .py PythonHandler mptest PythonDebug On 这里第一条指令是将所有URL末尾为.py的请求转发给mod_python处理程序，mod_python接收到请求之后再寻找适当的PythonHandler处理程序。第二条指令只定义了一个mptest处理程序。最后一条是启用Python代码调试功能，以便在代码运行出错时输出Python解释器返回的错误。 完成以上3步之后，就可以编写Python文件并进行测试了。在py目录下新建 mptest.py 文件，打开后添加如下代码： from mod_python import apache def handler(req): req.content_type = &#039;text/plain&#039; req.write(&#34;Hello World!&#34;) return apache.OK 保存。打开浏览器，输入http://localhost/py/mptest.py，回车。看到“Hello World！”了吗？ 实际上，由于前面只明确将mptest设置为处理程序，所以无论浏览器URL中的.py文件名是什么（如：login.py、default.py），都将被转发给mptest.py文件来处理，都会返回“Hello World！”。怎么办呢？长话短说，可以将上面第3步中的代码替换成如下所示： AddHandler mod_python .py PythonHandler mod_python.publisher PythonDebug On 更多内容，参见Mod_python Manual和Introducing mod_python。]]></description>
			<content:encoded><![CDATA[<p>要在Windows平台的Apache中使用Python，当然必须得先安装Apache和Python。Apache我使用的是<a title="下载XAMPP" href="http://www.apachefriends.org/zh_cn/xampp.html" target="_blank">XAMPP</a>，而Python则随便一搜，就可以找到下载链接。由于这个解决方案要通过安装Apache模块mod_python来实现，而mod_python的当前版本3.3.1只支持Apache 2.2和Python 2.5，所以不得不先缷载已经装好的Python 3.0，重新下载安装了Python 2.5。mod_python是一个Apache模块，它可以将Python解释器嵌入到Apache服务器中（<a title="http://www.modpython.org/" href="http://www.modpython.org/" target="_blank">详情可以看这里</a>）。</p>
<p>让Apache支持Python的过程很简单，只要3步。</p>
<ol>
<li><a title="下载mod_python安装文件" href="http://www.apache.org/dist/httpd/modpython/win/" target="_blank">下载mod_python模块安装程序</a>（注意文件名后面Python和Apache的版本号要与自己已经安装的版本一致；文件名前面的版本号则是mod_python的，文件名示例：<strong>mod_python-3.3.1</strong>.win32-<strong>py2.5</strong>-<strong>Apache2.2</strong>.exe），然后安装，安装向导会自动找到Python路径，但可能需要我们手工指定Apache路径，安装到最后，向导还会提示你如何修改Apache配置文件（参见下一步）并给出了<a title="修改完Apache配置文件（httpd.conf）后，看这个链接。" href="http://www.modpython.org/live/current/doc-html/inst-testing.html" target="_blank">后续步骤的英文说明</a>。</li>
<li>让Apache加载mod_python模块。在Apache安装目录下找到其配置文件apache\conf\httpd.conf，打开，搜“LoadModule”，找到加载模块的地方，然后添加一条语句：<strong>LoadModule python_module modules/mod_python.so</strong>，重新启动Apache。</li>
<li>在htdocs目录下新建一个目录，如：“py”。进入py目录，新建一个文本文件，并命名为“.htaccess”，加入下列3条指令：
<pre class="brush: py; ">

AddHandler mod_python .py
PythonHandler mptest
PythonDebug On
</pre>
<p>这里第一条指令是将所有URL末尾为.py的请求转发给mod_python处理程序，mod_python接收到请求之后再寻找适当的PythonHandler处理程序。第二条指令只定义了一个mptest处理程序。最后一条是启用Python代码调试功能，以便在代码运行出错时输出Python解释器返回的错误。</li>
</ol>
<p>完成以上3步之后，就可以编写Python文件并进行测试了。在py目录下新建 mptest.py 文件，打开后添加如下代码：</p>
<pre class="brush: py; ">

from mod_python import apache

def handler(req):
	req.content_type = &#039;text/plain&#039;
	req.write(&quot;Hello World!&quot;)
	return apache.OK
</pre>
<p>保存。打开浏览器，输入http://localhost/py/mptest.py，回车。看到“Hello World！”了吗？</p>
<p>实际上，由于前面只明确将mptest设置为处理程序，所以无论浏览器URL中的.py文件名是什么（如：login.py、default.py），都将被转发给mptest.py文件来处理，都会返回“Hello World！”。怎么办呢？长话短说，可以将上面第3步中的代码替换成如下所示：</p>
<pre class="brush: py; ">

AddHandler mod_python .py
PythonHandler mod_python.publisher
PythonDebug On
</pre>
<p>更多内容，参见<a href="http://www.modpython.org/live/current/doc-html/modpython.html"><em>Mod_python Manual</em></a>和<a href="http://onlamp.com/pub/a/python/2003/10/02/mod_python.html"><em>Introducing mod_python</em></a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2010/08/19/run-python-in-apache-by-use-mod_python-1960.html/feed</wfw:commentRss>
		<slash:comments>36</slash:comments>
		</item>
		<item>
		<title>你是那10%可以实现二分查找算法的程序员吗？</title>
		<link>http://www.cn-cuckoo.com/2010/04/20/are-you-one-of-the-10-percent-of-programmers-who-can-write-a-binary-search-1530.html</link>
		<comments>http://www.cn-cuckoo.com/2010/04/20/are-you-one-of-the-10-percent-of-programmers-who-can-write-a-binary-search-1530.html#comments</comments>
		<pubDate>Tue, 20 Apr 2010 08:32:43 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=1530</guid>
		<description><![CDATA[——《编程珠玑》引发的编程竞赛 原文链接：http://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/迈克·泰勒（Mike Taylor），2010年4月19日翻译完成：2010年4月20日 有一些讲编程的图书，我会从头到尾、一字不落地反复研读；还有一些讲编程的图书，我已经看过好几遍了，但每次差不多都是只看其中的一章。乔恩·本特利（Jon Bentley）1986年的经典名著《编程珠玑》（Programming Pearls）则是少数几本能同时归入上述两类的编程图书之一，我手里这本书的封面已经严重磨损，下图可以为证（点击看大图，注意左下角。——译者注）。 （我有这本书的第1版[amazon.com，amazon.co.uk]，上面就是扫描的这一版的封面，好像我该买一本又新又便宜的第2版了[amazon.com，amazon.co.uk]，第2版比第1版多了3章内容。） 我打算最近再专门写一篇关于这本书的文章（我已经为Coders at Work、The Elements of Programming Style、Programming the Commodore 64 和The C Programming Language专门写了几篇书评），但今天我只想就这本书中的几段话谈谈自己的想法。这几段内容有点骇人听闻。 只有10%的程序员可以写出二分查找 每次翻开《编程珠玑》，我都会先看一看下面这几段文字： 二分查找可以解决（预排序数组的查找）问题：只要数组中包含T（即要查找的值），那么通过不断缩小包含T的范围，最终就可以找到它。一开始，范围覆盖整个数组。将数组的中间项与T进行比较，可以排除一半元素，范围缩小一半。就这样反复比较，反复缩小范围，最终就会在数组中找到T，或者确定原以为T所在的范围实际为空。对于包含N个元素的表，整个查找过程大约要经过log(2)N次比较。 多数程序员都觉得只要理解了上面的描述，写出代码就不难了；但事实并非如此。如果你不认同这一点，最好的办法就是放下书本，自己动手写一写。试试吧。 我在贝尔实验室和IBM的时候都出过这道考题。那些专业的程序员有几个小时的时间，可以用他们选择的语言把上面的描述写出来；写出高级伪代码也可以。考试结束后，差不多所有程序员都认为自己写出了正确的程序。于是，我们花了半个钟头来看他们编写的代码经过测试用例验证的结果。几次课，一百多人的结果相差无几：90%的程序员写的程序中有bug（我并不认为没有bug的代码就正确）。 我很惊讶：在足够的时间内，只有大约10%的专业程序员可以把这个小程序写对。但写不对这个小程序的还不止这些人：高德纳在《计算机程序设计的艺术 第3卷 排序和查找》第6.2.1节的“历史与参考文献”部分指出，虽然早在1946年就有人将二分查找的方法公诸于世，但直到1962年才有人写出没有bug的二分查找程序。 ——乔恩·本特利，《编程珠玑（第1版）》第35-36页 几个小时！90%！老兄，严肃点！难道这还不够骇人听闻吗？ 之所以想看这本书的第2版，原因之一就是想看看这几段文字有没有修订过，看看从1986年到1999年出第2版，这个数字有没有变化。直觉告诉我，这个数字一定向好的方向变化了，事物都是向好的方向发展的嘛。但理性却告诉我，在一个程序员把更多的时间都花在摆弄库上，而不是编写实际代码的时代，重现核心算法的能力即使有也一定会弱化。别忘了，本特利提到的那些家伙可都不是等闲之辈，他们都是贝尔实验室和IBM的专业人员。所以，我们有理由相信他们的成绩实际上已经是最好的了。 好，下面就做一个二分查找的测验 我跟你一样（如果你是这么想的），想马上就试一试。（好啦，不是马上。先看完这篇文章！）我相信看这篇文章的人都知道什么是二分查找算法，即使你不知道，上面引用的本特利的描述也应该够了。请你打开编辑器，编写一个二分查找例程。什么时候觉得没有任何问题了，保留那个版本。然后测试，然后通过在下面留言的方式告诉我你是不是第一次就做对了。我们肯定能打破本特利10%的纪录吗？ 规则如下。 使用你喜欢的任何编程语言。 不要剪切粘贴或以任何方式复制别人的代码。甚至在你写完之前，都不要参考其他的二分查找代码。 甚至于我不得不强调，别调用bsearch()，或使用其他瞒天过海的手法 时间自己来定：5分钟不短——只要你能保证写完写对；8小时不长——只要你愿意（而且有那么多闲工夫）。 可以使用编译器消除一些无意识的错误，如语法错误或变量初始化失败，但…… 在确定程序正确之前不要测试。 最后，也是最重要的：如果决定参与这次测验，就必须报告。成功也好，失败也罢，甚至半途而废也要给我个话儿。否则，就无法保证测验结果的准确性了。 （考虑到这只是一次测验，可以忽略计算索引时导致的数值溢出。这里描述了相应的情形，但打算参加这次测验的人在编完程序之前不要看，因为那篇文章里包含一个正确的二分查找的实现，想洁身自好的朋友一定是不屑为之的。） 如果你的代码经验证确实正确，那么如果你愿意的话，欢迎你在留言里贴出自己的代码。不过，假如你这样做了，而后来的留言给你挑出了bug，请你一定想好怎样为维护自己的形象而自圆其说 更酷的玩法：对于那些信心十足的人，如果你真敢肯定自己的程序没有问题，可以先把代码贴在留言里，然后再测试。当然，你必须要在留言里说明这一点，以便大家发现你的bug时，会考虑多少给你留些情面。 我会在某个时间总结一下这次测试的结果——比如说，一周以后。 行动吧！ 第一次更新（一个半小时后） 感谢朋友们的积极响应，这么快就有那么多留言！我得提醒大家，WordPress的留言系统会解释HTML，因此会吞掉类似下面的代码段 if a[mid] &#60; value 最好的办法就是把源代码放在{source}&#8230;{source}标签之间，注意用方括号代替这里的花括号。（我第一次想告诉大家这一点时，使用的是方括号，结果我写的规避标记的说明，反而被当成了标记——悲哀呀！）这样做还可以保留缩进，否则我还不知道有什么办法可以做到这一点。 替WordPress向大家致歉：我真的希望这个平台允许留言者预览留言和/或在发表后还能编辑留言，这样就可以避免出现乱七八糟的代码了。我也想了办法自己动手解决这个问题，但WordPress不仅会错误地显示带有&#60;符号的代码，它实际上会丢弃该符号后面的所有内容，唉，我想我是没折了。 [...]]]></description>
			<content:encoded><![CDATA[<p style="font-size: 24px;">——《编程珠玑》引发的编程竞赛</p>
<p style="text-align: right;">原文链接：<a title="Are you one of the 10% of programmers who can write a binary search?" href="http://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/" target="_blank">http://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/</a><br/><a title="Mike Taylor" href="http://reprog.wordpress.com/about/" target="_blank">迈克·泰勒（Mike Taylor）</a>，2010年4月19日<br/>翻译完成：2010年4月20日</p>
<p>有一些讲编程的图书，我会从头到尾、一字不落地反复研读；还有一些讲编程的图书，我已经看过好几遍了，但每次差不多都是只看其中的一章。乔恩·本特利（Jon Bentley）1986年的经典名著《编程珠玑》（<em>Programming Pearls</em>）则是少数几本能同时归入上述两类的编程图书之一，我手里这本书的封面已经严重磨损，下图可以为证（点击看大图，注意左下角。——译者注）。</p>
<p><a href="http://reprog.files.wordpress.com/2010/04/programming-pearls-first-edition.jpeg"><img class="aligncenter colorbox-1530" title="Programming Pearls, First Edition" src="http://reprog.files.wordpress.com/2010/04/programming-pearls-first-edition.jpeg?w=199&amp;h=300" alt="" width="198" height="299" /></a></p>
<p>（我有这本书的第1版[<a title="http://www.amazon.com/exec/obidos/ASIN/0201103311/thedinosaurrea0a" href="http://www.amazon.com/exec/obidos/ASIN/0201103311/thedinosaurrea0a" target="_blank">amazon.com</a>，<a title="http://www.amazon.co.uk/exec/obidos/ASIN/0201103311/thedinosaurreadi" href="http://www.amazon.co.uk/exec/obidos/ASIN/0201103311/thedinosaurreadi" target="_blank">amazon.co.uk</a>]，上面就是扫描的这一版的封面，好像我该买一本又新又便宜的第2版了<a title="http://www.amazon.com/exec/obidos/ASIN/0201657880/thedinosaurrea0a" href="http://www.amazon.com/exec/obidos/ASIN/0201657880/thedinosaurrea0a" target="_blank">[amazon.com</a>，<a title="http://www.amazon.co.uk/exec/obidos/ASIN/0201657880/thedinosaurreadi" href="http://www.amazon.co.uk/exec/obidos/ASIN/0201657880/thedinosaurreadi" target="_blank">amazon.co.uk</a>]，第2版比第1版多了3章内容。）</p>
<p>我打算最近再专门写一篇关于这本书的文章（我已经为<em><a title="http://reprog.wordpress.com/2010/03/01/programming-books-part-1-coders-at-work/" href="http://reprog.wordpress.com/2010/03/01/programming-books-part-1-coders-at-work/" target="_blank">Coders at Work</a>、<a title="http://reprog.wordpress.com/2010/03/06/programming-books-part-2-the-elements-of-programming-style/" href="http://reprog.wordpress.com/2010/03/06/programming-books-part-2-the-elements-of-programming-style/" target="_blank">The Elements of Programming Style</a></em>、<em><a title="http://reprog.wordpress.com/2010/03/12/programming-books-part-3-programming-the-commodore-64/" href="http://reprog.wordpress.com/2010/03/12/programming-books-part-3-programming-the-commodore-64/" target="_blank">Programming the Commodore 64 </a></em>和<a title="http://reprog.wordpress.com/2010/04/06/programming-books-part-4-the-c-programming-language/" href="http://reprog.wordpress.com/2010/04/06/programming-books-part-4-the-c-programming-language/" target="_blank"><em>The C Programming Language</em></a>专门写了几篇书评），但今天我只想就这本书中的几段话谈谈自己的想法。这几段内容有点骇人听闻。</p>
<h2>只有10%的程序员可以写出二分查找</h2>
<p>每次翻开《编程珠玑》，我都会先看一看下面这几段文字：</p>
<div style="border-left: 3px solid #ddd; margin-left: 20px; padding-left: 20px;">
<p>二分查找可以解决（预排序数组的查找）问题：只要数组中包含T（即要查找的值），那么通过不断缩小包含T的范围，最终就可以找到它。一开始，范围覆盖整个数组。将数组的中间项与T进行比较，可以排除一半元素，范围缩小一半。就这样反复比较，反复缩小范围，最终就会在数组中找到T，或者确定原以为T所在的范围实际为空。对于包含N个元素的表，整个查找过程大约要经过log(2)N次比较。</p>
<p>多数程序员都觉得只要理解了上面的描述，写出代码就不难了；但事实并非如此。如果你不认同这一点，最好的办法就是放下书本，自己动手写一写。试试吧。</p>
<p>我在贝尔实验室和IBM的时候都出过这道考题。那些专业的程序员有几个小时的时间，可以用他们选择的语言把上面的描述写出来；写出高级伪代码也可以。考试结束后，差不多所有程序员都认为自己写出了正确的程序。于是，我们花了半个钟头来看他们编写的代码经过测试用例验证的结果。几次课，一百多人的结果相差无几：90%的程序员写的程序中有bug（我并不认为没有bug的代码就正确）。</p>
<p>我很惊讶：在足够的时间内，只有大约10%的专业程序员可以把这个小程序写对。但写不对这个小程序的还不止这些人：高德纳在《计算机程序设计的艺术 第3卷 排序和查找》第6.2.1节的“历史与参考文献”部分指出，虽然早在1946年就有人将二分查找的方法公诸于世，但直到1962年才有人写出没有bug的二分查找程序。</p>
<div style="text-align: right;">——乔恩·本特利，《编程珠玑（第1版）》第35-36页</div>
</div>
<p>几个小时！90%！老兄，严肃点！难道这还不够<strong>骇人听闻</strong>吗？</p>
<p>之所以想看这本书的第2版，原因之一就是想看看这几段文字有没有修订过，看看从1986年到1999年出第2版，这个数字有没有变化。直觉告诉我，这个数字一定向好的方向变化了，事物都是向好的方向发展的嘛。但理性却告诉我，在一个<a title="http://reprog.wordpress.com/2010/03/03/whatever-happened-to-programming/" href="http://reprog.wordpress.com/2010/03/03/whatever-happened-to-programming/" target="_blank">程序员把更多的时间都花在摆弄库上</a>，而不是编写实际代码的时代，重现核心算法的能力即使有也一定会弱化。别忘了，本特利提到的那些家伙可都不是等闲之辈，他们都是贝尔实验室和IBM的专业人员。所以，我们有理由相信他们的成绩实际上已经是最好的了。</p>
<p><a href="http://reprog.files.wordpress.com/2010/04/gari.jpg"><img class="aligncenter colorbox-1530" src="http://reprog.files.wordpress.com/2010/04/gari.jpg?w=292&amp;h=241" alt="" width="292" height="241" /></a></p>
<h2>好，下面就做一个二分查找的测验</h2>
<p>我跟你一样（如果你是这么想的），想马上就试一试。（好啦，不是马上。先看完这篇文章！）我相信看这篇文章的人都知道什么是二分查找算法，即使你不知道，上面引用的本特利的描述也应该够了。请你打开编辑器，编写一个二分查找例程。什么时候觉得没有任何问题了，保留那个版本。然后测试，然后通过在下面留言的方式告诉我你是不是第一次就做对了。我们肯定能打破本特利10%的纪录吗？</p>
<p>规则如下。</p>
<ol>
<li>使用你喜欢的任何编程语言。</li>
<li>不要剪切粘贴或以任何方式复制别人的代码。甚至在你写完之前，都不要参考其他的二分查找代码。</li>
<li>甚至于我不得不强调，别调用bsearch()，或使用其他瞒天过海的手法 <img src='http://www.cn-cuckoo.com/main/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley colorbox-1530' /> </li>
<li>时间自己来定：5分钟不短——只要你能保证写完写对；8小时不长——只要你愿意（而且有那么多闲工夫）。</li>
<li>可以使用编译器消除一些无意识的错误，如语法错误或变量初始化失败，但……</li>
<li>在确定程序正确之前<strong>不要测试</strong>。</li>
<li>最后，也是最重要的：如果决定参与这次测验，就必须报告。成功也好，失败也罢，甚至半途而废也要给我个话儿。否则，就无法保证测验结果的准确性了。</li>
</ol>
<p>（考虑到这只是一次测验，可以忽略计算索引时导致的数值溢出。<a href="http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html">这里描述了相应的情形</a>，但打算参加这次测验的人在编完程序之前不要看，因为那篇文章里包含一个正确的二分查找的实现，想洁身自好的朋友一定是不屑为之的。）</p>
<p>如果你的代码经验证确实正确，那么如果你愿意的话，欢迎你在留言里贴出自己的代码。不过，假如你这样做了，而后来的留言给你挑出了bug，请你一定想好怎样为维护自己的形象而自圆其说 <img src='http://www.cn-cuckoo.com/main/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley colorbox-1530' /> </p>
<p>更酷的玩法：对于那些信心十足的人，如果你真敢肯定自己的程序没有问题，可以先把代码贴在留言里，然后再测试。当然，你必须要在留言里说明这一点，以便大家发现你的bug时，会考虑多少给你留些情面。</p>
<p>我会在某个时间总结一下这次测试的结果——比如说，一周以后。</p>
<p>行动吧！</p>
<p><a href="http://reprog.files.wordpress.com/2010/04/19980-wasabi.jpg"><img class="aligncenter colorbox-1530" src="http://reprog.files.wordpress.com/2010/04/19980-wasabi.jpg?w=346&amp;h=332" alt="" width="346" height="332" /></a></p>
<h2>第一次更新（一个半小时后）</h2>
<p>感谢朋友们的积极响应，这么快就有那么多留言！我得提醒大家，WordPress的留言系统会解释HTML，因此会吞掉类似下面的代码段</p>
<p>if a[mid] &lt; value</p>
<p>最好的办法就是把源代码放在{source}&#8230;{source}标签之间，注意用方括号代替这里的花括号。（我第一次想告诉大家这一点时，使用的是方括号，结果我写的规避标记的说明，反而被当成了标记——悲哀呀！）这样做还可以保留缩进，否则我还不知道有什么办法可以做到这一点。</p>
<p>替WordPress向大家致歉：我真的希望这个平台允许留言者预览留言和/或在发表后还能编辑留言，这样就可以避免出现乱七八糟的代码了。我也想了办法自己动手解决这个问题，但WordPress不仅会错误地显示带有&lt;符号的代码，它实际上会丢弃该符号后面的所有内容，唉，我想我是没折了。</p>
<h2>第二次更新（在原文章发表4个小时后）</h2>
<p>哈哈，你们这些家伙太出人意料了。仅仅4个小时，这篇文章的留言数就超过了以前的一篇文章保持的纪录（<a href="http://reprog.wordpress.com/2010/03/03/whatever-happened-to-programming/">Whatever Happened to Programming</a>此时的留言有206条）。</p>
<p>如果想看到相关的更多讨论，<a href="http://news.ycombinator.com/item?id=1277459">Hacker News中有不少不错的留言</a>，另外<a href="http://www.reddit.com/r/programming/comments/bt7nh/according_to_jon_bentleys_book_programming_pearls/">Reddit的留言质量虽然差一点</a>，但也值得一看。这些讨论把实际地编写代码看成只有精英程序员才会干的事。</p>
<div style="border:1px dashed #ddd;background:#eee;padding:10px;">
译后记：看了几眼，能认出来的加上认不出来的：C、PHP、Ruby、Python、Common Lisp、VB.NET 、C#、Java、Javascript、Delphi、Haskell、Scheme、Clojure、Perl、Smalltalk、FORTRAN、Lua、Objective-C、ColdFusion……各种各样语言的实现齐聚一堂。</p>
<p>有心人<a href="http://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/#comment-1995">乔尔·甘勒（Joe Ganley）对前100个留言作了统计</a>，结果如下：</p>
<p>Python 40<br />
C/C++ 36<br />
Unknown/pseudocode 6<br />
Lisp/Clojure/Scheme 5<br />
PHP 4<br />
Three each: Java, Perl, C#, JavaScript<br />
Haskell 2<br />
One each: VB, Delphi, Smalltalk, FORTRAN, Lua, Objective-C, ColdFusion</p>
<p>Conclusion: Almost everyone (who cares about implementing binary search, anyway) uses C/C++ or Python.</p>
<p>PS：专注前端开发的程序员们，可以参考《JavaScript高级程序设计》的作者Nicholas C. Zakas使用JavaScript实现的一些基本算法，链接地址如下<a href="http://www.nczonline.net/blog/tag/computer-science/">http://www.nczonline.net/blog/tag/computer-science/</a>。其中，对本文提到的二分查找算法的实现如下：<br />
<coolcode language="javascript"><br />
//Copyright 2009 Nicholas C. Zakas. All rights reserved.<br />
//MIT-Licensed, see source file<br />
function binarySearch(items, value){</p>
<p>    var startIndex  = 0,<br />
        stopIndex   = items.length &#8211; 1,<br />
        middle      = Math.floor((stopIndex + startIndex)/2);</p>
<p>    while(items[middle] != value &#038;&#038; startIndex < stopIndex){</p>
<p>        //adjust search area（调整查找范围）<br />
        if (value < items[middle]){<br />
            stopIndex = middle - 1;<br />
        } else if (value > items[middle]){<br />
            startIndex = middle + 1;<br />
        }</p>
<p>        //recalculate middle（重新计算中项索引）<br />
        middle = Math.floor((stopIndex + startIndex)/2);<br />
    }</p>
<p>    //make sure it&#8217;s the right value（确保返回正确的值）<br />
    return (items[middle] != value) ? -1 : middle;<br />
}<br />
</coolcode>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2010/04/20/are-you-one-of-the-10-percent-of-programmers-who-can-write-a-binary-search-1530.html/feed</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>乔尔谈软件终结篇：分布式版本控制系统的时代到来了</title>
		<link>http://www.cn-cuckoo.com/2010/03/20/distributed-version-control-is-here-to-stay-baby-1436.html</link>
		<comments>http://www.cn-cuckoo.com/2010/03/20/distributed-version-control-is-here-to-stay-baby-1436.html#comments</comments>
		<pubDate>Sat, 20 Mar 2010 00:34:57 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[编程技术]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=1436</guid>
		<description><![CDATA[译者按：3月17日，Joel Spolsky在他影响了全球数百万程序员的著名博客Joel on Software中发表了最后一篇文章Distributed Version Control is here to stay, baby。特翻译这篇文章，以为纪念。另外，推荐他的博客文集《软件随想录》（More Joel on Software中文版）。 前不久，杰夫、我，还有埃里克一块参加Stack Overflow Podcast的在线聊天。哥几个针对版本控制问题，狠狠地发了一通牢骚。特别是大批特批了以Mercurial和Git为代表的、新潮的DVCS（Distributed Version Control System，分布式版本控制系统）。 聊天中，我说：“我觉得，用它们来做分支和合并更简单，恰恰说明了你的同事可能会做更多的分支和合并。结果呢，就是你会把自己绕进去，最后非晕菜不可。” 说实话，你们也猜得到，参加那种聊天活动，不需要事先准备什么，不过是几个人东拉西扯地闲聊天而已。既然是闲聊天嘛，就免不了说错话，或者用技术术语来说就是——犯错误。要么哪句话里有错误，要么出发点有错误，要么就是出发点和话里都有错误。但这一次，我犯了一个低级错误。就像草莓配比萨、辣椒配面包，属于那种低级得不能再低级的错误。 在这次聊天之前，我们团队已经切换到Mercurial很长时间了，但这次切换真的把我搞晕了，我不得不花钱请人替我check in代码（开个玩笑）。不过，有一段日子我确实不好过，因为我得记住几个关键的命令，想象着自己是在Subversion中使用它们，期待能看到熟悉的结果。可是，一旦碰到结果跟我想象得不一样，我就会大惑不解。为此，我经常得颠儿颠儿地跑到楼下大房间里，请教本杰明或者雅各布。 你猜我的团队怎么说，“哎，乔总，这‘水银果汁’[注1]太神了，我们打算用实际的产品代码来测试一下它。另外，还有，这一点更重要，我们发现了一个大市场，我们可以为它提供有偿技术支持和托管服务。”（Mercurial是基于GPL的自由软件，但有不少公司在决定使用什么之前，总会需要某种支持。） 我心里想，你们问我，我问谁？你们都知道，我在公司并不真正作主，因为“管理的职能就是提供支持”嘛。然后，他们就把所有6名实习生都组织起来，动手开发基于Mercurial的产品。 真不能再拖下去了，我必须得尽快弄明白，所谓的“分布式版本控制”到底咋回事。免得我们公司的产品都上市开卖了，人家让我介绍产品，我自己都说不出个子丑寅卯来。那样的话，博客圈里又会有人跳出来，对我指手画脚，说我糟蹋好东西了（junking the sharp）。 于是，我学呀，学呀，最后终于闹明白了。今天，我想跟大家在这里分享一下。 在分布式版本控制系统中，所谓的“分布式”其实不是最关键的。 最关键的是在使用这些系统时，你时刻要想着“更改”，而不是“版本”。 我明白，这里边其实有几分“道可道，非常道”的意味。在使用传统的版本控制系统时，你会想：嗯，这是版本1，这是版本2，这是版本3。 而在使用分布式版本控制系统时，其实我不用想版本。我只要想，我做了这些更改，我又做了另一些更改。 “程序模型”变了，“用户模型”也得跟着变。 在Subversion中，你可能会想，“要保证我的版本跟主版本随时一致”，或者“恢复到前一个版本”。 在Mercurial中，你会这样想，“看看雅各布的更改”，或者“我们先不用考虑那些更改”。 如果你在使用Mercurial时，心里想的是Subversion，多数情况下倒也问题不大；但是，万一出了问题，你就会找不着北、会不高兴，最后因为无计可施而恨上Mercurial。 可是，如果你能够解放思想，换一个角度来看版本控制，就能悟出管理“版本”与管理“变更”的差异所在。你会觉得豁然开朗，由衷地感叹这才是控制版本的最佳方式。 没错，是有点异样的感觉——从1972年至今，所有人关注的都是怎么管理版本，而今天，人们突然要去关注更改本身了，能不感觉异样吗？而且，关注更改解决了一个非常重要的问题：合并分支代码。 这一点最重要了。没错，在长达10年的时间里，我们深刻地体会到，这一点对提高开发效率最为重要。重要到了什么程度呢？重要到在我今天最后一次谈软件时，必须要反复强调它。好了，如果你只想记住一句话，那就记住下面这句： 在管理更改而非管理版本时，合并很容易做到，你可以根据工作需要随时创建分支，因为合并已经是小菜一碟了。 记不清有多少个使用Subversion的用户，曾对我说过下面这番话：“用它创建分支代码的时候，没什么问题。但是，等到要把分支合并到一块时，那个费劲啊，我们不得不手工重新应用每一次更改。我们发誓，这种蠢事一辈子也不会再干了。结果，我们想出了一个开发软件的新主意，就是用if语句，不用分支。” 有时候，他们在提到自己发明的这种一个主干的做法时，甚至还有几分得意。好像弥补了自己版本控制工具的不足，就可以流芳百世了。 使用分布式版本控制系统，合并简单，还不会出错。所以，你可以创建一个稳定的分支、一个开发分支，还可以为QA团队创建一个长期使用的分支，以便在实际部署之前进行测试。当然，还可以创建一个临时分支，用它来验证各种想法，观察结果。 这一点太重要了，怎么强调它都不算过分。自从我10年前开始写博客以来，这恐怕是我笔下的软件开发技术领域中最大的进步了。 或者，可以换一种说法，如果我要放弃Mercurial，除非是我想再使用C++。 如果你还在使用Subversion，停止使用。马上停止使用。Subversion＝水蛭。Mercurial＝抗生素。我们现在有更好的技术了。 很多人在没有完全理解这种新程序模型的情况下，往往会盲目地使用Mercurial，而这容易让人觉得它有问题，不可靠。为此，我专门写了一套Mercurial教程，叫做HgInit[注2]。 如今，要是有人问我那次聊DVCS时为什么要贬低DVCS，我会告诉他们那是我精心设计一个圈套，我是在放烟雾弹，目的是要迷惑我的老朋友和老竞争对手埃里克——他在开发一个非分布式的版本控制系统。就像上一次他要卖bug追踪软件一样。那一次，为了惩罚他，我们用一个特制的信封寄给他一个非常值钱的Fog Creek双肩背包。让他觉得我们会在圣诞节的时候，也会把这么值钱的背包作为圣诞礼物，送给所有FogBugz软件的客户。 我的博客写到今天，时间已经够长了。10年来，大家能够坚持阅读我写的文章，是我莫大的荣幸。这个博客的读者群能达到今天这个规模，是我当初做梦也想不到的。无论你是谁，是花自己的时间把我的文章翻译成40多种语言的那几百位志愿者中的一员，是给我写过电子邮件的22 894位朋友中的一位，是订阅了我的邮件简报的50 838名订阅者之一，还是每年2 262 [...]]]></description>
			<content:encoded><![CDATA[<div style="padding: 1em; margin: 0 0 10px 10px; width: 160px; float: right; background: #eee; border: 1px slashed #ddd;"><strong>译者按</strong>：3月17日，Joel Spolsky在他影响了全球数百万程序员的著名博客<a title="http://www.joelonsoftware.com/" href="http://www.joelonsoftware.com/" target="_blank"><strong>Joel on Software</strong></a>中发表了最后一篇文章<a title="http://www.joelonsoftware.com/items/2010/03/17.html" href="http://www.joelonsoftware.com/items/2010/03/17.html" target="_blank"><em>Distributed Version Control is here to stay, baby</em></a>。特翻译这篇文章，以为纪念。另外，推荐他的博客文集《<a title="http://www.china-pub.com/196194" href="http://www.china-pub.com/196194" target="_blank">软件随想录</a>》（<a title="http://www.amazon.com/More-Joel-Software-Occasionally-Developers/dp/1430209879" href="http://www.amazon.com/More-Joel-Software-Occasionally-Developers/dp/1430209879" target="_blank"><em>More Joel on Software</em></a>中文版）。</div>
<p><a href="http://www.inc.com/uploaded_files/image/2009-CJS-Spolsky-pop_645.jpg" target="_blank"><img class=" alignleft colorbox-1436" style="margin: 0 10px 10px 0;" src="http://www.inc.com/uploaded_files/image/170x170/2009-CJS-Spolsky-bkt_645.jpg" alt="Joel Spolsky is the founder and CEO of Fog Creek Software in New York City." width="170" height="170" /></a></p>
<p>前不久，杰夫、我，还有埃里克一块<a title="Stack Overflow Podcast 36" href="http://blog.stackoverflow.com/2009/01/podcast-36/" target="_blank">参加Stack Overflow Podcast的在线聊天</a>。哥几个针对版本控制问题，狠狠地发了一通牢骚。特别是大批特批了以<a title="http://mercurial.selenic.com/" href="http://mercurial.selenic.com/" target="_blank">Mercurial</a>和<a title="http://git-scm.com/" href="http://git-scm.com/" target="_blank">Git</a>为代表的、新潮的DVCS（Distributed Version Control System，分布式版本控制系统）。</p>
<p>聊天中，我说：“我觉得，用它们来做分支和合并更简单，恰恰说明了你的同事可能会做更多的分支和合并。结果呢，就是你会把自己绕进去，最后非晕菜不可。”</p>
<p>说实话，你们也猜得到，参加那种聊天活动，不需要事先准备什么，不过是几个人东拉西扯地闲聊天而已。既然是闲聊天嘛，就免不了说错话，或者用技术术语来说就是——犯错误。要么哪句话里有错误，要么出发点有错误，要么就是出发点和话里都有错误。但这一次，我犯了一个低级错误。就像草莓配比萨、辣椒配面包，属于那种低级得不能再低级的错误。</p>
<p>在这次聊天之前，我们团队已经切换到Mercurial很长时间了，但这次切换真的把我搞晕了，我不得不花钱请人替我check in代码（开个玩笑）。不过，有一段日子我确实不好过，因为我得记住几个关键的命令，想象着自己是在Subversion中使用它们，期待能看到熟悉的结果。可是，一旦碰到结果跟我想象得不一样，我就会大惑不解。为此，我经常得颠儿颠儿地跑到楼下大房间里，请教本杰明或者雅各布。</p>
<p>你猜我的团队怎么说，“哎，乔总，这‘水银果汁’<sup><a name="source1" href="#note1">[注1]</a></sup>太神了，我们打算用实际的产品代码来测试一下它。另外，还有，这一点更重要，我们发现了一个大市场，我们可以为它提供有偿技术支持和托管服务。”（Mercurial是基于GPL的自由软件，但有不少公司在决定使用什么之前，总会需要某种支持。）</p>
<p>我心里想，你们问我，我问谁？你们都知道，我在公司并不真正作主，因为“管理的职能就是提供支持”嘛。然后，他们就把所有6名实习生都组织起来，动手开发<a title="http://www.fogcreek.com/kiln/" href="http://www.fogcreek.com/kiln/" target="_blank">基于Mercurial的产品</a>。</p>
<p>真不能再拖下去了，我必须得尽快弄明白，所谓的“分布式版本控制”到底咋回事。免得我们公司的产品都上市开卖了，人家让我介绍产品，我自己都说不出个子丑寅卯来。那样的话，博客圈里又会有人跳出来，对我指手画脚，说我糟蹋好东西了（junking the sharp）。</p>
<p>于是，我学呀，学呀，最后终于闹明白了。今天，我想跟大家在这里分享一下。</p>
<p>在分布式版本控制系统中，所谓的“分布式”其实不是最关键的。</p>
<p>最关键的是在使用这些系统时，你时刻要想着“更改”，而不是“版本”。</p>
<p>我明白，这里边其实有几分“道可道，非常道”的意味。在使用传统的版本控制系统时，你会想：嗯，这是版本1，这是版本2，这是版本3。</p>
<p>而在使用分布式版本控制系统时，其实我不用想版本。我只要想，我做了这些更改，我又做了另一些更改。</p>
<p>“<a title="Figuring Out What They Expected" href="http://www.joelonsoftware.com/uibook/chapters/fog0000000058.html" target="_blank">程序模型</a>”变了，“用户模型”也得跟着变。</p>
<p>在Subversion中，你可能会想，“要保证我的版本跟主版本随时一致”，或者“恢复到前一个版本”。</p>
<p>在Mercurial中，你会这样想，“看看雅各布的更改”，或者“我们先不用考虑那些更改”。</p>
<p>如果你在使用Mercurial时，心里想的是Subversion，多数情况下倒也问题不大；但是，万一出了问题，你就会找不着北、会不高兴，最后因为无计可施而恨上Mercurial。</p>
<p>可是，如果你能够解放思想，换一个角度来看版本控制，就能悟出管理“版本”与管理“变更”的差异所在。你会觉得豁然开朗，由衷地感叹这才是控制版本的最佳方式。</p>
<p>没错，是有点异样的感觉——<a title="http://en.wikipedia.org/wiki/Source_Code_Control_System" href="http://en.wikipedia.org/wiki/Source_Code_Control_System" target="_blank">从1972年至今</a>，所有人关注的都是怎么管理版本，而今天，人们突然要去关注更改本身了，能不感觉异样吗？而且，关注更改解决了一个非常重要的问题：合并分支代码。</p>
<p>这一点最重要了。没错，在长达10年的时间里，我们深刻地体会到，这一点对提高开发效率最为重要。重要到了什么程度呢？重要到在我今天最后一次谈软件时，必须要反复强调它。好了，如果你只想记住一句话，那就记住下面这句：</p>
<p>在管理更改而非管理版本时，合并很容易做到，你可以根据工作需要随时创建分支，因为合并已经是小菜一碟了。</p>
<p>记不清有多少个使用Subversion的用户，曾对我说过下面这番话：“用它创建分支代码的时候，没什么问题。但是，等到要把分支合并到一块时，那个费劲啊，我们不得不手工重新应用每一次更改。我们发誓，这种蠢事一辈子也不会再干了。结果，我们想出了一个开发软件的新主意，就是用if语句，不用分支。”</p>
<p>有时候，他们在提到自己发明的这种一个主干的做法时，甚至还有几分得意。好像弥补了自己版本控制工具的不足，就可以流芳百世了。</p>
<p>使用分布式版本控制系统，合并简单，还不会出错。所以，你可以创建一个稳定的分支、一个开发分支，还可以为QA团队创建一个长期使用的分支，以便在实际部署之前进行测试。当然，还可以创建一个临时分支，用它来验证各种想法，观察结果。</p>
<p>这一点太重要了，怎么强调它都不算过分。自从我10年前开始写博客以来，这恐怕是我笔下的软件开发技术领域中最大的进步了。</p>
<p>或者，可以换一种说法，如果我要放弃Mercurial，除非是我想再使用C++。</p>
<p>如果你还在使用Subversion，停止使用。马上停止使用。Subversion＝水蛭。Mercurial＝抗生素。我们现在有更好的技术了。</p>
<p>很多人在没有完全理解这种新程序模型的情况下，往往会盲目地使用Mercurial，而这容易让人觉得它有问题，不可靠。为此，我专门写了<a title="http://hginit.com/" href="http://hginit.com/" target="_blank">一套Mercurial教程，叫做HgInit</a><sup><a name="source2" href="#note2">[注2]</a></sup>。</p>
<p>如今，要是有人问我那次聊DVCS时为什么要贬低DVCS，我会告诉他们那是我精心设计一个圈套，我是在放烟雾弹，目的是要迷惑我的老朋友和老竞争对手埃里克——他在开发一个非分布式的版本控制系统。就像上一次他要卖bug追踪软件一样。那一次，为了惩罚他，我们用一个特制的信封寄给他一个非常值钱的Fog Creek双肩背包。让他觉得我们会在圣诞节的时候，也会把这么值钱的背包作为圣诞礼物，送给所有FogBugz软件的客户。</p>
<p>我的博客写到今天，时间已经够长了。10年来，大家能够坚持阅读我写的文章，是我莫大的荣幸。这个博客的读者群能达到今天这个规模，是我当初做梦也想不到的。无论你是谁，是花自己的时间把我的文章翻译成40多种语言的那几百位志愿者中的一员，是给我写过电子邮件的22 894位朋友中的一位，是订阅了我的邮件简报的50 838名订阅者之一，还是每年2 262 384位访问这个站点并阅读了我写的1 067篇文章中哪怕只言片语的读者中的一位，我都要由衷地感谢你，感谢你对我的关注。</p>
<div style="margin-top: 30px; padding: 0; width: 80%; border-top: 1px solid #ddd;"><a style="display: block; margin-top: 10px;" name="note1" href="#source1">注1：Mecurial是“水银”的意思。</a><a style="display: block; margin: 0;" name="note2" href="#source2">注2：Hg是汞的化学元素符号，而Init是“初始化”的意思。因此，HgInit可以理解成“Mecurial入门”。</a></div>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2010/03/20/distributed-version-control-is-here-to-stay-baby-1436.html/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>N王后问题（续）</title>
		<link>http://www.cn-cuckoo.com/2009/01/22/n_queens_in_javascript-366.html</link>
		<comments>http://www.cn-cuckoo.com/2009/01/22/n_queens_in_javascript-366.html#comments</comments>
		<pubDate>Thu, 22 Jan 2009 03:47:04 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[编程技术]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/?p=366</guid>
		<description><![CDATA[N王后问题，是一个科学谜题，指的是在把N个王后放到N*N的棋盘上，结果是任何王后之间都不会彼此威胁。换句话说，每个后继的王后既不能与前面的王后在同一行、同一列，也不能位于同一对角线上。N王后问题给出了Python代码。下面是JavaScript代码，但结果有些误差，不知何故。 function conflict(state,posX) { var posY = state.length; for (var i=0;i&#60;posY ;i++ ) { var differ = Math.abs(state[i]-posX); if (differ == 0 &#124;&#124; differ == (posY - i)) { return true; } } return false; } function queens(num,state,solutions){ for (var x=0;x&#60;num ;x++ ) { if (!conflict(state,x)) { if (state.length == (num-1)) { solutions.unshift(x); return true; [...]]]></description>
			<content:encoded><![CDATA[<p>N王后问题，是一个科学谜题，指的是在把N个王后放到N*N的棋盘上，结果是任何王后之间都不会彼此威胁。换句话说，每个后继的王后既不能与前面的王后在同一行、同一列，也不能位于同一对角线上。<a href="http://www.cn-cuckoo.com/2009/01/16/n-queens-in-python-284.html">N王后问题</a>给出了Python代码。下面是JavaScript代码，但结果有些误差，不知何故。</p>
<pre class="brush: js; ">

function conflict(state,posX)	{
	var posY = state.length;
	for (var i=0;i&lt;posY ;i++ )
	{
		var differ = Math.abs(state[i]-posX);
		if (differ == 0 || differ == (posY - i))
		{
			return true;
		}
	}
	return false;
}

function queens(num,state,solutions){
	for (var x=0;x&lt;num ;x++ )
	{
		if (!conflict(state,x))
		{
			if (state.length == (num-1))
			{
				solutions.unshift(x);
				return true;
			}
			var result = queens(num,state.concat(x),solutions);
			if (result)
			{
				if (state.length==0)
				{
					solutions.unshift(x);
					continue;
				}
				solutions.unshift(x);
				return true;
			}

		}

	}
}
</pre>
<p>用法如下：</p>
<pre class="brush: js; ">

var result = [];
queens(4,[],result);
console.log(result);// [3, 1, 0, 2, 2, 0, 3, 1, 1, 3, 0, 2, 0, 3, 1, 2]
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2009/01/22/n_queens_in_javascript-366.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>N王后问题</title>
		<link>http://www.cn-cuckoo.com/2009/01/16/n-queens-in-python-284.html</link>
		<comments>http://www.cn-cuckoo.com/2009/01/16/n-queens-in-python-284.html#comments</comments>
		<pubDate>Fri, 16 Jan 2009 02:59:40 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[编程技术]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/2009/01/16/n-queens-in-python-284.html</guid>
		<description><![CDATA[关于N王后问题 模块代码： #nqueens.py #coding=UTF-8 # n王后问题解决方案 # 检查当前王后位置（可能是多个）与下一个王后位置是否冲突 def conflict(state,posX): posY = len(state) for i in range(posY): if abs(state[i]-posX) in (0,posY-i): return True return False # 采用回溯递归算法，结合生成器特性，计算可能的解决方案 def find(num=4,state=()): for pos in range(num): if not conflict(state,pos): if len(state)==num-1: yield (pos,) else: for result in find(num,state+(pos,)): yield (pos,)+result # 形象地表示每个解决方案 def show(solutions): def printSolution(index,solution): print &#34;\n方案&#34;+str(index)+&#34; [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.cn-cuckoo.com/2009/01/22/n_queens_in_javascript-366.html">关于N王后问题</a><br />
模块代码：</p>
<pre class="brush: py; ">

#nqueens.py
#coding=UTF-8

# n王后问题解决方案
# 检查当前王后位置（可能是多个）与下一个王后位置是否冲突
def conflict(state,posX):
	posY = len(state)
	for i in range(posY):
		if abs(state[i]-posX) in (0,posY-i):
			return True
		return False

# 采用回溯递归算法，结合生成器特性，计算可能的解决方案
def find(num=4,state=()):
	for pos in range(num):
		if not conflict(state,pos):
			if len(state)==num-1:
				yield (pos,)
		else:
			for result in find(num,state+(pos,)):
				yield (pos,)+result

# 形象地表示每个解决方案
def show(solutions):
	def printSolution(index,solution):
		print &quot;\n方案&quot;+str(index)+&quot; &quot;+str(solution)+&quot;\n&quot;
		for pos in solution:
			length=len(solution)
			print &quot;. &quot;*(pos)+&quot;Q &quot;+&quot;. &quot;*(length-pos-1)+&quot;(&quot;+str(pos)+&quot;)&quot;
			list_solutions = list(solutions)
		if not len(list_solutions)==0:
			enum_solutions = enumerate(list_solutions)
			n_solutions = len(list_solutions)
			n_queens = len(list_solutions[0])
			print str(n_queens)+&quot;王后问题有&quot;+str(n_solutions)+&quot;种方案：&quot;
		for index,solution in enum_solutions:
			printSolution(index+1,solution)

# 调用方法：
# nqueens.show(nqueens.find())
# 或
# nqueens.show(nqueens.find(5))
</pre>
<p>将以上代码复制到nqueens.py中，把nqueens.py保存在“你的路径”下。<br />
<span id="more-284"></span><br />
以4王后问题（默认）为例。首先设置系统路径，以便Python解析器能在该路径中找到相应模块：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys.path.append(&quot;你的路径&quot;)
</pre>
<p>然后，导入模块，并采用默认值调用show方法：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; import nqueens
&gt;&gt;&gt; nqueens.show(nqueens.find())
4王后问题有2种方案：

方案1 (1, 3, 0, 2)

. Q . . (1)
. . . Q (3)
Q . . . (0)
. . Q . (2)

方案2 (2, 0, 3, 1)

. . Q . (2)
Q . . . (0)
. . . Q (3)
. Q . . (1)
</pre>
<p>另外，如果不需要看图，可以直接调用find方法：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; list(nqueens.find())
[(1, 3, 0, 2), (2, 0, 3, 1)]
&gt;&gt;&gt; list(nqueens.find(6))
[(1, 3, 5, 0, 2, 4), (2, 5, 1, 4, 0, 3), (3, 0, 4, 1, 5, 2), (4, 2, 0, 5, 3, 1)]
</pre>
<p>或者，为查看方便，可以使用pprint模块的pprint（pretty-print）方法：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; import pprint
&gt;&gt;&gt; pprint.pprint(list(nqueens.find(5)))
[(0, 2, 4, 1, 3),
(0, 3, 1, 4, 2),
(1, 3, 0, 2, 4),
(1, 4, 2, 0, 3),
(2, 0, 3, 1, 4),
(2, 4, 1, 3, 0),
(3, 0, 2, 4, 1),
(3, 1, 4, 2, 0),
(4, 1, 3, 0, 2),
(4, 2, 0, 3, 1)]
</pre>
<p>如果王后多于6个（7王后问题有40种解决方案，8王后问题有92种解决方案），则可以只查看方案数量：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; len(list(nqueens.find(7)))
40
&gt;&gt;&gt; len(list(nqueens.find(8)))
92
&gt;&gt;&gt; len(list(nqueens.find(9)))
352
&gt;&gt;&gt; len(list(nqueens.find(10)))
724
&gt;&gt;&gt; len(list(nqueens.find(11)))
2680
</pre>
<p>注意：求解12个以上的王后时要小心。在俺的P4 2.4G电脑中，求解11王后问题，上面算法费时10秒。而12王后的解决方案是14200……，小心啊！<br />
参见“<a href="http://www.javaeye.com/post/122610" target="_blank">目前最快的算法</a>”。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2009/01/16/n-queens-in-python-284.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Python闭包的改进</title>
		<link>http://www.cn-cuckoo.com/2009/01/04/the-enhancement-of-closure-281.html</link>
		<comments>http://www.cn-cuckoo.com/2009/01/04/the-enhancement-of-closure-281.html#comments</comments>
		<pubDate>Sun, 04 Jan 2009 01:54:04 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[编程技术]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/2009/01/04/the-enhancement-of-closure-281.html</guid>
		<description><![CDATA[Python也支持闭包，但在Python3.0以前，闭包不能访问外部函数中的局部变量。Python3.0为此引入了nonlocal关键字，从而完善了闭包访问外部变量的机制。 在Python2.6中，如果像下面这样定义函数： &#62;&#62;&#62; def outerFun(): outerVar = 0 def innerFun(): outerVar += 1 print outerVar return innerFun 然后，调用outerFun返回的闭包，会导致错误： &#62;&#62;&#62; f = outerFun() &#62;&#62;&#62; f() Traceback (most recent call last): File &#34;&#60;pyshell#15&#62;&#34;, line 1, in &#60;module&#62; f() File &#34;&#60;pyshell#12&#62;&#34;, line 4, in innerFun outerVar += 1 UnboundLocalError: local variable &#039;outerVar&#039; referenced before assignment 把错误消息“UnboundLocalError: local variable [...]]]></description>
			<content:encoded><![CDATA[<p>Python也支持闭包，但在Python3.0以前，闭包不能访问外部函数中的局部变量。Python3.0为此引入了nonlocal关键字，从而完善了闭包访问外部变量的机制。</p>
<p>在Python2.6中，如果像下面这样定义函数：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; def outerFun():
    outerVar = 0
    def innerFun():
        outerVar += 1
	print outerVar
    return innerFun
</pre>
<p>然后，调用outerFun返回的闭包，会导致错误：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; f = outerFun()
&gt;&gt;&gt; f()

Traceback (most recent call last):
  File &quot;&lt;pyshell#15&gt;&quot;, line 1, in &lt;module&gt;
    f()
  File &quot;&lt;pyshell#12&gt;&quot;, line 4, in innerFun
    outerVar += 1
UnboundLocalError: local variable &#039;outerVar&#039; referenced before assignment
</pre>
<p>把错误消息“UnboundLocalError: local variable &#8216;outerVar&#8217; referenced before assignment”翻译成中文，就是“未绑定局部变量：引用局部变量&#8217;outerVar&#8217;之前没有赋值”。啥意思呢？在内部函数innerFun中，outerVar被Python解释器看成是内部函数innerFun中的局部变量，并不是我们认为的外部（函数outerFun中的）变量。即在innerFun中，outerVar只是一个尚未赋值的变量——尽管与外部的outerVar同名，因此不能执行加法计算。Python3.0以前的版本提供了global关键字，通过这个关键字能够在函数内部引用并重新绑定（修改）全局变量：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; x = 1
&gt;&gt;&gt; def c():
	global x
	x += 1
	print x

&gt;&gt;&gt; c()
2
</pre>
<p><span id="more-281"></span><br />
这里通过global关键字在函数c的局部作用域中引用了全局变量x，因此就可以重新绑定（修改）全局变量了。虽然能访问和修改全局变量，但还是不能解决闭包访问外部函数变量的问题。为此，Python3.0增加了一个新关键字——nonlocal，用于在嵌套函数中访问外部变量：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; def outerFun():
	outerVar = 0
	def innerFun():
		nonlocal outerVar	# 使用nonlocal引用外部函数变量
		outerVar += 1
		print(outerVar)	# 注意，print在Python3.0中是函数，不是语句
	return innerFun

&gt;&gt;&gt; f = outerFun()
&gt;&gt;&gt; f()
1
&gt;&gt;&gt; f()
2
</pre>
<p>而且，此时的外部变量对于内部的函数而言是共享的：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; def outerFun():
	outerVar = 0
	def innerFun():
		nonlocal outerVar
		outerVar += 1
		print(outerVar)
	def innerFun2():
		nonlocal outerVar
		outerVar *= 2
		print(outerVar)
	return [innerFun,innerFun2] # 通过列表返回两个函数

&gt;&gt;&gt; ff = outerFun()
&gt;&gt;&gt; ff[0]()
1
&gt;&gt;&gt; ff[1]()
2
&gt;&gt;&gt; ff[1]()	# 连续调用两次innerFun2
4
&gt;&gt;&gt; ff[0]()	# 结果表明，两个闭包共享同一个变量
5
</pre>
<p>关于这一点改进的详细内容，请参见PEP（Python Enhancement Proposals，Python改进建议）3104：<a href="http://www.python.org/dev/peps/pep-3104/" title="http://www.python.org/dev/peps/pep-3104/" target="_blank">Access to Names in Outer Scopes</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2009/01/04/the-enhancement-of-closure-281.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>短路逻辑与条件运算符</title>
		<link>http://www.cn-cuckoo.com/2008/12/30/short-circuit-logic-and-conditional-operators-276.html</link>
		<comments>http://www.cn-cuckoo.com/2008/12/30/short-circuit-logic-and-conditional-operators-276.html#comments</comments>
		<pubDate>Tue, 30 Dec 2008 06:01:20 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[编程技术]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/2008/12/30/short-circuit-logic-and-conditional-operators-276.html</guid>
		<description><![CDATA[逻辑运算符包括逻辑与、逻辑或、逻辑非，英文分别对应：（logical）and、or、not。由于逻辑运算返回的是布尔值true（真）或false（假），因此逻辑运算符也叫布尔运算符。（英文operator在程序设计的语境下有两种译为：运算符、操作符。） Python中的逻辑运算符有一个非常有意思的特性——“短路”，即不做无用功、选择最短路线。具体来说，逻辑运算中的短路表现在逻辑运算符只对有必要求值的表达式求值。例如，表达式x and y（x和y都有可能是表达式），只在x和y同时为真的情况下才会返回True。显然，如果x为假，那么就没有再对y求值的必要了——即使y为真，整个表达式仍然会返回False。因此，and运算符的行为就是：如果x为假，返回x；否则，返回y。 and运算符的这种行为就叫“短路逻辑”（或者懒惰求值）。可见，逻辑运算符的右运算数（如这里的y）有可能不会被求值——原因就是“短路”。 同样，or运算符也有短路行为。例如，对于表达式x or y，如果x是真，则返回x；否则，返回y。 设计短路逻辑有目的何在呢？主要是为了减少不必要的计算。但是，在短路逻辑的基础上，还可以实现一些巧妙的结构。例如： name = raw_input(&#039;Enter name:&#039;) or &#039;&#60;unknow&#62;&#039; 这个赋值语句就利用逻辑运算符or实现了在用户输入为空的情况下为name设置默认值的操作。另外，利用短路逻辑还可以实现另一种运算符，即条件运算符，或者叫三元运算符： b if a else c # 如果a为真，则返回b；否则，返回c 短路逻辑与条件运算符同样也存在于JavaScript中。即，x &#38;&#38; y的行为是：如果x可以转换false，则返回x；否则，计算并返回y的值。x &#124;&#124; y的行为是：如果x可以转换为true，则返回x；否则，计算并返回y的值。显示，如果&#38;&#38;、&#124;&#124;运算符分别在x可转换为false、true的情况下，会执行短路操作——忽略右运算数y。 而利用or运算符的短路行为，JavaScript也实现了为变量赋默认值： //如果name可以转换为false，则不能短路，必须对右运算符求值，从而实现赋默认值 var name = name &#124;&#124; &#039;unprovided&#039;; 以及另一种表现形式的条件表达式——?:： x &#62; 0 ? x * y : -x * y]]></description>
			<content:encoded><![CDATA[<p>逻辑运算符包括逻辑与、逻辑或、逻辑非，英文分别对应：（logical）and、or、not。由于逻辑运算返回的是布尔值true（真）或false（假），因此逻辑运算符也叫布尔运算符。（英文operator在程序设计的语境下有两种译为：运算符、操作符。）</p>
<p>Python中的逻辑运算符有一个非常有意思的特性——“短路”，即不做无用功、选择最短路线。具体来说，逻辑运算中的短路表现在逻辑运算符只对有必要求值的表达式求值。例如，表达式x and y（x和y都有可能是表达式），只在x和y同时为真的情况下才会返回True。显然，如果x为假，那么就没有再对y求值的必要了——即使y为真，整个表达式仍然会返回False。因此，and运算符的行为就是：如果x为假，返回x；否则，返回y。</p>
<p>and运算符的这种行为就叫“短路逻辑”（或者懒惰求值）。可见，逻辑运算符的右运算数（如这里的y）有可能不会被求值——原因就是“短路”。</p>
<p>同样，or运算符也有短路行为。例如，对于表达式x or y，如果x是真，则返回x；否则，返回y。</p>
<p>设计短路逻辑有目的何在呢？主要是为了减少不必要的计算。但是，在短路逻辑的基础上，还可以实现一些巧妙的结构。例如：</p>
<pre class="brush: py; ">

name = raw_input(&#039;Enter name:&#039;) or &#039;&lt;unknow&gt;&#039;
</pre>
<p>这个赋值语句就利用逻辑运算符or实现了在用户输入为空的情况下为name设置默认值的操作。另外，利用短路逻辑还可以实现另一种运算符，即条件运算符，或者叫三元运算符：</p>
<pre class="brush: py; ">

b if a else c # 如果a为真，则返回b；否则，返回c
</pre>
<p>短路逻辑与条件运算符同样也存在于JavaScript中。即，x &amp;&amp; y的行为是：如果x可以转换false，则返回x；否则，计算并返回y的值。x || y的行为是：如果x可以转换为true，则返回x；否则，计算并返回y的值。显示，如果&amp;&amp;、||运算符分别在x可转换为false、true的情况下，会执行短路操作——忽略右运算数y。</p>
<p>而利用or运算符的短路行为，JavaScript也实现了为变量赋默认值：</p>
<pre class="brush: py; ">

//如果name可以转换为false，则不能短路，必须对右运算符求值，从而实现赋默认值
var name = name || &#039;unprovided&#039;;
</pre>
<p>以及另一种表现形式的条件表达式——?:：</p>
<pre class="brush: py; ">

x &gt; 0 ? x * y : -x * y
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2008/12/30/short-circuit-logic-and-conditional-operators-276.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript中的Function（函数）对象</title>
		<link>http://www.cn-cuckoo.com/2008/12/26/on-javascript-function-271.html</link>
		<comments>http://www.cn-cuckoo.com/2008/12/26/on-javascript-function-271.html#comments</comments>
		<pubDate>Fri, 26 Dec 2008 01:57:45 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[编程技术]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/2008/12/26/on-javascript-function-271.html</guid>
		<description><![CDATA[JavaScript中的Function对象是函数，函数的用途分为3类： 作为普通逻辑代码容器； 作为对象方法； 作为构造函数。 1.作为普通逻辑代码容器 function multiply(x, y){ return x*y; } 函数multiply封装了两位数的乘法运算公式： var product = multiply(128,128); // product = 16384 创建函数实例的方式有3种。第一种是声明式，即像声明变量一样，将通过function(){}标识符创建的匿名函数直接赋值给变量，以该变量作为调用时的函数名称： var multiply = function(x, y){ return x*y; } 第二种是定义式，即以function关键字后跟函数名称及(){}来直接定义命名函数，前面第一个multiply函数就是通过定义式创建的。 第三种是构造函数式，即通过new运算符调用构造函数Function来创建函数。这种方式极不常用，因此就不作介绍了。 在创建函数的3种方式中，声明式和定义式还存在细微的差别。比如下列代码中的函数采用声明式： var example = function(){ return 1; } example(); var example = function(){ return 2; } example(); 执行结果如下： 1 2 而如果采用定义式，即： function example(){ return [...]]]></description>
			<content:encoded><![CDATA[<p>JavaScript中的Function对象是函数，函数的用途分为3类：</p>
<ol>
<li>作为普通逻辑代码容器；</li>
<li>作为对象方法；</li>
<li>作为构造函数。</li>
</ol>
<p><strong>1.作为普通逻辑代码容器</strong></p>
<pre class="brush: js; ">

function multiply(x, y){
    return x*y;
}
</pre>
<p>函数multiply封装了两位数的乘法运算公式：</p>
<pre class="brush: js; ">

var product = multiply(128,128); // product = 16384
</pre>
<p>创建函数实例的方式有3种。第一种是声明式，即像声明变量一样，将通过function(){}标识符创建的匿名函数直接赋值给变量，以该变量作为调用时的函数名称：</p>
<pre class="brush: js; ">

var multiply = function(x, y){
    return x*y;
}
</pre>
<p>第二种是定义式，即以function关键字后跟函数名称及(){}来直接定义命名函数，前面第一个multiply函数就是通过定义式创建的。</p>
<p>第三种是构造函数式，即通过new运算符调用构造函数Function来创建函数。这种方式极不常用，因此就不作介绍了。<span id="more-271"></span></p>
<p>在创建函数的3种方式中，声明式和定义式还存在细微的差别。比如下列代码中的函数采用声明式：</p>
<pre class="brush: js; ">

var example = function(){
    return 1;
}
example();

var example = function(){
    return 2;
}
example();
</pre>
<p>执行结果如下：<br />
1<br />
2</p>
<p>而如果采用定义式，即：</p>
<pre class="brush: js; ">

function example(){
     return 1;
}
example();

function example(){
    return 2;
}
example();
</pre>
<p>那么会得到另一种结果：<br />
2<br />
2</p>
<p>即，在采用定义式创建同名函数时，后创建的函数会覆盖先创建的函数。这种差别是由于JavaScript解释引擎的工作机制所导致的。JavaScript解释引擎在执行任何函数调用之前，首先会在全局作用域中注册以定义式创建的函数，然后再依次执行函数调用。由于注册函数时，后定义的函数重写了先定义的函数，因此无论调用语句位于何处，执行的都是后定义的函数。相反，对于声明式创建的函数，JavaScript解释引擎会像对待任何声明的变量一样，等到执行调用该变量的代码时才会对变量求值。由于JavaScript代码是从上到下顺序执行的，因此当执行第一个example()调用时，example函数的代码就是首先定义代码；而当执行第二个example()调用时，example函数的代码又变成了后来定义的代码。</p>
<p><strong>2.作为对象方法</strong></p>
<p>JavaScript在解析代码时，会为声明或定义的函数指定调用对象。所谓调用对象，就是函数的执行环境。如果函数体内有以关键字this声明的变量，则this引用的就是调用对象。</p>
<p>事实上，在普通的函数中，也存在调用对象，只不过这个调用对象是默认的全局window对象而已。例如：</p>
<pre class="brush: js; ">

var product = window.multiply(128,128); // product = 16384
</pre>
<p>这说明，默认情况下，在全局作用域中定义或声明的函数的调用对象就是window。</p>
<p>在面向对象编程中，通常将作为对象成员的函数称为方法。例如：</p>
<pre class="brush: js; ">

var dog = {};
dog.name = &quot;heibao&quot;;
dog.age = &quot;3 months&quot;;
dog.shout = function(){
    return &quot;Hello, My name is &quot;+ this.name + &quot; and I am &quot; + this.age + &quot; old!&quot;;
}
dog.shout(); // &quot;Hello, My name is heibao and I am 3 months old!&quot;
</pre>
<p>有意思的是，对象也可以借用其他对象的方法：</p>
<pre class="brush: js; ">

var cat = {};
cat.name = &quot;xiaohua&quot;;
cat.age = &quot;2 years&quot;;
cat.greet = dog.shout;
cat.greet(); // &quot;Hello, My name is xiaohua and I am 2 years old!&quot;
</pre>
<p>另外，使用函数对象的call和apply方法，还可以动态指定函数或方法的调用对象：</p>
<pre class="brush: js; ">

dog.shout.call(cat); // &quot;Hello, My name is xiaohua and I am 2 years old!&quot;
</pre>
<p>或者</p>
<pre class="brush: js; ">

dog.shout.apply(cat); // &quot;Hello, My name is xiaohua and I am 2 years old!&quot;
</pre>
<p><strong>3.作为构造函数</strong></p>
<p>JavaScript是通过构造函数来模拟面向对象语言中的类的。例如：</p>
<pre class="brush: js; ">

function Animal(sort, character){
    this.sort = sort;
    this.character = character;
}
</pre>
<p>以Animal作为构造函数，就可以像下面这样创建一个新对象：</p>
<pre class="brush: js; ">

var dog = new Animal(&quot;mammal&quot;,&quot;four legs&quot;);
</pre>
<p>创建dog的对象的过程如下：首先，new运算符创建一个空对象（{}），然后以这个空对象为调用对象调用函数Animal，为这个空对象添加两个属性sort和character，接着，再将这个空对象的默认constructor属性修改为构造函数的名称（即Animal；空对象创建时默认的constructor属性值是Object），并且将空对象的__proto__属性设置为指向Animal.prototype——这就是所谓的对象初始化。最后，返回初始化完毕的对象。这里将返回的新对象赋值给了变量dog。</p>
<pre class="brush: js; ">

dog.sort; // mammal
dog.character; // four legs
dog.constructor; // Animal
</pre>
<p>聪明的读者结合前面介绍的内容，可能会认为使用new运算符调用构造函数创建对象的过程也可以像下面这样来实现：</p>
<pre class="brush: js; ">

var dog = {};
Animal.call(dog, &quot;mammal&quot;,&quot;four legs&quot;);
</pre>
<p>表面上看，这两行代码与var dog = new Animal(&#8220;mammal&#8221;,&#8221;four legs&#8221;);是等价的，其实却不是。虽然通过指定函数的执行环境能够部分达到初始化对象的目的，例如空对象dog确实获得了sort和character这两个属性：</p>
<pre class="brush: js; ">

dog.sort; // mammal
dog.character; // four legs
dog.constructor; // Object —— 注意，没有修改dog对象默认的constructor属性
</pre>
<p>但是，最关键的是新创建的dog对象失去了通过Animal.prototype属性继承其他对象的能力。只要与前面采用new运算符调用构造函数创建对象的过程对比一下，就会发现，new运算符在初始化新对象期间，除了为新对象添加显式声明的属性外，还会对新对象进行了一番“暗箱操作”——即将新对象的constructor属性重写为Animal，将新对象的__proto__属性设置为指向Animal.prototype。虽然手工“初始化对象”也可以将dog.constructor重写为Animal，但根据ECMA262规范，对象的__proto__属性对开发人员是只读的，对它的设置只能在通过new运算符创建对象时由JavaScript解释引擎替我们完成。<br />
JavaScript是基于原型继承的，如果不能正确设置对象的__proto__属性，那么就意味着默认的继承机制会失效：</p>
<pre class="brush: js; ">

Animal.prototype.greet = &quot;Hi, good lucky!&quot;;
dog.greet; // undefined
</pre>
<p>事实上，在Firefox中，__proto__属性也是可写的：</p>
<pre class="brush: js; ">

Animal.prototype.greet = &quot;Hi, good lucky!&quot;;
dog.__proto__ = Animal.prototype;
dog.greet; // Hi, good lucky!
</pre>
<p>但这样做只能在Firefox中行得通。考虑到在兼容多浏览器，必须依赖于new运算符，才能实现基于原型的继承。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2008/12/26/on-javascript-function-271.html/feed</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>浅复制与深复制中的传值与传址</title>
		<link>http://www.cn-cuckoo.com/2008/12/24/shallow-or-deep-copy-and-pass-by-value-or-reference-270.html</link>
		<comments>http://www.cn-cuckoo.com/2008/12/24/shallow-or-deep-copy-and-pass-by-value-or-reference-270.html#comments</comments>
		<pubDate>Wed, 24 Dec 2008 07:29:45 +0000</pubDate>
		<dc:creator>为之漫笔</dc:creator>
				<category><![CDATA[编程技术]]></category>

		<guid isPermaLink="false">http://www.cn-cuckoo.com/2008/12/24/shallow-or-deep-copy-and-pass-by-value-or-reference-270.html</guid>
		<description><![CDATA[这个标题念起来有点拗口，但却是理解数据结构的关键。标题中的4个术语，对应的英文分别是：shallow copy（注意，不是shadow copy）、deep copy、pass by value、pass by reference（或pass by address）。传址和传引用是一回事。 一门编程语言的核心是数据结构，粗略来讲，可以把数据结构分成不可变类型（immutable）和可变类型（mutable）。为什么这么分呢？这涉及到内存分配问题。对于不可变类型，只要分配有限的内存空间即可，而对于可变类型，理论上则要分配没有大小限制的空间。因此，这么分是出于合理利用系统资源的考虑。实际上，栈内存和堆内存分别用于保存不可变类型值和可变类型值。 什么是不可变类型？就是该值一旦赋予某个变量，就只属于某个变量，不能同属于其他变量。如： var stringValue = &#34;I&#039;m immutable data structure, mean you can&#039;t modify me!&#34;; var anotherStringValue = stringValue; stringValue = &#34;I have changed&#34;; 此时，anotherStringValue中保存的值会不会也变成“I have changed”？不会。因为 var anotherStringValue = stringValue; 照stringValue中保存的字符串的原样，复制一个字符串（相应地，在内存中分配一块新空间），并将该字符串赋给anotherStringValue。换句话说，这两个变量虽然保存的值相同，但它们的值并不在一块内存中。因此，修改任何一个变量，都不会影响另一个变量。即 stringValue = &#34;I have changed&#34;; 只会影响stringValue的值。但是，确切来讲，stringValue = &#8220;I have changed&#8221;;并不是修改stringValue，而是创建了一个新字符串（相应地，在内存中分配一块新空间），然后让stringValue引用该字符串——更像是替换变量的值；原来的字符串呢？因为没有变量引用它，也就成为垃圾了（当然，垃圾所占用的内存会被回收）。 由此可见，赋值操作对于不变类型而言，传递的是内存中的值本身。那么，对于可变类型呢？当然，传递的是内存中值的引用（或者说地址），而且无论传递多少次，内存中始终都只有一份原始值——毕竟可变类型大小莫测，只保存一份原始值能最大限度节省内存空间。例如： var objectValue = [...]]]></description>
			<content:encoded><![CDATA[<p>这个标题念起来有点拗口，但却是理解数据结构的关键。标题中的4个术语，对应的英文分别是：shallow copy（注意，不是shadow copy）、deep copy、pass by value、pass by reference（或pass by address）。传址和传引用是一回事。</p>
<p>一门编程语言的核心是数据结构，粗略来讲，可以把数据结构分成不可变类型（immutable）和可变类型（mutable）。为什么这么分呢？这涉及到内存分配问题。对于不可变类型，只要分配有限的内存空间即可，而对于可变类型，理论上则要分配没有大小限制的空间。因此，这么分是出于合理利用系统资源的考虑。实际上，栈内存和堆内存分别用于保存不可变类型值和可变类型值。</p>
<p>什么是不可变类型？就是该值一旦赋予某个变量，就只属于某个变量，不能同属于其他变量。如：</p>
<pre class="brush: js; ">

var stringValue = &quot;I&#039;m immutable data structure, mean you can&#039;t modify me!&quot;;
var anotherStringValue = stringValue;
stringValue = &quot;I have changed&quot;;
</pre>
<p>此时，anotherStringValue中保存的值会不会也变成“I have changed”？不会。因为</p>
<pre class="brush: js; ">

var anotherStringValue = stringValue;
</pre>
<p>照stringValue中保存的字符串的原样，复制一个字符串（相应地，在内存中分配一块新空间），并将该字符串赋给anotherStringValue。换句话说，这两个变量虽然保存的值相同，但它们的值并不在一块内存中。因此，修改任何一个变量，都不会影响另一个变量。即</p>
<pre class="brush: js; ">

stringValue = &quot;I have changed&quot;;
</pre>
<p>只会影响stringValue的值。但是，确切来讲，stringValue = &#8220;I have changed&#8221;;并不是修改stringValue，而是创建了一个新字符串（相应地，在内存中分配一块新空间），然后让stringValue引用该字符串——更像是替换变量的值；原来的字符串呢？因为没有变量引用它，也就成为垃圾了（当然，垃圾所占用的内存会被回收）。<span id="more-270"></span></p>
<p>由此可见，赋值操作对于不变类型而言，传递的是内存中的值本身。那么，对于可变类型呢？当然，传递的是内存中值的引用（或者说地址），而且无论传递多少次，内存中始终都只有一份原始值——毕竟可变类型大小莫测，只保存一份原始值能最大限度节省内存空间。例如：</p>
<pre class="brush: js; ">

var objectValue = {1:1,&#039;s&#039;:&#039;string&#039;,&#039;innerObject&#039;:{&#039;innerArray&#039; : [1,2,3]}};
var anotherObjectValue = objectValue;
objectValue[1] = 100;
anotherObjectValue[1]; //100
</pre>
<p>不言自明，这里的anotherObjectValue通过赋值操作，从objectValue那里只获得了对原始对象（ {1:1,&#8217;s':&#8217;string&#8217;,'innerObject&#8217;:{&#8216;innerArray&#8217; : [1,2,3]}}）的引用，也就是该对象在内存中的地址，或者说“门牌号码”。因此，通过objectValue修改原始对象的第一个元素（objectValue[1] = 100;），结果同样会在anotherObjectValue[1]那里得到反映——因为这两个变量共享同一份原始值。</p>
<p>在JavaScript中，给函数传递参数是按照上述默认约定——即对不可变类型，传值；对可变类型，传址——进行的。如：</p>
<pre class="brush: js; ">

function example(str, obj){
……
}
example(stringValue,objectValue);
</pre>
<p>调用example函数时，第一个参数传递的是实际的字符串值，第二参数传递的是对象的引用（内存地址）。</p>
<p>在PHP中，定义函数时可以指定相应参数是传值还是传址——通常是传值。其实，这也很容易理解：假如函数要求为某个可变类型参数传值，而不是传址，那么也就意味着内存中会因此多出一份该类型值的副本。相应地，在函数中修改这份新副本，不会影响函数外的原副本。因为新旧副本在内存中就不是同一个地址。</p>
<p>说到这，也就引出了浅复制和深复制的概念。事实上，浅复制和深复制的区别恰恰在于复制可变类型时，是传值还是传址。如果是像往常一样传址（传引用），那么就是浅复制。如果是传值，那么就是深复制。浅复制和深复制到底有什么区别呢？以下面的Python代码为例：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; x = {&#039;username&#039;: &#039;admin&#039;, &#039;machines&#039;: [&#039;foo&#039;, &#039;bar&#039;, &#039;baz&#039;]}
&gt;&gt;&gt; y = x.copy()
&gt;&gt;&gt; y[&#039;username&#039;] = &#039;mlh&#039;
&gt;&gt;&gt; y[&#039;machines&#039;].remove(&#039;bar&#039;)
&gt;&gt;&gt; y
{&#039;username&#039;: &#039;mlh&#039;, &#039;machines&#039;: [&#039;foo&#039;, &#039;baz&#039;]}
&gt;&gt;&gt; x
{&#039;username&#039;: &#039;admin&#039;, &#039;machines&#039;: [&#039;foo&#039;, &#039;baz&#039;]}
</pre>
<p>调用字典x的copy方法返回一个新字典并赋值给y，新字典中带有与原字典相同的键－值对。注意，copy方法采用浅复制创建的新字典，与原字典有区别也有联系。区别体现在，对于原字典中不可变的值，如数字、字符串、元组等，会在新字典中重新生成一份新副本；因此，修改（实际上是替换，或者说是重新赋值）这些键的值（y['username'] = &#8216;mlh&#8217;）不会影响原字典。联系体现在，对于原字典中可变的值，如列表、字典，不会在新字典中生成新副本，而只复制值的引用，即新字典中相应的键保存的是引用，当然，原字典中相应的键保存的也是引用，而且这两个引用都指向同一块内存地址。这就是所谓的浅复制。因此，如果修改的是可变类型的值（y['machines'].remove(&#8216;bar&#8217;)），就意味着修改了新、旧字典共享的值（即本例中的列表['foo', 'bar', 'baz']），因此一定会影响引用该值的原字典。</p>
<p>深复制则不然。深复制是实实在在地把原字典中所有的值全都照原样子重新创建一遍，无论是不变类型值，还是可变类型值。执行深复制后，内存中会存在两份完全一样的数据段，但分别处于不同内存空间中，即地址不同。而且，分别由不同变量（原字典、新字典）引用。因此，经过深复制后修改一个字典，不会影响另一个字典。Python的copy模块中的deepcopy函数可以实现深复制：</p>
<pre class="brush: py; ">

&gt;&gt;&gt; from copy import deepcopy
&gt;&gt;&gt; d = {}
&gt;&gt;&gt; d[&#039;names&#039;] = [&#039;Alfred&#039;, &#039;Bertrand&#039;]
&gt;&gt;&gt; c = d.copy()
&gt;&gt;&gt; dc = deepcopy(d)
&gt;&gt;&gt; d[&#039;names&#039;].append(&#039;Clive&#039;)
&gt;&gt;&gt; c
{&#039;names&#039;: [&#039;Alfred&#039;, &#039;Bertrand&#039;, &#039;Clive&#039;]}
&gt;&gt;&gt; dc
{&#039;names&#039;: [&#039;Alfred&#039;, &#039;Bertrand&#039;]}
</pre>
<p>显然，修改深复制得到的新值不会影响原值；而修改浅复制得到的“新”值，在某种程度上仍然会影响原值。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cn-cuckoo.com/2008/12/24/shallow-or-deep-copy-and-pass-by-value-or-reference-270.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>

