JS里的对象在Ajax发展之后, 就跟着疯狂起来, 要玩好JS的对象, 就要搞清楚构造器(constructor), 指针(pointer), 原型(prototype)
这篇文章主要记录我对prototype的理解
prototype
最初的用法是, 为了避免方法在构造器里随机数据被实例化时而产生重复的副本
后来被用在"继承"上面了, 注意, JS语义上是没有继承的, 这里说的是人为的实现
总结, prototype其实也是指针, 默认是实例的指针, 当然也可以人为的改变,
父类A, 子类B
默认情况, B.prototype=A.prototype
这样, B可以访问A的所有原型方法, 这时, B的构造器也变成A了
改变一下, B.prototype=A
这样, B可以访问A的所有静态方法, 这时, B的构造器也变成类A的指针->类的指针->Function的指针->Function的构造器
以下是实例验证
function demo(){ var A=function(name){ this.name=name; } A.prototype.showName=function(){alert(this.name);} var B=function(name, age){ A.call(this, name); this.age=age; } //B.prototype=new A(); //这里2行代码的效果是一样的 B.prototype=A.prototype; //目的是返回A的"实例"的指针 B.prototype.showAge=function(){alert(this.age);} var b=new B("david", 31); b.showName(); b.showAge(); }
严重注意以上代码的备注, 是new之后的instance的指针, 不是Class的指针,
下面改一下代码验证一下
var A=function(name){ this.name=name; //return this; 这是隐式代码, JS会自动帮我们执行, 这里是关键 } 改为 var A=function(name){ var o=new Object(); o.name=name; return o; }
然后, 当执行到b.showName();时, 就会抛出异常了, 因为 showName是prototype方法, 是指定在A的实例指针下的, 给A添加方法pointer()以获取实例的指针, 然后把它赋予B.prototype
function demo(){ var A=function(name){ this.name=name; } A.prototype.showName=function(){alert(this.name);} //the function return this, "this" is the pointer of instance A.prototype.pointer=function(){return this;} var B=function(name, age){ A.call(this, name); this.age=age; } //B.prototype=new A(); //B.prototype=A.prototype; B.prototype=new A().pointer(); B.prototype.showAge=function(){alert(this.age);} var b=new B("david", 31); b.showName(); b.showAge(); }
以上代码运行正常, 证明了prototype实质上就是指于实例的指针
那么, 如果, 把prototype的指针指于类, 又会如何呢, 我的想法是, 会得到类的静态方法或属性
以下代码, 给A添加静态方法showSex和静态属性city, 然后把A的类,
结果应该是A的原型方法showName会报错, 而b.showSex和alert(b.city)会得到正确的执行
function demo(){ var A=function(name){ this.name=name; } A.prototype.showName=function(){alert(this.name);} //the function return this, "this" is the pointer of instance A.prototype.pointer=function(){return this;} A.showSex=function(){alert("male");} A.city="shenzhen"; var B=function(name, age){ A.call(this, name); this.age=age; } //B.prototype=new A(); //B.prototype=A.prototype; B.prototype=A; B.prototype.showAge=function(){alert(this.age);} var b=new B("david", 31); try { b.showName(); } catch (exception) { alert(exception); } b.showAge(); b.showSex(); alert(b.city); }
代码执行正常, 验证无误
再检验一下constructor
function demo(){ var A=function(name){ this.name=name; } A.prototype.showName=function(){alert(this.name);} //the function return this, "this" is the pointer of instance A.prototype.showSex=function(){alert("male");} A.prototype.city="shenzhen"; var B=function(name, age){ A.call(this, name); this.age=age; } B.prototype=A.prototype; B.prototype.showAge=function(){alert(this.age);} var b=new B("david", 31); try { alert(b.constructor); //output Class A construct alert(b.constructor==A);//output true } catch (exception) { alert(exception); } }
结果表明
alert(b.constructor), 连 B 的构造器都变成 A 的了
alert(b.constructor==A), 再比较一次, 输出true, 没有悬念了
继续挖掘, 把B.prototype=A, 即把A的类指针赋予B的prototype
function demo(){ var A=function(name){ this.name=name; } A.prototype.showName=function(){alert(this.name);} //the function return this, "this" is the pointer of instance A.prototype.showSex=function(){alert("male");} A.prototype.city="shenzhen"; var B=function(name, age){ A.call(this, name); this.age=age; } B.prototype=A; B.prototype.showAge=function(){alert(this.age);} var b=new B("david", 31); try { alert(b.constructor==Function.constructor);//output true alert(b.constructor); //output Function's constructor } catch (exception) { alert(exception); } }
结果表明, B.prototype指到类A的指针去了, 而类的指针, 又指向类的构造器了
再看看A, 被B"继承"后的样子, 在B.prototype=A的时候, this就已经是指向A了, showAge是不是也应该存在于A的实例呢? 在做个实验, 一个是A被继承前, 一个是A被继承后
function demo(){ var A=function(name){ this.name=name; } A.prototype.showName=function(){alert(this.name);} //the function return this, "this" is the pointer of instance A.prototype.showSex=function(){alert("male");} A.prototype.city="shenzhen"; var a=new A("mochacoffee"); var B=function(name, age){ A.call(this, name); this.age=age; } B.prototype=A.prototype; B.prototype.showAge=function(){alert(this.age);} B.prototype.post="518000"; //add property for test var c=new A("reno"); a.showAge(); //*1 alert(a.showAge); //*2 c.showAge(); //*3 alert(c.showAge); //*4 alert(a.post); //*5 alert(c.post); //*6 }
结果为:
1: undefined
2: function showAge(){...}
3: undefined
4: function showAge(){...}
5: 518000
6: 518000
可以看出, expend之前之后的A, 其实已经是一样了,
undefined, 是因为没有给变量赋值, 但函数指针已经正确指到B的showAge了
关于prototype也是, 对于a, 在被B继承后, 也可以获取了518000了, 用java语言来说, prototype只是实例指针而已, 而不论showAge还是post, 都是一个对象, 所以当a的指针指向这个"后来"加上的对象时, 当然也就可以得到518000了
但这样的话, B被继承A之后, A也得到了B的prototype了, 对于继承机制来说, 这样还算是继承吗? A和B的区别, 仅仅是静态方法和属性而已