js系列—继承

继承父类:

            // 父类
			function Person(name) {
				this.name = name
				this.show = function() {
					console.log('名字', this.name)
				}
			}
			Person.prototype.age = 20
			var p = new Person('aron')

一、原型链继承

            // 原型链继承
			function Son(name) {
				this.name = name
			}
			// 子级(构造函数)元素的原型与父级(构造函数)原型相对
			Son.prototype = new Person() // 重点代码
			var s1 = new Son('张三')
			console.log(s1 instanceof Person) // true
			var s2 = new Son('王五')
			console.log('子级', s1.age) // 20
			console.log('子级', s1.show) // 父级的show函数
			// 当Son.prototype = new Person('李四'),new Son()不传参时,打印undefined,
			// 因为自己本身有name属性,但没赋值,继承时本身有该属性不会去父级原型中找,只有本身没有该属性才会去父级原型中找
			// 实际的寻找过程是:s1(son的实例,相当于son的儿子)——————Son构造函数——————Person(父级)
			console.log('子级', s1.name) // 张三
			// 从下面测试可知,继承是指继承了父级的属性,当子级的该属性没有属性值时,会继承父级的属性和值,当子级有自己的值时,不会继承父级的值,
			// 父级的实例修改自己的值不会影响子级的值,只有父级修改自己原型的值才会对子级有影响,当子级自己没有值时,会去父级的原型取最初始的值
			p.age = 40
			s2.age = 15
			console.log('p', p.age) // 40
			console.log(s2.age) // 15

实现思路:让子级构造函数的原型等于父级构造函数原型,因为我们知道new Person的内部过程就是把Person的原型赋值给其他对象

特点:子级的实例可继承的有:子级构造函数的属性,父级构造函数的属性,父级原型内的属性,另外,子级的实例与父级的实例是没有关系的,虽然这两者可能继承了父级同样的属性,但是修改本身属性不会影响对方,只有父级修改原型的值才会对子级有影响(就好比说,虽然你和你兄弟都继承了你父亲的黑头发,但是你兄弟去染个黄头发,是对你父亲和对你都没有任何关系的,改变的只是他自己,只有你父亲的基因库(原型对象)的属性发生了变化,才会对子级有影响,因为子级的属性都是从他那继承而来的)

缺点:1.子级的实例无法向父级传参(因为自己的实例是用子级的构造函数生成的,只能向子级构造函数传参)

            2.会共享父级构造函数和原型里面的属性和方法,所以一修改父级构造函数和原型的值(Person.prototype.age =60,前提是子级的实例没有自己赋值,如:p.age = 40),子级都会受影响

上面说的有四个对象,分别是:父级构造函数、父级实例、子级构造函数、子级实例,他们之间的关系如下:

二、借用构造函数继承

            // 借用构造函数继承
			function Son2() {
                // 重点代码
				Person.call(this, 'james')
				this.head = 'big'
			}
			var ss3 = new Son2()
			console.log('ss3', ss3.age) // undefined
			console.log('ss3', ss3.show) // function show
			console.log('ss3', ss3.name) // james
			console.log('ss3', ss3 instanceof Person) // false

实现思路:拷贝父级的作用域,把构造函数的属性复制到子类

特点:只继承父级构造函数的属性,不继承父级原型的属性(和原型没有关系),解决了向父级传参的问题(可以向父级传参)

缺点:只继承了父级构造函数的属性,每个子级实例都有父级构造函数的副本,代码内部臃肿

三、组合式继承(构造函数+原型链)

还有一种常用方法是结合了原型链继承和构造函数继承,如下:

            // 组合式继承(构造函数+原型链)
			function Son3(name) {
				// 构造函数继承,继承父级构造函数的属性,解决传参问题
				Person.call(this, name)
			}
			// 原型链继承,继承父级构造函数和原型中的属性,解决复用问题
			Son3.prototype = new Person()
			var ss4 = new Son3('小明')
			console.log('——————————————')
			console.log('ss4', ss4.name) // 小明
			console.log('ss4', ss4.age) // 20
			console.log('ss4', ss4.show) // function show
			console.log('ss4', ss4 instanceof Person) // true

实现思路:既使用原型链继承也使用构造函数继承

特点:可以传参,复用,既可以继承原型属性,也可以继承构造函数中的属性

缺点:调用两次父类构造函数,耗内存

四、寄生组合式继承

上面三种是最基本的三种继承,其实还有很多基于以上三种的变种写法,如这种寄生式继承

            // 寄生组合式(寄生+构造函数:寄生是在原型链基础之上外部加了一个函数包裹,)
			// 寄生写法,box实际上是子级实例的另外一种写法,找一个宿主,写在宿主里面
			// 本质是原型链继承
			function box(obj) {
				function A() {}
				A.prototype = obj
				return new A()
			}
			// 创建子级实例(ss5实际上是P的实例,继承了传入对象obj (Person的原型)的属性)
			var ss5 = box(Person.prototype)
			// 构造函数方式继承Person的构造函数属性
			function Son4(name) {
				Person.call(this, name)
			}
			// 重点部分:Son4继承了ss5实例
			Son4.prototype = ss5
			ss5.constructor = Son4
			var ss6 = new Son4('LiLi')
			console.log('ss6', ss6.name) // LiLi(继承的构造函数属性)
			console.log('ss6', ss6.age) // 20(继承的原型上的属性)
			console.log('ss6', ss6 instanceof Person) // true

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值