每个函数都有一个prototype属性,这个prototype就是通过构造函数创建的对象实例的原型对象。
使用原型的好处就是可以让所有对象实例共享它所包含的属性和方法。
不用在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
原型对象的性质
- 只要创建了新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。
- 默认所有原型对象都会有一个constructor(构造函数)属性,包含一个指向prototype属性所在函数的指针,也就是说一个构造函数Student,Student.prototype.constructor === Student
- 创建自定义构造函数后,其原型对象默认只会取得constructor属性,其他方法都是从Object继承来的,调用构造函数创建一个新实例后,这个实例内部包含一个指针,指向构造函数的原型对象,也就是
__proto__
对象的这个属性,重要明确一点这个连接时存在于实例与构造函数的原型对象之间的。
如何确定对象之间是否有这种关系:
//isPrototypeOf()
alert(Student.prototype.isPrototypeOf(student0));
alert(Objcet.getPrototypeOf(student0) == Student.prototype); //true,返回的对象就是这个对象的原型
alert(Objcet.getPrototypeOf(student0).name);//获取这个实例的某个属性
再谈一下实例属性与原型属性的概念
- 假设构造函数Student的原型对象中有个属性name,这个对象的值为”Mac Pro”
- 当我们new出来两个student0和student1实例的时候,这两个实例对象中的包含name属性,并且值都是”Mac Pro”
- 现在我通过
student1.name = "外星人";
这个操作就会把实例对象student1中与原型中同名的原型属性屏蔽住,进而替换为实例属性,注意:这种操作并不会影响原构造函数的原型对象,只是在这个实例对象中属性被改变了 - 这个实例对象中替换掉原型对象中的name属性可以通过delete关键字删除
delete student1.name;
在这之后原来被屏蔽的继于原型对象中的name属性就会重新出来,student1.name;//"Mac Pro"
- hasOwnProperty() 这个方法可以判断一个一个实例中的属性是属于实例属性还是原型属性
student1.hasOwnProperty("name")
in操作符
- 不管属性是原型属性还是实例属性,使用
"name" in student1
都会返回true hasPrototypeProperty(object, propertyName)
这个函数是这么定义的
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(propertyName) && (propertyName in object);
}Object.keys(obj)
这个方法可以获得对象上所有可枚举的实例属性,返回一个包含所有可枚举属性的字符串数组,这个方法遍历实例对象的时候只会返回这个对象的实例属性,不包含原型属性Object.getOwnPropertyNames()
这个方法可以获得所有的实例属性,不管是否能枚举,在传入一个原型对象的时候包括constructor属性都会返回- 可以通过这样简化给原型对象添加属性的方法
function Student(){};
Student.prototype = {
constructor: Student,//注意这里如果不写,Student的原型里面就没有constructor这个属性了
name: "Mac Pro",
sayHi: function(){
alert(this.name);
}
};
这么写还会导致constructor属性的Enumerable特性会被设置为true,默认情况下原生的constructor属性是不可枚举的 原型对象的缺点:在原型的实例对象中包含引用类型值的属性的情况下
未完待续……