1、理论基础
- 我们创建的每个函数都有一个prototype(原型)属性
- 这个属性是一个指针,指向原型对象
- 原型对象的作用:存放这个类型创建的所有实例共享的属性与方法即:原型对象可以让所有实例共享它的属性与方法
2、原型对象
1、创建
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。
即:每创建一个函数, 就会同时创建它的prototype对象, 这个对象也会自动获取constructor属性;
2、constructor属性
默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指针,指向prototype属性所在函数。
对原型对象自身来说, 只有一个constructor属性, 而其他属性可以由我们添加或者从Object中继承.
3、创建实例
当调用构造函数创建一个新实例后,该实例的内部将包含一个内部属性proto,该属性的指针, 指向构造函数的原型对象。即:每个实例中, 会有一个属性, 该属性是指向原型对象的.
写到此我们会发现构造函数和实例通过prototype指针指向原型对象,原型对象通过constructor属性指针指向构造函数,可以理解它们互为映射,只有它们互相关联才能保证可以为原型对象添加自定义属性,同时实例对象能够共享原型对象的属性与方法。
3、对象搜索属性的过程
- 首先从实例对象本身查找,如果实例对象包含该属性,则返回此属性
- 如果实例对象本身不包含此属性,则从原型对象中查找,
- 可以通过hasOwnProperty方法判断属性属于对象本身还是原型
4、原型对象的缺陷
- 它不再需要为构造函数传递参数, 所以在默认情况下所有实例将有相同的属性值
- 原型中所有的属性是被很多实例共享的, 这种共享对于函数来说非常适合, 对于基本属性通常情况下也不会有问题. (因为通过person.name直接修改时, 会在实例上重新创建该属性名, 不会在原型上修改. 除非使用person.proto.name修改).
- 但是, 对于引用类型的实例, 就必然会存在问题.
// 定义Person构造函数
function Person() {}
// 设置Person原型
Person.prototype = {
constructor: Person,
name: "Tom",
hobby: ["Basketball", "Football"],
sayHello: function () {
console.log("Hello JavaScript")
}
}
// 创建两个person对象
var person1 = new Person()
var person2 = new Person()
console.log(person1.hobby)
<!--Basketball,Football-->
console.log(person2.hobby)
<!--Basketball,Football-->
person1.hobby.push("tennis")
console.log(person1.hobby)
// Basketball,Football,tennis
console.log(person2.hobby)
// Basketball,Football,tennis
为了解决上面的问题可以采用构造函数模式与原型模式的组合:共享属性与方法都写在原型上,实例对象的私有属性都写在构造函数中
function Person(name, age, height) {
this.name = name
this.age = age
this.height = height
this.hobby = ["Basketball", "Football"]
}
Person.prototype = {
constructor: Person,
sayHello: function () {
console.log("Hello JavaScript")
}
}
var person1 = new Person("Coderwhy", 18, 1.88)
var person2 = new Person("Kobe", 30, 1.98)
console.log(person1.sayHello == person2.sayHello) // true
person1.hobby.push("tennis")
console.log(person1.hobby)
// [ 'Basketball', 'Football', 'tennis' ]
console.log(person2.hobby)
// [ 'Basketball', 'Football' ]
网友评论