原型对象
《js高程3》中对原型对象的解释
我们创建的每个函数都有一个
prototype
(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。也就是说,prototype
就是通过调用构造函数而创建的那个对象实例的原型对象。
使用原型对象的好处就是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
这段话个人看了不下5次,第一次是完全懵的,第二次还是懵的,再后来结合了一些实例和分析,才慢慢理解这段话的意思,接下来通过实例来捋一下上面那段话。
function Person(){
}
Person.prototype.name = "张三"
Person.prototype.age = 30
Person.prototype.job = "boss"
Person.prototype.sayName = function(){
alert(this.name)
}
var person1 = new Person();
person1.sayName() //"张三"
var person2 = new Person()
person2.sayName() //"张三"
1.无论什么时候,只要创建了新函数,就会为该函数创建一个prototype
属性
function Person(){
}
2.prototype
属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor
(构造函数)属性,这个属性是一个指向prototype
属性所在函数的指针。创建了自定义的构造函数之后,其原型对象默认只会取得constructor
属性;至于其他方法,则都是从Object
继承而来的。
3.为Person的原型对象添加属性和方法
Person.prototype.name = "张三"
Person.prototype.age = 30
Person.prototype.job = "boss"
Person.prototype.sayName = function(){
alert(this.name)
}
4.在调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。这个指针叫[[Prototype]]
。
var person1 = new Person();
person1.sayName() //"张三"
var person2 = new Person()
person2.sayName() //"张三"
在Firefox、Safari和Chrome中,每个对象都支持一个属性__proto__
,可用来访问[[Prototype]]
。
原型链
原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法
function Father() {
this.fName = "father"
this.job = "boss"
}
Father.prototype.getFName = function () {
return this.fName;
}
function Son() {
this.sName = "son"
}
Son.prototype = new Father(); //实现继承。
Son.prototype.getSName = function () { //在儿子的原型对象上添加getSName函数
return this.sName
}
var s = new Son()
console.log(s.getFName()) //father
上面例子定义了Father和Son函数,并且都拥有各自的方法和属性,然后Son继承了Father,并且输出Father的方法。
接下来一步步来分解上面的例子
- 创建一个Father函数,在它的原型对象上添加函数(fName和job是Father实例中的属性,不是Father的属性)
function Father() {
this.fName = "father"
this.job = "boss"
}
Father.prototype.getFName = function () {
return this.fName;
}
2. 创建一个Son函数
function Son() {
this.sName = "son"
}
3. 创建Father实例,并将该实例赋给Son.prototype
function Son() {
this.sName = "son"
}
Son.prototype = new Father(); //实现继承。
这时Son.prototype指针会指向新的原型对象,切断与原对象的联系
- 为Son的原型对象添加方法
Son.prototype.getSName = function () { //在儿子的原型对象上添加getSName函数
return this.sName
}
5.创建一个Son的实例,然后实现Father的方法
var s = new Son()
console.log(s.getFName()) //"father"
为什么 s.getFName()会输出"father"?
当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。
所以上面的搜索步骤是这样的:1)搜索s实例。2)搜索s.prototype。3)搜索s.prototype.prototype,即Father.prototype。
到了这里,原型链还缺少一环,那就是Object
。因为所有引用类型默认都继承了Object
,而这个继承就是通过原型链实现的。记住,所有函数的默认原型都是Object
实例,因此默认原型都会包含一个内部指针,指向Object.prototype
。现在知道为啥自定义类型都有toString()
、valueOf()
等默认方法吧?
全部代码+原型链图
function Father() {
this.fName = "father"
this.job = "boss"
}
Father.prototype.getFName = function () {
return this.fName;
}
function Son() {
this.sName = "son"
}
Son.prototype = new Father(); //实现继承。
Son.prototype.getSName = function () { //在儿子的原型对象上添加getSName函数
return this.sName
}
var s = new Son()
console.log(s.getFName()) //father
到这里,重新去看本章开头那段话,希望能有进一步的理解。
以上内容参考自《JavaScript高级程序设计(第3版)》,再加上一些个人的理解,小白学习阶段,如有错误,欢迎指出。