Doc:http://docs.google.com/Doc?docid=0AZUdXGtQa0xqZGRocmo3MzZfMjEyaG5tNGdncXQ&hl=en
继续装机。看到装机能赚钱,但咱没特别的厂家支持,不过也没关系,这样能给用户提供的电脑品种反而更多。OK,那咱也打算去申请个小工作间,开始给用户装机。我们这提供多种主流的硬件配置,比如提供了Acer、Dell,这样用户只要说给我装个Acer的,ok,那就给用户一个Acer的机器。但我们这里不生产Acer的配件(当然Acer也不生产所有配件... anyway)我们会到其他地方(可能是Acer生产线)去拿配件来给用户组装。同时用户看到我们这代理nVidia,他们也可以直接向我们要nVidia的显卡(我们帮他们拿呗,赚点路费)。
到这里我们把这个例子和js里的类、对象对应起来看看。
我们的工作间 <-> js里定义的类 // 只是个名字
工作间的功能 <-> 类的prototype // 这里有我们想给用户提供什么功能和东西
装Acer的机器 <-> protytope里的getAcer方法 // 具体方法了
来块nVidia显卡(因为我们有渠道拿到显卡) <-> getNVidia方法 // 这个是prototype链提供的
以上废话结束,直接进入代码。
一般的说JavaScript来定义类的方法如下:
function MyClass () {}
MyClass.prototype.foo = function() {}
MyClass.prototype.bar = 'Hello world';
...
var myClass = new MyClass();
这样会让刚接触js的人有点疑问,这js里的函数怎么又能做这种事呢,怎么类和函数有啥关系?我不想把自己的关于“js里并没有类”的想法强加给别人(当然不一定对,但对指导目前),你能接受并且真正的理解是最好。或者这样写的话可能更符合我们举的例子:
var MyClass = new Function(); // 定义了我们的类名
MyClass.prototype.foo = function() {} // 定义了一个提供的方法
var myClass = new MyClass(); // 生成实例
myClass.foo(); // 执行方法
以上是大众化的定义js类的方法,当然大家都是这么写的,但自己要明白js里的类到底是如何工作的:prototype链。
我们先画个草图来表示一下类、prototype、实例的联系:
这个图并不完整,有些东西没有标在上面,比如prototype里的constructor,{foo: ..., bar: ...}是一个对象,它也应该有个__proto__指向Object的prototype等等。为了清晰说明prototype链的问题,我这里不会把这些东西都画出来,但你应该自己去考虑这些存在的东西(现在不会考虑没问题,看完这篇后应该知道了)
OK,言归正传。
我们通过 function MyClass(), MyClass = new Function()等方法来定义这个函数是,系统会给他开辟一个prototype空间,默认里面有个会有一些方法(如constructor,以后再说),这时候我们可以通过MyClass.prototype直接访问它,并且增加、修改里面的内容。
var myClass = new MyClass(); 时,系统生成了一个对象叫myClass,他有一个属性__proto__,这个__proto__指向MyClass.prototype。
myClass.foo(); 这是js会看myClass这个对象里面有没有foo方法,有则执行,无则沿prototype链(通过__proto__)向上找,直到找到找到这个方法或者到达最顶层,没该方法,出错。
以这个例子为基础,再讲一点:myClass这个对象里面现在“有”foo方法和bar属性(这里的“有”其实是他能够在prototype链上找到的,并不表示自己有,就想我们的装机店,我们有各种配件,那是因为我们能找到供应商,而不是自己生产)。
很多人会并不在意这一点,比如想“修改”myClass里的bar的值:
myClass.bar = "changed";
看过上一篇“JavaScript对象”,我想首次指向这句的时候大家应该知道他做了什么。myClass.bar并没有把自己__proto__里的bar的值修改掉(可以想想修改了会有什么后果),而是给自己新增一个属性bar,并赋值为"changed"。
我们上面的MyClass看上去是一个空函数,貌似只提供了一个prototype给自己的实例去调用,下面的这种写法可能比较实际一点。我们举经典的例子,Shape。先看下如何定义一个Point。
function Point(x, y) {
this.x = x;
this.y = y;
}
var p = new Point(0, 0);
功能一目了然,定义了Point类,可以用两个参数来初始化一个对象,这个对象会有x、y两个属性(这里是真的有)。为了表示的清晰一点,还是看图:
注意这里Point.prototype里面并没有什么自定义的属性和方法了(我们没定义嘛)
但是当运行 var p = new Point(); 时,js回去调用自己的constructor方法,constructor有时指向Point这个函数的... 关系有点怪,但这种结构也为我们做一些js的底层框架库提供了很好的设计基础(一个对象可以通过constructor来获知自己是谁的实例)。
在p的constructor方法(Point函数)里面,我们通过this.x, this.y给p设置了两个属性,还是那句话,当我们尝试对一个对象的属性赋值时,他会给自己添加一个这样的属性(已有的话当然直接修改即可)。
这样就完成了我们如何去定义一个js的类,sigh... 为了表述的清楚,我还是使用类、实例这些OOP的概念,但还是强调一句,js里面对象的工作方式是以prototype链为基础的,而不像c++、java里面每个对象都会有自己的内存空间来存放自己的属性。特别对于继承来说,每个实例要存放自己类的属性,还要存放基类的属性,每个对象都是,不管这些属性是否是只读的。我们将在下一篇“JavaScript类的继承”里看到js里面是如何实现“继承”的:prototype链。x你丫没完没了了,怎么啥都是prototype链...