读书笔记:JavaScript——继承

一、原型链
1、利用原型让一个引用类型继承另一个引用类型的属性和方法
每个实例都指向其构造函数的原型对象并继承其属性和方法,而一个原型也可能是另一个原型的实例

function SuperType () {
	this.property = true
}
SuperType.prototype.getSuperValue = function () {
	return this.property
}
function SubType () {
	this.subproperty = false
}
SubType.prototype = new SuperType()//重写SubType的原型
SubType.prototype.getSubValue = function () {
	return this.subproperty
}
const instance = new SubType()
console.log(instance.getSuperValue())//true

分析:instance是SubType函数的实例对象,指向SubType.prototype,继承了上面的属性和方法;SubType.prototype作为SuperType函数的实例对象,指向SuperType.prototype,继承了上面的属性和方法,因此instance也就继承了SuperType.prototype上的方法getSuperValue()

2、默认原型
所有对象的默认原型都是Object的实例,因此其指针__proto__都会指向Object.prototype,这也是原型链的顶端

console.log(Object.getPrototypeOf(SuperType.prototype) == Object.prototype)//true
console.log(Object.getPrototypeOf(SubType.prototype) == SuperType.prototype)//true
console.log(Object.getPrototypeOf(Object.prototype)) //null:Object.prototype-原型链的顶端,没有原型

3、给原型添加方法要在替换原型的语句之后

function SuperType () {
	this.property = true
}
SuperType.prototype.getSuperValue = function () {
	return this.property
}
function SubType () {
	this.subproperty = false
}
SubType.prototype = new SuperType()
// 创建一个新方法
SubType.prototype.getSubValue = function () {
	return this.subproperty
}
// 重写了原型对象上已有的方法,即屏蔽了原型上的方法
SubType.prototype.getSuperValue = function () {
	return false
}
const instance = new SubType()
console.log(instance.getSuperValue())//false:调用的是实例方法

4、不使用字面量创建原型方法,使用字面量方式创建原型方法,相当于重写原型对象,原本的原型链已被切断

function SuperType () {
	this.property = true
}
SuperType.prototype.getSuperValue = function () {
	return this.property
}
function SubType () {
	this.subproperty = false
}
SubType.prototype = new SuperType()
// 使用字面量方式创建原型方法
SubType.prototype = {
	getSubValue: function () {
		return this.subproperty
	}
}
const instance = new SubType()
console.log(instance.getSuperValue())//Uncaught TypeError: instance.getSuperValue is not a function

5、原型链的问题
1)原本的实例属性变为了另一个对象的原型属性,继承自此原型属性的实例就可以改变其中的方法\引用类型值

function SuperType () {
	this.colors = ['red', 'blue'] //引用类型
}
function SubType () {}
SubType.prototype = new SuperType()//原本SuperType对象的实例属性成为了SubType的原型属性,所有SubType的实例对象都可以改变并共享这些原型属性

const instance1 = new SubType()
const instance2 = new SubType()

instance1.colors.push('green')
console.log(instance1.colors)//["red", "blue", "green"]
console.log(instance2.colors)//["red", "blue", "green"]//instance2的colors属性也随之改变

2)无法向上层构造函数传递参数

二、借用构造函数(伪造对象/经典继承)
1、解决原型链的引用类型值属性共享的问题

function SuperType () {
	this.colors = ['red', 'blue']
}
function SubType () {
	SuperType.call(this)//调用了SuperType函数,传入自己的作用域
}
const instance1 = new SubType()
const instance2 = new SubType()
instance1.colors.push('green')
console.log(instance1.colors)//["red", "blue", "green"]
console.log(instance2.colors)//["red", "blue"]

分析:
1)原本SuperType()中this.colors中的this指向的是全局
2)SubType()调用SuperType()并通过call()传入自己的作用域,1)中的this也就指向SubType()
3)instance1、instance2作为SubType函数的实例,各自拥有SubType上的属性和方法,调用colors时,this指向的是实例自己,因此instance1修改colors属性,instance2不受影响

2、可传递参数

function SuperType (name) {
	this.name = name
}
function SubType () {
	SuperType.call(this, 'diana')//调用了SuperType函数,传入自己的作用域,并传入参数
	this.age = 28
}
const instance1 = new SubType()
const instance2 = new SubType()
console.log(instance1.name)//diana
console.log(instance1.age)//28

3、借用构造函数的问题
借用构造函数,也存在构造函数模式的问题——函数复用

三、组合继承/伪经典继承
组合使用原型链继承和借用构造函数,使用原型链实现对原型属性和方法的继承,同时通过借用构造函数实现实例属性的分离

function SuperType (name) {
	this.name = name
	this.colors = ['red', 'blue']
}
SuperType.prototype.sayName = function () {
	alert(this.name)
}
// 借用构造函数继承属性(不共享的)
function SubType (name, age) {
	SuperType.call(this, name)
	this.age = age
}
// 原型链继承公共方法
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType()//原本SubType.prototype的constructor指向SuperType.prototype,此处重写constructor属性指向
SubType.prototype.sayAge = function () {
	alert(this.age)
}
const instance1 = new SubType('diana', 29)
instance1.colors.push('green')
console.log(instance1.colors)//["red", "blue", "green"]
instance1.sayName()//diana
instance1.sayAge()//29

const instance2 = new SubType('js', 0)
console.log(instance2.colors)//["red", "blue"]
instance2.sayName()//js
instance2.sayAge()//0

四、原型式继承
1、基于已有对象创建新对象,无需创建自定义类型

function object (o) {//传入一个对象参数
	function F () {}//创建一个临时函数对象
	F.prototype = o//临时函数对象的原型指向o
	return new F()//返回这个对象的实例,也就是说返回的实例__proto__指向传入的参数对象,继承它的属性和方法
}

const Person = {
	name: 'diana',
	colors: ['red', 'blue']
}

const person1 = object(Person)
person1.name = 'a'
person1.colors.push('green')
console.log(person1.name)//a
console.log(person1.colors)//["red", "blue", "green"]

const person2 = object(Person)
person2.name = 'b'
person2.colors.push('black')
console.log(person2.name)//b
console.log(person2.colors)//["red", "blue", "green", "black"]
//但也存在原型链继承的问题
console.log(Person.name)//diana
console.log(Person.colors)//["red", "blue", "green", "black"]

2、ES5标准的Object.create()方法,接收2个参数:作为新对象的原型对象的对象,为新对象设置属性的对象
只传入第一个参数:
原型对象:

const Person = {
	name: 'diana',
	colors: ['red', 'blue']
}

只传入第一个参数:

const person1 = Object.create(Person)
person1.name = 'a'
person1.colors.push('green')
console.log(person1.name)//a
console.log(person1.colors)//["red", "blue", "green"]
console.log(Object.getOwnPropertyDescriptor(person1,'name').writable)//true
console.log(Object.getOwnPropertyDescriptor(Person,'name').writable)//true

const person2 = Object.create(Person)
person2.name = 'b'
person2.colors.push('black')
console.log(person2.name)//b
console.log(person2.colors)//["red", "blue", "green", "black"]
console.log(Person.name)//diana
console.log(Person.colors)//["red", "blue", "green", "black"]

再传入第二个参数:

const person3 = Object.create(Person, {
	name: {
		value: 'js'
	},
	age: {
		value: 28
	}
})
console.log(person3.name)//js
console.log(person3.age)//28
console.log(Person.name)//diana
console.log(Person.age)//undefined
console.log(Object.getOwnPropertyDescriptor(person3,'name').writable)//false:另外2个特性值也是false
console.log(Object.getOwnPropertyDescriptor(Person,'name').writable)//true:另外2个特性值也是true

注:使用字面量形式、Object.create()方式创建的对象,属性(如writable等)默认为true;当Object.create()传入第二个参数设置对象的属性时,默认为false

五、寄生式继承
创建一个用于封装继承过程的函数,最终返回对象

function object (o) {//传入一个对象参数
	function F () {}//创建一个临时函数对象
	F.prototype = o//临时函数对象的原型为o
	return new F()//返回这个对象的实例,也就是说返回的实例__proto__指向传入的参数对象,继承它的属性和方法
}
function createAnother (original) {//创建一个函数封装继承
	const clone = object (original)//clone继承自original
	clone.sayHi = function () {//clone创建自己的方法sayHi()
		alert('hi')
	}
	return clone
}

const person = {
	name: 'diana',
	colors: ['red', 'blue']
}

const anotherPerson = createAnother(person)
//anotherPerson继承person对象的属性和方法
console.log(anotherPerson.name)//diana
//此外还有一个sayHi方法
anotherPerson.sayHi()//hi

缺点:也存在构造函数模式的问题——函数复用

六、寄生组合式继承
1、常用的组合继承模式,会调用两次超类型函数,第二次调用会重写第一次时继承的超类型属性,解决这个问题的方法就是寄生组合式继承。
通过借用构造函数来继承属性,通过原型链的混成模式来继承方法
依旧使用原型式继承的函数:

function object (o) {//传入一个对象参数
	function F () {}//创建一个临时函数对象
	F.prototype = o//临时函数对象的原型为o
	return new F()//返回这个对象的实例,也就是说返回的实例的原型对象是传入的对象o
}

原型链继承封装到函数内,后面调用此函数;使函数第一个参数对象作为第二个参数对象的实例,原型指向第二个参数的原型:

function inheritPrototype (subType, superType) {
	const prototype = object (superType.prototype);//创建一个superType.prototype的实例
	prototype.constructor = subType;//副本的constructor默认是指向superType的,这里重写指向subType
	subType.prototype = prototype;//副本赋值给subType的原型
}

创建超类型和子类型函数对象:

function SuperType (name) {
	this.name = name;
	this.colors = ['red', 'blue'];
}
SuperType.prototype.sayName = function () {
	alert(this.name);
};
function SubType (name, age) {
	SuperType.call(this, name);
	this.age = age;
}

调用创建的继承函数:

inheritPrototype(SubType, SuperType);//子类型只在此调用了一次超类型构造函数
SubType.prototype.sayAge = function () {
	alert(this.age);
};
const instance1 = new SubType('diana', 28)
instance1.sayName()//diana
instance1.sayAge()//28

2、优点:

  • 子类型只调用了一次超类型构造函数,提高效率;
  • 避免了在subType.prototype上创建不必要的多余的属性;
  • 原型链能够保持不变;
  • 能够正常使用instanceof和isPrototypeOf()。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值