关于JavaScript原型对象

本篇介绍了关于JavaScript原型对象的细节知识

原型对象的基本概念

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。

在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性是一个指向prototype属性所在函数的指针。

每个对象上都支持一个属性 proto ,这个属性连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。

如下图:
构造函数、原型属性以及实例之间的关系

实例person1和person2的内部属性指向了Person.prototype,它们与构造函数没有直接的关系。

使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中:访问的是实例属性就返回true,原型属性返回false。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(){
}
Person.prototype.name = "john";
Person.prototype.age = 29;
Person.prototype.job = "software engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
alert(person1.hasOwnProperty("name")); //false
person1.name = "greg";
alert(person1.name);
person1.hasOwnProperty("name"); //true

获得属性的方法

  • Object.keys()
    取得的是可枚举的实例属性,接收一个对象为参数,返回一个包含所有可枚举属性的字符串。(不包含原型对象上的属性)
  • Object.getOwnPropertyNames()
    得到所有的实例属性,无论它是否可枚举。

更简单的原型语法

使用一个包含所有属性和方法的对象字面量来重写整个原型对象,可以减少不必要的输入。

1
2
3
4
5
6
7
8
9
10
11
function Person(){
}
Person.prototype = {
name : "John";
age:29;
job:"software engineer";
sayName:function(){
alert(this.name);
}
}

这种方法有一个例外:constructor属性不再指向Person了。指向Object构造函数。


所以这种情况下这样做会报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(){
}
var friend = new Person();
Person.prototype = {
name : "John";
age:29;
job:"software engineer";
sayName:function(){
alert(this.name);
}
}
friend.sayName(); //error

解决方案是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(){
}
Person.prototype = {
constructor:Person, //添加constructor属性,设置为Person
name : "John",
age:29,
job:"software engineer",
sayName:function(){
alert(this.name);
}
}
var friend = new Person();
friend.sayName(); //John

原型对象的问题

  • 省略了构造函数传递初始化参数这一环节,结果所有的实例在默认情况下都将取得相同的属性值。
  • 原型模式的最大问题是由其共享的本质所导致的。
    原型中所有属性是被许多实例共享的,所以对于包含引用类型值的属性来说,问题就比较突出了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function Person(){
    }
    Person.prototype = {
    name : "John";
    age:29;
    friends:["shelby","jenny"]
    job:"software engineer";
    sayName:function(){
    alert(this.name);
    }
    }
    var person1 = new Person();
    var person2 = new Person();
    person1.friends.push("van");
    alert(person1.friends); //"shelby","jenny","van"
    alert(person2.friends); //"shelby","jenny","van"

这也是我们为什么不去单独使用原型模式的原因。