做前端的同学应该对红宝书高级程序设计很熟悉,堪称前端人员必读经典,也是我入行买的第一本书。现在总结一下关于对象 原型 原型链 继承 的一些内容
1. 创建对象
-
1) 工厂模式
function creat(name,age){
var obj = new Object()
obj.name = name
obj.age = age
obj.getAge = function(){
console.log(this.age)
}
return obj
}
var studentA = creat('xiaohong',18)
studentA.getAge() // 18
console.log(studentA.name) // xiaohong
复制代码
每次调用 creat函数都会返回一个对象 这个对象包含 name age 属性 和一个 getAge方法
缺点:没解决对象识别问题
-
2) 构造函数模式
function Creat(name,age){
this.name = name
this.age = age
this.getAge = function(){
console.log(this.age)
}
}
var studentA = new Creat('xiaohong',18)
studentA.getAge() // 18
console.log(studentA.name) // xiaohong
复制代码
构造函数的首字母为大写 要创建构造函数的实例,必须使用关键字 new 操作符
new操作符创建实例的过程是面试常考题!!!可以重点看下
a.创建一个新对象
b.将构造函数的作用域赋值给新对象(修改this指向)
c.执行构造函数(为新对象添加对象和方法)
d.返回新对象
studentA是Creat的一个实例,
这个实例中包含一个 constructor属性 , constructor属性指向Person.
这个实例中包含一个 constructor属性 , constructor属性指向Person.
这个实例中包含一个 constructor属性 , constructor属性指向Person.
console.log(studentA.constructor == Creat) //true
复制代码
instanceof 操作符可以 检测对象类型 studentA 是 Creat的实例 也是Object的实例
console.log(studentA instanceof Creat) //true
console.log(studentA instanceof Object) //true
复制代码
构造函数的问题 每个方法都要在每个实例上创建一遍。解决方案
function Creat(name,age){
this.name = name
this.age = age
this.getAge = getAge
}
function getAge (){
console.log(this.age)
}
复制代码
每个实例都共享了全局对象中的方法 ,但是如果定义多个方法就要定义很多全局函数
3) 原型模式
function Person(){}
Person.prototype.age = '18'
Person.prototype.name = 'xiaohong'
Person.prototype.getAge = function(){
console.log(this.age)
}
var studentA = new Person()
studentA.getAge() //18
复制代码
无论什么时候创建一个函数 该函数的 prototype属性 指向函数的原型对象。所有的原型对象都会获得一个constructor属性 这个属性包含一个指向 prototype属性所在函数的指针
如果实例中新添加的属性和原型上的属性重名,那么原型上的属性就会被屏蔽(只屏蔽不修改)
function Person(){}
Person.prototype.age = '18'
Person.prototype.name = 'xiaohong'
Person.prototype.getAge = function(){
console.log(this.age)
}
var studentA = new Person()
studentA.name='lucy'
console.log(studentA.hasOwnProperty('name')) //true
console.log('name' in studentA) //true
console.log('job' in studentA) //false
复制代码
hasOwnProperty()方法 可以判断属性是来自实例 还是来自原型
in 操作符 只要能通过对象能够访问到的属性都返回 true
所以通过 hasOwnProperty() 和 in 就可以确定该属性是来自 原型还是实例
缺点:所有实例在默认情况下都有相同的属性和方法 如果实例修改了原型对象的属性值 其他实例也被修改
4)组合使用构造函数和原型模式
创建自定义类型最常见的方法就是 使用构造函数创建属性 , 原型模式定义方法和共享的属性
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype={
construtor:Person,
getAge:function(){
console.log(this.age)
}
}
var studentA = new Person()
studentA.name='lucy'
console.log(studentA.name) //lucy
复制代码
5) 寄生构造函数
这个模式其实和工厂模式一样。但是不能用instanceof 来定义类型 所以一般不用这种模式。
function Person(name,age){
var o = new Object()
o.name = name;
o.age = age;
o.sayAge = function(){
console.log(this.age)
}
return o
}
var studentA = new Person('lucy','18')
studentA.sayAge()//18
复制代码
2.原型链
-
原型链-是实现继承的主要方法
利用原型对象让一个引用类型继承另外一个引用类型的属性和方法
function Father(){
this.property=true
}
Father.prototype.sayFatherName = function(){
return this.property
}
function Child(){
this.subproperty=false
}
Child.prototype = new Father()
Child.prototype.sayChildName = function(){
return this.subproperty
}
var child1 = new Child()
console.log(child1.sayFatherName()) //true
复制代码
注意: 子类型添加方法要放在替换类型之后
给子类添加方法 不可以使用字面量!!!会导致继承失效!!!
原型练的继承是通过创建Father的实例
instanceof 可以确定原型和实例的关系
console.log(child1 instanceof Child ) //true
console.log(child1 instanceof Father ) //true
console.log(child1 instanceof Object ) //true
复制代码
-
借用构造函数继承 借用 call apply
function Father(){
this.color=['red','green']
}
function Child(){
Father.call(this)
}
var child1 = new Child()
var child2 = new Child()
child1.color.push('blue')
console.log('child1:',child1.color) //[ 'red', 'green', 'blue' ]
console.log('child2:',child2.color) //[ 'red', 'green' ]
复制代码
缺点:方法都是在构造函数中定义的 无法复用
-
组合继承
function father(name,age){
this.name = name
}
Father.prototype.sayName = function(){
console.log(this.name)
}
function Child(name,age){
Father.call(this,name) //第二次调用 Father
this.age = age
}
Child.prototype = new Father() //第一次调用 Father
Child.prototype.constructor = Child
Child.prototype.sayAge = function(){
console.log(this.age)
}
var child1 = new Child('Lucy','18')
child1.sayName()//Lucy
child1.sayAge()//18
复制代码
组合继承避免了原型链和构造函数继承的缺点 是最常用的继承模式
-
寄生组合式继承
function inheritPrototype(subtype,supertype){
var prototype = Object(supertype.prototype) //创建对象
prototype.constructor = subtype //增强对象
subtype.prototype = prototype //指定对象
}
function Father(name,age){
this.name = name
}
Father.prototype.sayName = function(){
console.log(this.name)
}
function Child(name,age){
Father.call(this,name)
this.age = age
}
inheritPrototype(Child,Father)
Child.prototype.sayAge = function(){
console.log(this.age)
}
var child1 = new Child('Lucy','18')
child1.sayName()//Lucy
child1.sayAge()//18
复制代码
总结
JavaScript中主要通过 原型链实现继承。
原型链的构建是通过将一个类型实例 赋值给另外一个构造函数的原型 而实现的。
使用最多的继承方式是组合继承,通过构造函数继承实例实例属性 使用原型链继承共享的属性和方法。