JavaScript学习总结--创建对象(4_原型扩展)
上一节说到,我们使用原型+构造函数混合的方式创建了一个对象,这个对象很好的区分了实例对象的私有属性和它们的共享属性,代码是这样的
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } Person.prototype.sayName=function(){ console.log(this.name); } var person_01=new Person("Sakura",22,"前端开发"); var person_02=new Person("Misaka",20,"网页设计");
那么问题又来了,当我们需要在Person类的原型上添加更多的属性和方法时,就不可避免的要一遍又一遍的重写Person.prototype.XXX=XXX
很显然我们是不愿意这样写的
我们知道原型实际上也是一个对象,是对象那它就可以用字面量的方式重写
理所当然的我们可以这样写
//省略之前代码 Person.prototype={ sayName:function(){ console.log(this).name; }, sex:"man", country:"China" }
但是这里有一个问题需要注意,我们知道原本Person的原型对象有一个constructor属性,指向Persou构造函数本身
而当我们用字面量的方式重写了原型对象以后,就相当于抹去了它原本的所有属性,直接将一个新对象赋值给了prototype对象,那么这时原本它隐含的constructor就被改写了,重写后的原型对象constructor属性指向就变成了Object,而非原本的Person,所以这里们需要人为的改写原型对象的constructor属性
Person.prototype={ constructor:Person, //... }
保证通过字面量重写后的原型对象和构造函数保持连接的关系
重写原型时有一点非常重要
我们先来看一个例子
function Person(name,age){ this.name=name; this.age=age; } var person_01=new Person("Sakura",22,"前端开发"); //注意这里先实例化了对象person_01,然后才在原型上添加了sayName方法 Person.prototype.sayName=function(){ console.log(this.name); } person_01.sayName(); //Sakura
这里我们先实例化了对象,然后才添加了sayName方法,而调用后是没有问题的,因为当我们调用sayName方法时,它会先从构造函数内部寻找,没有找到则继续往上在袁兴中查找,实例与原型的链接是一个指针,一个引用,这也说明我们随时可以用这种方式给原型添加新的属性和方法
但是,当我们实例化person_01对象后再用字面量的方式重写原型对象,就有问题了
function Person(name,age){ this.name=name; this.age=age; } var person_01=new Person("Sakura",22,"前端开发"); Person.prototype={ sayName:function(){ console.log(this.name); } } person_01.sayName(); //error
原本在原型上添加方法和属性时,实例对象person_01始终与原型保持连接关系(通过__proto__),所以无论何时给Person原型添加属性方法都能被实例对象寻找到
但是上面说到,当我们用字面量重写原型对象后,实际上是为原型赋值了一个新对象,而这个新的原型还没有和实例对象person_01建立连接,也就是说此时person_01的__proto__属性指向的是重写前的原型对象,而非重写后的新原型对象,调用时,从重写前的原型对象里并不能找到sayName方法,所以发生了错误
所以通过字面量重写原型有两个非常重要的问题需要注意
1.重写后的原型constructor指向Object,需要人为的修改constructor属性指向原本的构造函数
2.一定不能在重写原型之前实例化对象,这样会切断实例化对象与新原型的链接,导致无法调用新原型中的方法
待续
...