3.原型模式(Prototype)
我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象。使用原型对象的好处是让所有实例共享它所包含的属性和方法。
function Person(){ } Person.prototype.name = "Bob"; Person.prototype.age = 18; Person.prototype.job = "Developer"; Person.prototype.sayName = function(){ alert(this.name); } var person1 = new Person(); var person2 = new Person();
3.1 理解原型对象
只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会获得一个constructor属性,这个属性指向prototype属性所在的函数。
当调用构造函数创建一个新实例后,该实例的内部包含一个指针(内部属性--[[Prototype]]),指向构造函数的原型对象。 虽然我们无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。如果[[Prototype]]指向调用isPrototypeOf()方法的对象,那么这个方法就返回true。
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //true
Object.getPrototypeOf()方法返回[[Prototype]]的值:
alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(Object.getPrototypeOf(person1).name); //Bob
每当代码读取某个对象的属性时,都会执行一次搜索。搜索首先从对象实例本身开始,如果在实例中找到了,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象。
我们可以通过对象实例访问保存在原型中的值,但不能通过对象实例改变原型中的值:
person1.name = "Peter"; person1.sayName(); //"Peter" 来自实例 person2.sayName(); //"Bob" 来自原型
使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。接上面的例子:
alert(person1.hasOwnProperty("name")); //true alert(person2.hasOwnProperty("name")); //false alert(person1.hasOwnProperty("age")); //false
3.2 原型与in操作符
in操作符会在 通过对象能访问属性时返回true, 无论该属性存在于实例中还是原型中。
alert("name" in person1); //true 来自实例 alert("name" in person2); //true 来自原型
同时使用hasOwnProperty()方法与in操作符,就可以确定该属性的位置。
function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object); } alert(hasPrototypePropoty(person1, "name")); //false
alert(hasPrototypePropoty(person2, "name")); //true
使用Object.keys()方法可以取得对象上所有可枚举的实例属性:
var keys = Object.keys(Person.prototype); alert(keys); //name, age, title, sayName var keys1 = Object.keys(person1); alert(keys1); //name
Object.getOwnPropertyNames() 可以或得所有实例属性(包括不可枚举的)
alert(Object.getOwnPropertyNames(Person.prototype)); //constructor,name,age,title,sayName
3.3 更简单的原型方法
function Person(){ } Person.prototype = { name: "Bob", age: 29, title: "Developer", sayName: function(){ alert(this.name); } };
在上面的例子中,我们将Person.prototype设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但constructor属性不再指向Person。上面的例子本质上重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数)。
3.4原型对象的问题
1. 原型模式省略了为构造函数传递参数,所以所有实例在默认情况下都取得相同的属性值。
2. 对于引用类型值:
function Person(){ } Person.prototype = { name: "Bob", age: 29, title: "Developer", friends:["Court", "Ann"], sayName: function(){ alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); person1.friends.push("Alice"); alert(person1.friends); // Court, Ann, Alice alert(person2.friends); // Court, Ann, Alice