四、组合使用构造函数和原型模式
创建自定义类型的最常见方式,是组合使用构造函数模式与原型模式。构造函数用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享这对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是几两种模式之长。下面的代码重写了前面的例子:
在这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性constructor和方法sayName()则是在原型中定义的。而修改了p1.friends,并不会影响p2.friends,因为它们分别引用了不同的数组。
这种构造函数和原型混成的模式,是目前在ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式。
2014-6-16
至此,可以对构造函数,原型,对象三者之间的关系再做一个完整的总结:
一、对于一个构造函数,必须包含:
1,属性prototype,指向原型对象的指针
2,属性[[prototype]](也叫__prototype__)指向Function.prototype。
而Function.prototype的[[prototype]]也指向Object.prototype。(2014-07-01补充)
二、自定义构造函数后,其prototype对象,必须包含
1,属性constructor,这是谁的原型,是指向prototype属性所在的函数的指针
2,继承自Object的属性和方法
原型里还有一个隐藏的[[prototype]]属性指针,指向Object的原型对象(Object.prototype)。而Object.prototype的[[prototype]]则是null,因为它已经是原型链的最顶层。
3,自定义的属性和方法
三、对于一个对象,必须包含:
1,属性[[prototype]],指向原型对象的指针
换句话说,对象与构造函数没有直接的关系
对象也即实例,他拥有实例属性、方法,也拥有原型属性、方法。
对象的实例属性、方法是自己的。与他人不共享的。
对象的原型属性、方法是共享的。与他人共用一份,原因在对象仅仅有一个属性[[prototype]],指向原型对象(里面有属性、方法)
hasOwnProperty("name");//只有实例属性中存在才返回TRUE
alert("name" in p1); //不管name是实例属性,还是原型属性,都返回TRUE
三者之间的关系(包括与Function、Object的关系)
批注:没涉及到继承
关于重写对象原型的问题:
结论:重写原型对象切断了现有原型与任何之前已存在的对象实例之间的联系,它们引用的仍然是最初的原型
创建自定义类型的最常见方式,是组合使用构造函数模式与原型模式。构造函数用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享这对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是几两种模式之长。下面的代码重写了前面的例子:
- function Person(name, age, job){
- this.name = name;
- this.age = age;
- this.job = job;
- this.friends = ["Shelby","Court"];
- }
- Person.prototype = {
- constructor:Person,
- sayName:function(){
- alert(this.name);
- }
- }
- var p1 = new Person("Nicholas", 29, "Software Engineer");
- var p2 = new Person("Greg", 29, "Doctor");
- p1.friends.push("Van");
- alert(p1.friends); //"Nicholas,Greg,Van"
- alert(p2.friends); //"Nicholas,Greg"
- alert(p1.friends == p2.friends); // false;
- alert(p1.sayName == p2.sayName); // true
在这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性constructor和方法sayName()则是在原型中定义的。而修改了p1.friends,并不会影响p2.friends,因为它们分别引用了不同的数组。
这种构造函数和原型混成的模式,是目前在ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式。
2014-6-16
至此,可以对构造函数,原型,对象三者之间的关系再做一个完整的总结:
一、对于一个构造函数,必须包含:
1,属性prototype,指向原型对象的指针
2,属性[[prototype]](也叫__prototype__)指向Function.prototype。
而Function.prototype的[[prototype]]也指向Object.prototype。(2014-07-01补充)
二、自定义构造函数后,其prototype对象,必须包含
1,属性constructor,这是谁的原型,是指向prototype属性所在的函数的指针
2,继承自Object的属性和方法
原型里还有一个隐藏的[[prototype]]属性指针,指向Object的原型对象(Object.prototype)。而Object.prototype的[[prototype]]则是null,因为它已经是原型链的最顶层。
3,自定义的属性和方法
三、对于一个对象,必须包含:
1,属性[[prototype]],指向原型对象的指针
换句话说,对象与构造函数没有直接的关系
对象也即实例,他拥有实例属性、方法,也拥有原型属性、方法。
对象的实例属性、方法是自己的。与他人不共享的。
对象的原型属性、方法是共享的。与他人共用一份,原因在对象仅仅有一个属性[[prototype]],指向原型对象(里面有属性、方法)
hasOwnProperty("name");//只有实例属性中存在才返回TRUE
alert("name" in p1); //不管name是实例属性,还是原型属性,都返回TRUE
三者之间的关系(包括与Function、Object的关系)
批注:没涉及到继承
- function Foo(y){
- this.y = y;
- }
- Foo.prototype.x = 10;
- Foo.prototype.calculate = function(z){
- return this.x + this.y + z;
- }
- var b = new Foo(20);
- var c = new Foo(30);
- b.calculate(30);//60
- c.calculate(40);//80
关于重写对象原型的问题:
- function Person(){}
- var friend = new Person();
- Person.prototype = {
- name:"Nicholas",
- age:29,
- job:"Software Engineer",
- sayName:function(){
- alert(this.name);
- }
- };
- friend.sayName();//error
结论:重写原型对象切断了现有原型与任何之前已存在的对象实例之间的联系,它们引用的仍然是最初的原型