之前写的有关对象的文章——对象
原型和原型链
原型
JavaScript 常被描述为一种
基于原型的语言(prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。
如果无法在对象本身找到需要的属性,就会继续访问对象的prototype值
原型与实例
- 每个构造函数都有一个
prototype属性,这个属性指向一个原型对象。 - 每个原型对象都包含一个指向构造函数的指针
constructor。 - 实例都包含一个指向原型对象的指针
__proto__。 - 对象的[[prototype]]属性指向它的原型对象,可以通过
obj._proto_或者Object.getPrototypeOf(obj)访问
对象体系结构图
Function.prototype === 被创建函数.__proto__;函数.prototype === 被创建对象.__proto__;Object.prototype === 任意函数.prototype.__proto__;
function People(){}
var p = new People();
p.__proto__ === People.prototype; //true
People.__proto__ === Function.prototype; // true People是Function创造的,任何函数都是Function创造的
People.prototype._proto_ === Object.prototype;// true 基本的对象都是Object创建的
// 鸡生蛋蛋生鸡
Function.prototype === Function.__proto__;//true function本身也是个函数。
Function.prototype === Object.__proto__;//true object是个函数
Function.prototype.__proto__ === Object.prototype;//true
Function.__proto__ === Object.prototype;//false
Function.__proto__.__proto__ === Object.prototype;//true
Function.prototype.__proto__ === Object.prototype;//true
确定原型与实例的关系
instanceof
检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
A instanceof B。如果函数B在对象A的原型链 (prototype chain) 中被发现,那么instanceof操作符将返回true,否则返回false.
Object instanceof Function;//true
Function.__proto__.__proto__ === Object.prototype;//true
Function instanceof Object;//true
Object.__proto__ === Function.prototype;//true
Function instanceof Function;//true
Function.__proto__ === Function.prototype;//true
Object instanceof Object;//false
isPrototypeOf()
检查一个对象是否存在于另一个对象的原型链上
Object.prototype.isPrototypeOf(Function);//true
继承
原型链继承
利用原型让一个
引用类型继承另一个引用类型的属性和方法
原型链作为实现继承的主要方法
function SuperType(){
this.prototype = true
}
SuperType.prototype.getSuperValue = function(){
return this.prototype
}
function SubType(){
this.Subprototype = false
}
SubType.prototype = new SuperType()
console.log(SubType.prototype instanceof SuperType) //true
//这是赋值,SubType.prototype是Super的实例
//subType继承了superType,通过创建SuperType实例,将该实例赋给SubType.prototype实现的
console.log(SubType.prototype)
//给原型添加新方法一定要在替换原型的语句之后
SubType.prototype.getSubValue = function(){
return this.Subprototype
}
//通过原型链实现继承时,不能使用对象字面量创建原型方法,会导致原型链重写
// SubType.prototype = {
// getSubValue : function(){
// return this.Subprototype
// }
// }
var instance = new SubType()
//instance.__proto__ == SubType.prototype
console.log(instance)
console.log(instance.__proto__ == SubType.prototype)
console.log(instance.getSuperValue())
//instance继承了SubType,SubType继承了SuperType,SuperType继承了Object
优点:
- 超类新增原型方法/属性,子类都能访问到
- 实例是子类的实例,也是父类的实例
缺点:
function SuperType(){
this.colors = ["red","blue","green"]
}
function SubType(){
}
SubType.prototype = new SuperType()
//SubType的所有实例都会共享colors属性
SubType.prototype.colors //["red", "blue", "green"]
var instance1 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors)//["red", "blue", "green", "black"]
var instance2 = new SubType()
console.log(instance2.colors)//["red", "blue", "green", "black"]
//引用类型值的原型属性修改
SubType.prototype.colors//["red", "blue", "green", "black"]
- 无法实现多继承
- 想要为子类添加原型方法,必选在创建超类实例赋给子类原型
SubType.prototype = new SuperType()之后 - 在通过原型链实现继承时,原型实际上会成为另一个类型的实例**(引用类型值的原型属性会被所有实例共享)**
- 在创建子类型的实例时,不能向超类型的构造函数中传递参数(没有办法在不影响所有对象实例的情况下,向超类传递参数)
借用构造函数实现继承
又叫伪造对象,或者经典继承。
在子类构造函数内部通过call()/apply()方法调用超类构造函数,解决了原型中包含引用类型值所带来的问题
function SuperType(){
this.colors = ["red","blue","green"]
}
function SubType(){
//继承了SuperType
SuperType.call(this)
}
SubType.prototype = new SuperType()
var instance1 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors)//["red", "blue", "green", "black"]
var instance2 = new SubType()
console.log(instance2.colors)//["red", "blue", "green"]
//多继承
优点:
- 在子类型构造函数中向超类型构造函数传递参数
- 可以实现多继承
function SuperType(name){
this.name = name
}
function SuperBType(sex){
this.sex = sex
}
function SubType(){
//继承了SuperType,传递了参数
SuperType.call(this,'Bob')
//通过这一步可以实现多继承
SuperBType.call(this,'male')
//添加在子类型中应该定义的属性
this.age = 18
}
var instance = new SubType()
console.log(instance.name)//"Bob"
console.log(instance.sex)//"Jane"
console.log(instance.age)//18
缺点:
- 方法都在构造函数中定义,无法实现函数复用(不同实例上同名函数不相等)
- 实例是子类的实例
function SuperType(name){
this.name = name
}
function SubType(){
SuperType.call(this,'Bob')
this.age = 18
}
var instance = new SubType()
console.log(instance)
console.log(instance instanceof SuperType)//false
console.log(instance instanceof SubType)//true
//由此可得instance是子类的实例,不是超类的实例
组合继承(常用)
伪经典继承,将原型链和借用构造函数技术组合到一块
- 原型链实现对原型属性和方法的继承
- 借用构造函数实现对实例的继承
function SuperType(name){
this.name = name
this.colors = ["red","blue","white"]
}
SuperType.prototype.sayName = function(){
console.log(this.name)
}
function SubType(name,age){
SuperType.call(this,name)
this.age = age
}
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function(){
console.log(this.age)
}
var instance1 = new SubType('Jane',18)
console.log(instance1)
instance1.colors.push("green")
console.log(instance1)
instance1.sayName()//"Jane"
instance1.sayAge()//18
var instance2 = new SubType('Bob',20)
console.log(instance2)
instance2.sayName()//"Bob"
instance2.sayAge()//20
优点:
- 可以继承实例属性/方法,也可以继承原型属性/方法
- 函数可复用
- 子类构造函数可向超类构造函数传参
- 可以实现多继承
缺点:
- 无论什么情况下,都会调用两次超类型构造函数
原型式继承
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
function object(o){
function F(){}
F.prototype = o
return new F()
}
var person = {
name: 'Bob',
colors : ["red","blue","white"]
}
//object对传入其中的对象进行了一次浅复制
var instance = object(person)
instance.name = "Jane"
instance.colors.push("green")
console.log(instance.colors)//["red", "blue", "white", "green"]
等同于Object.create()
var person = {
name: 'Bob',
colors : ["red","blue","white"]
}
var instance = Object.create(person)
instance.name = "Jane"
instance.colors.push("green")
console.log(instance.colors)//["red", "blue", "white", "green"]
优点:
- 多继承
- 不用兴师动众地创建构造函数 缺点:
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回对象
function createAnother(obj) {
var clone = Object(obj);
clone.sayHi = function() {
console.log('Hi');
}
return clone
}
var person = {
name: 'Bob',
colors : ["red","blue","white"]
}
var instance = createAnother(person)
instance.name = "Jane"
instance.colors.push("green")
console.log(instance)
console.log(instance.name)//"Jane"
console.log(instance.colors)//["red", "blue", "white", "green"]
instance.sayHi()//Hi
var instance1 = createAnother(person)
console.log(instance1.colors)//["red", "blue", "white", "green"]
优点:
- 多继承
缺点:
- 不能实现复用
- 实例之间会影响
寄生组合式继承(理想类型)
借用构造函数来继承属性,通过原型链的混成形式来继承方法
解决了组合继承的缺点,不用两次调用超类构造函数
function inheritPrototype(subType,superType){
var obj = Object(superType.prototype)
obj.constructor = subType
subType.prototype = obj
}
function SuperType(name){
this.name = name
this.colors = ["red","blue","green"]
}
SuperType.prototype.sayName = function(){
console.log(this.name)
}
function SubType(name,age){
SuperType.call(this,name)
this.age = age
}
inheritPrototype(SubType,SuperType)
SubType.prototype.sayAge = function(){
console.log(this.age)
}
var instance = new SubType('Bob',18)
console.log(instance)
instance.colors.push("white")
console.log(instance.colors)//["red", "blue", "green", "white"]
var instance1 = new SubType()
console.log(instance1.colors)//["red", "blue", "green"]