2010年5月31日星期一

阮一峰的网络日志

阮一峰的网络日志


1979年的电子打字机

Posted: 30 May 2010 10:12 AM PDT

上周五,《大西洋》月刊的记者James Fallows,在Blog上回忆28年前(1982年)的一篇旧文

那个时候,他刚入行不久,每周有大量的写稿任务,天天都是在打字机旁度过。

这种生活让他痛苦不堪,因为打字机实在太不方便了。如果他写一篇6000个单词的长篇报道,用A4纸印刷出来需要8页,但是用双倍行距在打字机上打出来,需要100页!其中任何一个单词打错了,整页都要重新打。他的文章通常需要修改5遍以上,并且最终定稿时需要多个副本。因此,打字工作量可想而知。

此时,James Fallows想起了,他在杂志上见过广告,有一种叫做"电子打字机"的新产品。

看上去,这种东西能够解除他的打字痛苦。于是,他决定买一台。

那是1979年,行业巨头"王安电脑"的报价是15000美元/台,James Fallows承受不了这个价格。他通过熟人,找到了一家俄亥俄州的小公司Optek,用4000多美元的"内部价",买了他们的产品Sol-20

它是当时最先进的文字处理系统之一,整个系统分成四个部分:

1)主机。内存48KB,CPU为Intel公司的8086芯片,主频2M Hz。

48KB等于49000多个字节(Byte),以一个字节储存一个字符计算,共可储存49000多个字符,相当于6500~7500个单词。每次输入的时候,文章长度不能超过这个数字。

2)12英寸的显示屏。显示效果为黑底白字,或者黑底绿字,就像下面图片中的样子。

3)2台5.25英寸的磁盘驱动器,用作外部储存设备。

5.25英寸大小的磁盘,容量为100KB,大概能够保存两篇长文章。磁盘写入的速度为1000词/秒,所以长文章的保存,最快也需要5~6秒。

4)一台IBM公司出品的"电动打印机"(IBM Selectric typewriter)。

这种打印机每秒打印30个字符,一分钟就是1800个字符,相当于250~300个单词。打完一篇6000单词的文章,需要20分钟以上。所以,当你按下"打印"按钮以后,不妨离开现场,去倒上一杯啤酒,慢慢品尝吧。

经过两个星期的学习期,James Fallows对这套系统爱不释手,写文章已经离不开它了。

他在文章中向读者隆重介绍,这套系统好在哪里:

"整个过程很简单,我只要击打键盘,文字就自动显示在屏幕上了。"(When I sit down to write a letter or start the first draft of an article, I simply type on the keyboard and the words appear on the screen.)

这就告诉了我们,直到1982年,用键盘在屏幕上输入字母,还是一件非常非常酷的事情!

(完)

2010年5月26日星期三

阮一峰的网络日志

阮一峰的网络日志


上海的酸雨

Posted: 25 May 2010 06:37 AM PDT

1.

昨天,《2010上海市环境状况公报》发布。

关于"酸雨"的统计数字,堪称恐怖。

"2009年,全市降水pH平均值为4.66,酸雨频率为74.9%。"

2.

上海属于季风型气候,是一个多雨的城市,全年三天之一的天数在下雨。"酸雨频率74.9%",意味着每个星期都要遇到1场~2场酸雨。

"pH平均值为4.66"更吓人,根据酸雨的定义,这已经到了"重酸雨"的地步。报道称,2008年上海环保局测到的最低pH值为2.3,从化学角度看,这已经不是水了,而接近于强酸了!

去年,香港旺角多次发生"泼酸"事件,有人从高楼向下洒硫酸,一时之间,全港"谈酸色变"。可是,与上海一比,这真的不算什么大事!上海每个星期都在下很酸的酸雨,那才是真正的恐怖!

3.

酸雨有什么危害?

首先,它表明空气污染严重,大气中有大量的含硫、含氮化合物和尘埃粒子。所以,你在上海呼吸的每一口空气,都是不干净的,会刺激你的呼吸道。

其次,酸雨会导致土壤酸化,使得植物难于生长,花草非常容易死亡。

再次,酸雨腐蚀建筑物和金属。暴露在空气中的车辆,如果不加以维护,就会快速生锈,几年后成为废铁。

最后,酸雨对人体皮肤有一定的损害,甚至会导致脱发。因此,你在上海千万不要忘记打伞,不要以为在雨中散步很浪漫。

4.

我在上海环保局的网站上,查阅了历年的环境公报,发现酸雨的发展速度非常快。

90年代中期,上海降水的pH值还是正常的,低于5.65的门槛值,发生频率刚刚超过10%。而到了2008年,pH值快速下降到了4.39,发生频率则猛升到接近80%。

学过化学的朋友都知道,pH值下降1,就意味着酸性提高10倍。所以,这15年来,上海酸雨的浓度和频率都提高了一个数量级!

5.

写到这里,我不禁联想到了上海的房价。它也是提高了一个数量级,并且直到今天,还有人在媒体上说,上海房价"长期看好"、"只升不跌"。

我真想问问这些人:电视只能看CCTV,上网就是"连接被重置",创业会遭遇苛捐杂税和蛮不讲理的政府干预,人大代表号称"代表"你,但是从来不与你见面,幼儿园门口都有拿着钢叉的警卫严密看守,新闻报道都在宣传"不法分子当场击毙",走在街上会遇到酸雨,就连你呼吸的空气都会引发呼吸道疾病,请你回答我,让你花几百万元买一套房子,生活在这个地方,你觉得怎么样?

(完)

2010年5月25日星期二

阮一峰的网络日志

阮一峰的网络日志


Javascript面向对象编程(三):非函数对象的继承

Posted: 24 May 2010 08:13 AM PDT

昨天,我本来打算,把这个话题结束了。

但是,写到一半的时候,我突然意识到,Javascript的继承有两种。一种是基于"函数对象"的继承,也就是一个function去继承另外一个function,这个我已经在昨天介绍过了。

另一种则是基于"非函数对象"的继承,不涉及function。它的做法与前一种情况完全不同。

一、什么是"非函数对象"的继承?

比如,现在有一个对象,叫做"中国人"。

  var Chinese = {
    nation:'中国'
  };

还有一个对象,叫做"医生"。

  var Doctor ={
    career:'医生'
  }

请问,我怎样才能让"医生"去继承"中国人",也就是说,我怎样才能生成一个"中国医生"的对象?

二、object()方法

json格式的发明人Douglas Crockford,提出了一个object()函数,可以做到这一点。

  function object(o) {

    function F() {}

    F.prototype = o;

    return new F();

  }

这个object()函数,其实只做一件事,就是把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。

使用的时候,第一步先在父对象的基础上,生成子对象:

  var Doctor = object(Chinese);

然后,再加上子对象本身的属性:

  Doctor.career = '医生';

这时,子对象已经继承了父对象的属性了。

  alert(Doctor.nation); //中国

三、浅拷贝

除了使用"prototype链"以外,还有另一种思路:把父对象的属性,全部拷贝给子对象,也能实现继承。

下面这个函数,就是在做拷贝:

  function extendCopy(p) {

    var c = {};

    for (var i in p) {
      c[i] = p[i];
    }

    c.uber = p;

    return c;
  }

使用的时候,这样写:

  var Doctor = extendCopy(Chinese);

  Doctor.career = '医生';

  alert(Doctor.nation); // 中国

但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。

请看,现在给Chinese添加一个"出生地"属性,它的值是一个数组。

  Chinese.birthPlaces = ['北京','上海','香港'];

通过extendCopy()函数,Doctor继承了Chinese。

  var Doctor = extendCopy(Chinese);

然后,我们为Doctor的"出生地"添加一个城市:

  Doctor.birthPlaces.push('厦门');

发生了什么事?Chinese的"出生地"也被改掉了!

  alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门

  alert(Chinese.birthPlaces); //北京, 上海, 香港, 厦门

所以,extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做"浅拷贝"。这是早期jQuery实现继承的方式。

四、深拷贝

所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用"浅拷贝"就行了。

  function deepCopy(p, c) {

    var c = c || {};

    for (var i in p) {

      if (typeof p[i] === 'object') {

        c[i] = (p[i].constructor === Array) ? [] : {};

        deepCopy(p[i], c[i]);

      } else {

         c[i] = p[i];

      }
    }

    return c;
  }

使用的时候这样写:

  var Doctor = deepCopy(Chinese);

现在,给父对象加一个属性,值为数组。然后,在子对象上修改这个属性:

  Chinese.birthPlaces = ['北京','上海','香港'];

  Doctor.birthPlaces.push('厦门');

这时,父对象就不会受到影响了。

  alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门

  alert(Chinese.birthPlaces); //北京, 上海, 香港

目前,jQuery库使用的就是这种继承方法。

(完)

2010年5月24日星期一

阮一峰的网络日志

阮一峰的网络日志


Javascript面向对象编程(二):继承

Posted: 23 May 2010 05:58 AM PDT

上一次的文章,主要介绍了如何"封装"数据和方法,从原型对象生成实例。

今天要介绍的是,多个原型对象之间如何"继承"。

比如,现在有一个"动物"对象,


  function Animal(){

    this.species = "动物";

  }

还有一个"猫"对象,


  function Cat(name,color){

    this.name = name;

    this.color = color;

  }

怎样才能使"猫"继承"动物"呢?

1. 原型对象绑定

最简单的方法,大概就是使用call或apply方法,将父对象绑定在子对象上,也就是在子对象函数中加一行:

  function Cat(name,color){

    Animal.apply(this, arguments);

    this.name = name;

    this.color = color;

  }

  var cat1 = new Cat("大毛","黄色");

  alert(cat1.species); // 动物

2. prototype模式

更常见的做法,则是使用prototype属性。

如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。

  Cat.prototype = new Animal();

  Cat.prototype.constructor = Cat;

  var cat1 = new Cat("大毛","黄色");

  alert(cat1.species); // 动物

代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。

  Cat.prototype = new Animal();

它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是,第二行又是什么意思呢?

  Cat.prototype.constructor = Cat;

原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。也就是说,Cat.prototype 这个对象的constructor属性,是指向Cat的。

我们在前一步已经删除了这个prototype对象原来的值,所以新的prototype对象没有constructor属性,所以我们必须手动加上去,否则后面的"继承链"会出问题。这就是第二行的意思。

总之,这是很重要的一点,编程中务必要遵守。下文都遵循这一点,即如果替换了prototype对象,

  o.prototype = {};

那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。

  o.prototype.constructor = o;

3. 直接继承prototype

由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。

现在,我们先将Animal对象改写:

  function Animal(){ }

  Animal.prototype.species = "动物";

然后,将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。

  Cat.prototype = Animal.prototype;

  Cat.prototype.constructor = Cat;

  var cat1 = new Cat("大毛","黄色");

  alert(cat1.species); // 动物

与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。

所以,上面这一段代码其实是有问题的。请看第二行

  Cat.prototype.constructor = Cat;

这一句实际上把Animal.prototype对象的constructor属性也改掉了!

  alert(Animal.prototype.constructor); // Cat

4. 利用空对象作为中介

由于"直接继承prototype"存在上述的缺点,所以可以利用一个空对象作为中介。

  var F = function(){};

  F.prototype = Animal.prototype;

  Cat.prototype = new F();

  Cat.prototype.constructor = Cat;

F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。

  alert(Animal.prototype.constructor); // Animal

5. prototype模式的封装函数

我们将上面的方法,封装成一个函数,便于使用。

  function extend(Child, Parent) {

    var F = function(){};

    F.prototype = Parent.prototype;

    Child.prototype = new F();

    Child.prototype.constructor = Child;

    Child.uber = Parent.prototype;

  }

使用的时候,方法如下

  extend(Cat,Animal);

  var cat1 = new Cat("大毛","黄色");

  alert(cat1.species); // 动物

这个extend函数,就是YUI库如何实现继承的方法。

另外,说明一点。函数体最后一行

  Child.uber = Parent.prototype;

意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。这等于是在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。

6. 拷贝继承

上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?

首先,还是把Animal的所有不变属性,都放到它的prototype对象上。

  function Animal(){}

  Animal.prototype.species = "动物";

然后,再写一个函数,实现属性拷贝的目的。

  function extend2(Child, Parent) {

    var p = Parent.prototype;

    var c = Child.prototype;

    for (var i in p) {

      c[i] = p[i];

      }

    c.uber = p;

  }

这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。

使用的时候,这样写:

  extend2(Cat, Animal);

  var cat1 = new Cat("大毛","黄色");

  alert(cat1.species); // 动物

(完)

2010年5月22日星期六

阮一峰的网络日志

阮一峰的网络日志


善于说,善于做

Posted: 21 May 2010 07:53 AM PDT

Seth Godin是美国畅销书作家,专门教你如何创业。

我读过他的《创业者圣经》,还真有收获。所以,就订阅了他的blog,每天都有更新。

昨天他的更新,让我读得心情激荡。

从"善于说"到"善于做"

作者:Seth Godin

企业的销售部门,就是负责向消费者"说话"的部门。

广告是一种"说",传单是一种"说",展览会也是一种"说"。

但是,你无法说得太多,因为不能强迫人们去听。

所以,最后真正起作用的,还是"做"。这意味着,你的产品、你的服务,才是最能打动顾客的武器。

如果你"善于说",但是不"善于做",那么你无法把东西卖出去。


他的这篇文章,说到了我最心虚的地方。我总觉得,自己说得太多,做得太少。

我的blog的读者人数越多,我就越心虚。因为如果别人问我,"你说得头头是道,请问你做了什么?"我就无言以对,确实没有成果嘛。

说起来,真是令人哭笑不得的事情。多年前,我一门心思想要创业,根本没想过要写blog,只想在网上记录一些随感和读书笔记。可是,因为模式问题、技术问题、资金问题、政策问题,项目一直没有做起来,反而是blog越写越多,在我没有做任何宣传的情况下,居然有了超过5位数的订阅者。

命运的嘲笑莫过于此:你想做的事情没有做成,你不想出名的时候反而出名了。所以,我总是很低调,不出席公共场合,也不太想见人,因为觉得自己很惭愧。

在我看来,Blog写得再好,也只是"善于说",而真正有用的是"善于做"。正如马克思的名言,"哲学家们只是用不同的方式解释世界,而问题在于改变世界。"

我对自己的期望就是,"善于说",更"善于做"。我的Blog已经写得太久了,不能永远这样写下去。我必须要有一个自己的项目,去实现那些Blog不能实现的事情。这一天不会太远了。

(完)

2010年5月21日星期五

阮一峰的网络日志

阮一峰的网络日志


VP8视频格式初探

Posted: 20 May 2010 05:09 AM PDT

昨天,Google发布了一个开源项目WebM

这个项目的目的,是在文件格式方面,为制作和发布互联网视频提供了一个开源的解决方案。

WebM采用MKV作为封装格式,里面的音频编码用Vorbis格式,视频编码用VP8格式。

MKV和Vorbis都是早就存在的开源格式,而VP8本来属于On2公司的封闭格式,是不开源的。去年8月,Google花了1亿美元收购On2,才有了今天。

这个决定轰动了业界,因为这意味着,我们终于有了一个没有专利约束、并且获得大公司支持的免费视频编码格式VP8(详见我翻译的《HTML5视频格式之争》一文)。

但是,VP8其实只是一种规格,以前从来没有公开过,也没有任何基于它的产品问世。所以,外界一直不知道VP8的性能究竟如何。

开源视频转换程序ffmpeg的开发者之一Jason Garrett-Glaser,有机会提前接触到了VP8。他写了一篇很详细的评估,说出了自己对VP8的印象,并将VP8与专利格式H.264做了比较。

下面就是这篇评估的简单翻译,删去了讨论技术细节的部分。

=======================

VP8视频格式初探(精简版)

作者:Jason Garrett-Glaser

译者:阮一峰

原文网址:http://x264dev.multimedia.cx/?p=377


一、On2是一家怎样的公司?

在开始讨论VP8之前,我想先谈谈对On2公司的印象。

它曾经宣称,VP8比H.264的性能高出50%。但是,它的话是不可信的。因为它也说过,VP7比H.264的性能高出15%。但是后来人们发现,VP7远远不如H.264。

2003年,On2宣布VP3开源。表面上,它好像为开源事业做出了贡献。但是实际上,它的目的是,希望开源社区为它修正错误。Theora项目上了当,选择VP3作为自己的代码基础,结果修改代码的时间用去了6年,做出来的产品性能还是不如H.264。

二、VP8的规格

这份规格文件令人很不满意。很多技术细节,不是写得太简单,就是写得太模糊。大部分地方都是直接张贴C代码,而不是用文字表述。要知道C代码和格式规格,完全是两回事,根本不能替代。

我曾经觉得,H.264的规格写得太啰嗦,但它至少是准确的。VP8的规格根本就是不清晰,不准确,太简短,很多细节没有解释清楚。老实说,仅仅根据这份规格,地球上根本不可能有人能够写出VP8的解码器。

更令人惊奇的是,根据代码中的注释,VP8有些部分写于2004年初,比H.264还要古老!On2在此后6年的时间中,都不做修改,这是说不过去的。

三、VP8编码器(Encoder)

首先要明确一件事情。格式规格和它的具体实现,是两回事。一个很好的编码程序,可能是基于一个很烂的规格;而一个很好的规格,也可能会产生出一个很烂的编码程序。

原厂提供的解码器,生成的图像质量虽然大大好于VP3,但是并没有明显胜过H.264的地方。

这个编码器的编码速度要慢于H.264。我的机器是1.6Ghz的Core i7,编码1080p时速度为26fps;而用H.264编码器,选择"最快速度"选项时,可以达到101fps。

在压缩性能方面,VP8也不如H.264。

四、VP8解码器(Decoder)

原厂提供的VP8解码器,比ffmpeg的H.264解码器慢了16%,更不要说其他更先进的H.264解码器了。

就算最终通过各种优化,VP8解码器可以达到H.264的同样水平。但是,H.264有众多硬件支持,而VP8只能靠软解码,所以谁快谁慢不言而喻。

五、专利问题

VP8的一大卖点,就是没有专利权问题。但是,它的某些细节与H.264太像,我觉得已经很难用巧合解释了,将来肯定会出现专利纠纷。

在没有明确证据表明VP8通过专利检验之前,我建议使用时一定要非常谨慎。

[附录]

Youtube已经开始提供WebM视频了,不过只有最新的浏览器才支持。具体的观看方法请查看http://www.ghacks.net/2010/05/20/webm-video/(英文)。

(完)

2010年5月18日星期二

阮一峰的网络日志

阮一峰的网络日志


不会沉没的海盗湾

Posted: 18 May 2010 04:43 AM PDT

几天前,德国汉堡法庭判决,海盗湾必须强制下线。

于是,北京时间昨天深夜,带宽提供商断开了海盗湾的服务器。这至少是第四次海盗湾被强制下线了。

我当时就预言,海盗湾一定会第四次回来,而且就在24小时之内。

结果就在北京时间18点左右,海盗湾的首页果然可以重新打开了。而且,它的首页还放了一张图片。

下面写着:"我就是你们的天网。你们想用那些可笑的伎俩控制互联网,做梦去吧!"

海盗湾的官方网志,也贴出了最新回应

请记住,我们是关不掉的!

各位,你们可能已经听说了,那些家伙又一次试图关闭我们。他们不会成功的。我们的带宽提供商是好人,所以我们决定把网站搬个地方,不让他们为难。

海盗湾是不会沉没的。只要我们愿意,它将永远航行在互联网上。请记住这一点。

自从2003年海盗湾上线以来,他们从我们这里得到的,只是一次又一次的失败。

多么激动人心的宣言啊!

只要这个世界还有一个热爱自由的人存在,我们就不会下线。

那些与互联网为敌的人们,你们是不会成功的。

(完)

2010年5月17日星期一

阮一峰的网络日志

阮一峰的网络日志


Javascript 面向对象编程(一):封装

Posted: 17 May 2010 02:03 AM PDT

学习Javascript,最难的地方是什么?

我觉得,Object(对象)最难。因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握。

下面就是我的学习笔记,希望对大家学习这个部分有所帮助。我主要参考了Object-Oriented JavaScriptProfessional JavaScript for Web Developers (2nd Edition)这两本书。它们都是非常优秀的Javascript读物,推荐阅读。

笔记分成两部分。今天的第一部分是讨论"封装"(Encapsulation),下一次的第二部分讨论"继承"(Inheritance)。

============================

Javascript 面向对象编程(一):封装

作者:阮一峰

Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。

那么,如果我们要把"属性"(property)和"方法"(method),封装成一个对象,甚至要从原型对象生成一个实例对象,我们应该怎么做呢?

1. 生成对象的原始模式

假定我们把猫看成一个对象,它有"名字"和"颜色"两个属性。

  var Cat = {

    name : '',

    color : ''

  }

现在,我们需要根据这个原型对象,生成两个实例对象。

  var cat1 = {};

    cat1.name = "大毛";

    cat1.color = "黄色";

  var cat2 = {};

    cat2.name = "二毛";

    cat2.color = "黑色";

好了,这就是最简单的封装了。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。

2. 原始模式的改进

我们可以写一个函数,解决代码重复的问题。

  function Cat(name,color){

    return {

      name:name,

      color:color

    }

  }

然后生成实例对象,就等于是在调用函数:

  var cat1 = Cat("大毛","黄色");

  var cat2 = Cat("二毛","黑色");

这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。

3. 构造函数模式

为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。

所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

比如,猫的原型对象现在可以这样写,

  function Cat(name,color){

    this.name=name;

    this.color=color;

  }

我们现在就可以生成实例对象了。

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat("二毛","黑色");

  alert(cat1.name); // 大毛

  alert(cat1.color); // 黄色

这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。

  alert(cat1.constructor == Cat); //true

  alert(cat2.constructor == Cat); //true

Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。

  alert(cat1 instanceof Cat); //true

  alert(cat2 instanceof Cat); //true

4. 构造函数模式的问题

构造函数方法很好用,但是存在一个浪费内存的问题。

请看,我们现在为Cat对象添加一个不变的属性"type"(种类),再添加一个方法eat(吃老鼠)。那么,原型对象Cat就变成了下面这样:

  function Cat(name,color){

    this.name = name;

    this.color = color;

    this.type = "猫科动物";

    this.eat = function(){alert("吃老鼠");};

  }

还是采用同样的方法,生成实例:

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat ("二毛","黑色");

  alert(cat1.type); // 猫科动物

  cat1.eat(); // 吃老鼠

表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

  alert(cat1.eat == cat2.eat); //false

能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。

5. Prototype模式

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

  function Cat(name,color){

    this.name = name;

    this.color = color;

  }

  Cat.prototype.type = "猫科动物";

  Cat.prototype.eat = function(){alert("吃老鼠")};

然后,生成实例。

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat("二毛","黑色");

  alert(cat1.type); // 猫科动物

  cat1.eat(); // 吃老鼠

这时所有实例的type属性和eat()方法,其实都是一个内存地址,指向prototype对象,因此就提高了运行效率。

  alert(cat1.eat == cat2.eat); //true

6. Prototype模式的验证方法

6.1 isPrototypeOf()

这个方法用来判断,某个proptotype对象和某个实例之间的关系。

  alert(Cat.prototype.isPrototypeOf(cat1)); //true

  alert(Cat.prototype.isPrototypeOf(cat2)); //true

6.2 hasOwnProperty()

每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

  alert(cat1.hasOwnProperty("name")); // true

  alert(cat1.hasOwnProperty("type")); // false

6.3 in运算符

in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

  alert("name" in cat1); // true

  alert("type" in cat1); // true

in运算符还可以用来遍历某个对象的所有属性。

  for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

(完)

2010年5月16日星期日

阮一峰的网络日志

阮一峰的网络日志


HTML5的视频格式之争

Posted: 15 May 2010 05:55 AM PDT

你可能听说过,HTML5支持直接播放视频。

但是,你可能不知道的是,这背后涉及到复杂的视频格式之争,甚至还牵涉到所有的电子影像设备。

未来,如何在互联网上看视频?

如果你想知道答案,请不要错过下面这篇精彩的文章。它是我迄今读到的最清晰易懂的解说。

=========================

HTML5的视频格式之争

作者:Ruthsarian

译者:阮一峰

原文网址:http://ruthsarian.wordpress.com/2010/05/05/the-elephant-in-html5s-room/

发表日期:2010年5月5日


下一代的网页语言HTML5,提供了一个video标签。它允许开发者直接将视频嵌入网页,不需要任何第三方插件(比如 Adobe公司的Flash)就能播放。

这当然是一大进步。

但是,有一个核心问题,却没有得到解决。HTML5没有规定,浏览器到底应该播放哪一种格式的视频。浏览器厂商可以自行选择支持的格式。

现在,最流行的视频格式是H.264。它有很多优点,编码后生成的视频文件,体积较小,画质也不错。蓝光技术(Blu-ray)就采用这种格式,眼下几乎所有的高清摄像机----不管民用的还是商业的----都使用它。互联网上的在线视频播放,采用它的比例也正在不断上升。

不过,H.264是一种专利视频格式。它的专利被一家MPEG-LA公司控制。

这家公司专门负责管理与H.264有关的"专利池"(patent pool)。所谓"专利池",就是指好几家公司把各自的H.264专利放在一起,组成一个"池"。其他人如果要使用H.264,就必须向"池"的管理公司申请许可,一旦获得了许可,就可以使用"池"中的所有专利。

这就是说,MPEG-LA公司是H.264的实际管理者和收费者。任何支持播放H.264视频的DVD播放机、蓝光播放机、摄像机或者别的设备,都必定有一张MPEG-LA颁发的许可证。

目前为了推广H.264,MPEG-LA规定,只要你的视频用于互联网上的免费播放,就可以无偿获得使用许可证。这就是为什么YouTube可以免费使用MPEG-LA许可证的原因。而像Netflix这样的付费收看公司,就得不到这种优惠了。

MPEG-LA的这种促销政策,并不会永远不变。当前的H.264免费许可证,将于2010年12月31日当期。那么,从2011年1月1日起,MPEG-LA会不会向YouTube、甚至向嵌入H.264视频的个人网站收费呢?完全存在这种可能。专利使用费会是多少?谁也不知道,这由MPEG-LA说了算。另一种可能是,MPEG-LA为了进一步推广H.264,继续保持免费政策,等到2、3年后,它一统市场了,再开始收费。到了那时,如果大多数公司都依赖这种格式,那么它们就别无选择,只能向MPEG-LA交钱。

一些人对这种情形,感到担忧和不满。他们决定自行开发一种没有专利的视频格式,生成的文件体积要与H.264相仿,画质也要差不多。这种格式就叫做Theora

Theora的主要开发者,也是Ogg Vorbis([译注] 一种开源的、无专利的音频压缩格式)的开发者。Theora的基础是On2 Technologies公司开发的VP3视频格式。本世纪初,On2公司将VP3放入了公共领域。Theora对VP3做了大量改进,并且在开发过程中非常小心,避免触犯到任何现存专利。结果,我们就有了一种任何人都可以免费使用、不用担心专利问题的视频格式。

听上去很欢欣鼓舞,对不对?但是为什么大家还在用H.264,还不是抛弃它呢?

这里有几个原因。

第一个原因。没有一家实体公司来承担Theora的专利责任,用户必须自己负责。万一将来有人起诉Theora侵犯了某某专利,用户很可能必须自己掏钱打官司。所以,业界有一种广泛的担心,现在之所以没人起诉Theora,并不是这些人不想起诉,而是要等到某一家大型公司开始采用Theora以后,有可能出现高额的专利赔偿金时,他们再来起诉。最近,苹果公司的CEO乔布斯,就公开表达了这种看法

不过,话说回来,这么多年来,一直有人在威胁Theora,但是从来没人真的起诉。部分原因可能确实是Theora目前还没有重量级使用者,敲诈不到足够的金钱。不过,很多人相信还存在另一种原因,那就是这些"黑暗中的威胁者"害怕闹上法庭以后,万一法庭最后判决Theora胜诉,不存在任何专利问题,那么MPEG-LA公司的大麻烦就来了。因为大家可能就不会再付给它专利费了,而是放心地改为使用Theora了。

第二个原因。一些主要的大公司,本身就是MPEG-LA"专利池"的所有者,比如苹果公司和微软公司。它们各自拥有一些H.264专利,可以从推广H.264中赚到钱,Theora的普及将对它们的利润产生不利影响。所以,苹果公司的Safari浏览器和微软公司的IE浏览器,完全不支持Theora。

第三个原因。有一种观点认为,Theora生成的视频质量不如H.264。早期的Theora 1.0,确实效果不好;但是Theora 1.1 已经被证明,效果不逊于H.264,尤其是在低码率的情况下。对Theora的怀疑,导致基于Theora的硬件解码器非常少。这一点对Theora的打击很大。因为H.264解码芯片随处可见,苹果公司的每一台iTouch、iPhone、iPad里面都有,进一步说,过去5年中全世界生产的几乎每一台摄像机都支持H.264硬解码。

现在,再回过头谈HTML5和它的video标签。

开源浏览器Firefox和KHTML,没有资源去购买H.264许可证。因此,它们原生不支持H.264格式的视频,除非用户自己安装第三方插件。而微软公司和苹果公司则是完全不支持Theora,只支持H.264。

这意味着,未来的HTML5网页,不存在一种通用的视频格式。也就是说,HTML5网站开发者必须为同一个视频,准备两个格式的版本,一个是H.264,另一个是Theora。不过,开发者还有另一个选择,就是要求用户安装第三方插件。

猜猜看,大多数开发者会怎么做?他们很可能什么也不做!保持现状不就行了,让用户继续用Flash观看视频吧,什么麻烦都没了。

等一等!苹果公司已经宣布放弃Flash了。它的iPad、iPhone和iTouch,不支持任何形式的Flash。想在这些设备上播放视频的开发者,不得不求助于HTML5的video标签。

解决方案是什么?

我想大多数开发者会选择做一个浏览器"嗅探",专门为苹果公司的设备提供一个H.264格式的视频,其余的设备则显示一个Flash播放器,里面也可以播放这个H.264格式的视频。所以,Flash和H.264成了赢家,Theora和开源软件成了输家,这真是一个令人悲哀的结果。

我们也许有机会避免这种结局。

去年,Google收购了On2 Technologies,并且计划把On2的VP8格式开源。 VP8和VP3是同一个体系的视频格式,这意味着它和Theora有亲缘关系。但是,VP8比VP3高出5个版本,这意味着它的效果应该好于Theora。那么,我们就会有一个更好的开源格式,它的背后是一家真正的大公司(Google)在支持。此外,全世界最大的视频网站Youtube,归Google所有,毫无疑问,它会采用VP8。因此,有了这些因素,我们就可能在今后几年中,看到VP8格式的视频飞速增长,把Theora和H.264都甩在身后。

不过,我的预测是,将来的互联网上,各种视频格式都有一席之地。Theora将继续得到开源浏览器(比如Firefox)的支持,苹果公司和微软公司将不断推进H.264,Google将尝试在YouTube上使用VP8。但是,Google也会被迫保留H.264和Flash格式的视频,这是为了支持苹果公司的设备和历史遗留下来的不支持HD视频的设备。

我很希望,Google把VP8放入公共领域。那样的话,Xiph就能利用VP8,做出Theora 2.0。然后,Firefox、 WebKit和Opera都开始支持Theora 2,YouTube也开始把它的视频转为VP8/Theora 2兼容格式,而Flash也将升级支持Theora 2。那么,只剩下苹果公司一家,它要么也支持Theora 2,要么只能开一个自己的视频分享网站,因为它的iPhone用户到时将无法收看Youtube。

这样的未来,难道不值得期待吗?

(完)

2010年5月12日星期三

阮一峰的网络日志

阮一峰的网络日志


我的Tweet档案

Posted: 12 May 2010 12:10 AM PDT

"微博"就是不超过140个字的微型网志。

很长一段时间,我都想不出它有什么用,140个字可以说什么?大概只有自恋狂,才会把自己的一举一动贴上网,让全世界看到吧!

所以,尽管我在2007年5月就注册了,但是一直没有使用。我还做了一个试验,用它直播我的大连之行,最后的结论是,它对我真的没用!

不过,从今年开始,我的看法变了。

我发现,查看最新消息,比如某地发生地震,"微博"是最好的工具。而且,人与人之间的直接联络,用它也非常方便。

所以,我就重新启用自己的帐户了。我的ID是@ruanyf,欢迎大家follow。

但是,官方网站的用户界面有很多缺点,比如不能使用标签,不能查看档案,不提供档案搜索等等。所以,我就决定自己做一个本地档案,方便使用。

网址是:http://www.ruanyifeng.com/tweets/ ,欢迎访问。

=============================

它基本上采用Doug Bowman的方案,非常容易搭建,你完全可以用20分钟,自己做一个。下面就是具体步骤。

第一步,从TweetBackup.com下载你的所有发言。不过,最多只能返回3200条结果。

第二步,用文字编辑器打开下载的RSS文件,把每段话前面的"发言人: "用替换功能去掉,比如我的是"ruanyf: "。另外,这个文件中每段话的title部分,可能会出现乱码,你可以不用管它,也可以用正则替换来处理。

第三步,搭建一个新的Wordpress,然后打开"Import"功能,选择RSS格式,将上一步的文件上传输入。

第四步,安装Twitter Tools plugin插件,并做相应设置。

第五步,安装Autolink URI插件。它的作用是将网址字符串,转成超级链接。但是,除了网址以外,我们还要转"@"、"#"这两个特殊字符,所以要对这个插件做一些修改。

打开这个插件的sem-autolink-uri.php文件,找到下面这一行:

$text = autolink_uri::unescape($text);

在它前面,再加两行,

$text = preg_replace_callback("/(^|\s)@(\w+)/",array('autolink_uri', 'tweet_callback1'), $text);

$text = preg_replace_callback("/(^|\s)#(\w+)/",array('autolink_uri', 'tweet_callback2'), $text);

然后,找到email_callback()函数,

function email_callback($match) {
$email = end($match);
return '<a href="' . esc_url('mailto:' . $email) . '">' . $email . '</a>';
} # email_callback()

在它后面,再加两个函数,

function tweet_callback1($match) {
return $match[1].'@<a href="http://twitter.com/' . $match[2] . '">' . $match[2] . '</a>';
}

function tweet_callback2($match) {
return $match[1].'#<a href="http://search.twitter.com/search?q=%23' . $match[2] . '">' . $match[2] . '</a>';
}

如果,你觉得这样修改太麻烦,也可以直接下载Autolink URI插件的修改版(4KB)。

第六步,下载Doug Bowman的样式文件,39KB。(需要修改头像、网址等一些小地方。)

到此就全部完成了。

(完)

2010年5月8日星期六

阮一峰的网络日志

阮一峰的网络日志


Dropbox的创业经历

Posted: 08 May 2010 01:06 AM PDT

上月底,美国旧金山有一场演讲会"Startup Lessons Learned"

许多创业公司聚在一起,分享经验教训,其中就有Dropbox。它是一家云储存公司,最近很红,你可以用它在朋友之间、电脑之间共享文件。

Drew Houston是这家公司的CEO,在会议上做了主题演讲。演讲的视频PPT,都已经放上了互联网。我看了以后,爱不释手。

我觉得,这不仅是难得的第一手创业经验,而且PPT做得非常鲜明生动,很值得借鉴。我就把它翻译出来了,大家一起来学习吧。

============================

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

28.

29.

30.

31.

32.

33.

34.

(完)