今天再读了《JS高程》的第六章,有了些深入的感悟和理解,总结分享一下。
创建对象的方式有很多,有一种是动态原型模式,最实用的是构造函数与原型组合的模式,原型的动态性在这两个模式里都有所体现,我本人的理解是:前者的“动态”是通过一些判断,看方法是否存在来决定是否对原型进行初始化,同时,在构造函数内部对原型的修改会立即体现在所有的实例中,后者的“动态”是主要是说无论是先创建实例还是先修改原型,对原型对象所做的修改都会立即反应在实例中,针对后者来个栗子(栗子1):
function Person(){} var p = new Person(); Person.prototype.sayHello = function(){ alert("Hello!"); } p.sayHello(); //弹出:Hello
其实,实例与原型之间的关联纽带就是一个定向指针,因此,我感觉构造函数与实例某种程度上是平等关系,只不过构造函数拥有原型指过来的一个指针(constructor)。
到这里,一切都很好理解,偏偏文章接下来有扩展了一点儿内容把我搞迷糊了,直接上码(栗子2):
function Person(){} Person.prototype = { constructor : Person, name : "Tom", age : 29, sayName : function(){ alert(this.name); } } var p = new Person(); p.sayName(); //弹出:"Tom"
那好,既然上边说了,原型有动态性,那我这样写(栗子3):
function Person(){} var p = new Person(); Person.prototype = { constructor : Person, name : "Tom", age : 29, sayName : function(){ alert(this.name); } } p.sayName(); //error
按理说,我实例调用了原型上的方法,应该弹出“Tom”啊,事实上却报错:“undefined is not a function”。
其实,这已经不再是原型动态不动态的问题了,而是实例与新、旧原型对象之间的问题。另外原型与实例间关系,我们可以用isPrototypeOf或者instanceof等来判断。
我们都看到了,栗子1与栗子2、3对原型的修改方式是不一样的,栗子1相当于纯粹的给原型这个对象添加了一个方法,而栗子2、3用的是字面量法创建了一个新的原型(相当于新建一个对象),完完全全,彻彻底底地覆盖了原来的原型对象,只不过用一句“constructor:Person;”伪装了一下,仔细的读者或许会发现我在第二段的说明是给"修改"这个词加粗了,栗子1只是“修改”,栗子2、3是“新建覆盖”。
栗子2中创建实例对象时,原来的原型已经被新建的原型覆盖了,因此能够访问到这个方法。而栗子3中,当创建实例时,它的指针指向的还是之前的原型,即便后来又新建了一个原型对象,这个指针依然没变,一次在原来原型上是访问不到这个方法的,故报错。语言的描述显得很干巴巴,我们再来个栗子(栗子4):
function Person(){} Person.prototype = { name : "Tom", age : 29, sayHello : function(){ alert("Hello"); } } var proto = Person.prototype; var p = new Person(); p.syaHello(); //Hello alert(Person.prototype.isPrototypeOf(proto)); //false alert(Person.prototype.isPrototypeOf(p)); //true alert(proto.isPrototypeOf(p)); //true
上述代码非常清晰的告诉我们:proto里保存的始终是原来的原型对象,而alert里的Person.prototype是已经被覆盖了的新的原型对象,p此时访问的也是新的原型对象。
最后来个栗子(栗子5):
function Person(){} Person.prototype = { var proto = Person.prototype; var p = new Person(); name : "Tom", age : 29, sayHello : function(){ alert("Hello"); } } p.syaHello(); //error alert(Person.prototype.isPrototypeOf(proto)); //false alert(Person.prototype.isPrototypeOf(p)); //false alert(proto.isPrototypeOf(p)); //true
上述代码非常清晰的告诉我们:proto里保存的依然是原来的原型对象,p此时访问的还是原来的原型对象。
综上,个人感觉这两部分的内容不应该放在一块儿说,很容易让人迷糊,其次,看问题,一定能进得去、跳的出,遇到死角,站在更高的角度看一下,就会有新的发现,自己在看这块儿时一直拐不过来弯儿,分明前边说了有动态性,后边确没有体现,并且还相违背,这分明不合理,后来才发现,这其实是两码事儿,应该分开来理解。
(另:这是JS里比较重要的模块儿,本人才学疏浅、难免疏漏,欢迎指正,共同进步^_^)