前端面试题:继承的实现

这个是让我崩溃的那个面试题,不是说这个题有多难,而是说自己粗心吧,只是简单的看了实现继承的方式有原型继承,构造函数继承,组合继承,class继承,但是怎么实现的都不知道。昨天面试完总结了一下,索性就把它写成文章,分享加自我回顾。

1、原型链继承

首先说的继承就是原型链的继承,因为在问我继承前先问了原型和原型链,原型链的集成有什么特点呢,就是子类的prototype指向父类的一个实例。这样的话子类没有的属性方法会在他的原型中去找到,就是说子类的实例的__proto__会指向父类的实例,这个时候就可以拿到父类的属性和方法,代码更好理解:

function father(phone,sex){
    this.phone = phone;
	this.sex = sex;
	this.say = function(){
	    console.log("这是你爹的方法");
    }
}
function son(name,age){
	this.name = name;
	this.age = age;
	this.eat = function(){
		console.log("吃");
	}
}
son.prototype = new father("110","女");
var tomCat = new son("tomcat",15);
console.log(tomCat);

每种继承方式都有它自己的优缺点,首先来说原型链继承的优点,其实他最大的优点就是实现的方式简单易用,只需要把子类的__proto__指向父类的一个实例即可,缺点呢也是很明显的,首先呢是无法实现多继承,其次主要因为来自原型对象的所有属性被所有实例共享,而且创建子类实例时,无法向父类构造函数传参,要想为子类新增属性和方法,必须要在Student.prototype = new Person() 之后执行,不能放到构造器中。

原型链继承结果

2、构造函数继承

构造函数的继承比较依赖call和apply,它的实现方式是在子类内部调用父类方法,并通过call改变它的作用域,指向子类的this,从而把父类的方法拿过来。

function father2(phone,sex){
	this.phone = phone;
	this.sex = sex;
	this.say = function(){
		console.log("这是你爹的方法");
	}
}
function son2(name,age){
	father2.call(this,"120","男")
	this.name = name;
	this.age = age;
	this.eat = function(){
		console.log("吃");
	}
}
var jerui = new son2("tomcat",15);
console.log(jerui);

构造函数的继承解决了原型链继承中子类实例共享父类引用属性的问题,并且创建子类实例时,可以向父类传递参数,而且可以实现多继承,就是多call几个爹,但是也有它的缺点,就是它的实力不是父类的实例,而且只能继承父类的属性方法,父类的原型上的无法继承,无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。

 3、原型链+构造函数的组合继承

特点呢就是通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用,就是把上边两种方式的优点结合到一块实现的。

function Person(name, age) {
    this.name = name,
    this.age = age,
    this.setAge = function () { }
}
Person.prototype.setAge = function () {
    console.log("111")
}
function Student(name, age, price) {
    Person.call(this,name,age)
    this.price = price
    this.setScore = function () { }
}
Student.prototype = new Person()
Student.prototype.constructor = Student//组合继承也是需要修复构造函数指向的
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
var s2 = new Student('Jack', 22, 14000)
console.log(s1)
console.log(s1.constructor) //Student
console.log(p1.constructor)

相当于是把上边两种方式的优点集于一身,缺点的话就是调用了两次父类构造函数,生成了两份实例。

4、组合继承优化

通过父类原型和子类原型指向同一对象,子类可以继承到父类的公有方法当做自己的公有方法,而且不会初始化两次实例方法/属性,避免的组合继承的缺点。

function Person(name, age) {
    this.name = name,
        this.age = age,
        this.setAge = function () { }
}
Person.prototype.setAge = function () {
    console.log("111")
}
function Student(name, age, price) {
    Person.call(this, name, age)
    this.price = price
    this.setScore = function () { }
}
Student.prototype = Person.prototype
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
console.log(s1)

5、ES6中的class继承

ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。

class father3{
	constructor(phone,sex){
		this.phone = phone;
		this.sex = sex;
	}
	showName(){
		console.log("这是你爹的方法");
		console.log(this.phone,this.sex);
	}
}
let houdie = new father3("爸爸","40");
console.log(houdie);
class son3 extends father3{
	constructor(name,age,salary){
		super(name,age);
		this.salary = salary;
	}
	showName(){
		console.log("这是孩子自己的方法");
		console.log(this.name,this.age,this.salary);
	}
}
let baby = new son3("baby","2","helloWorld");
console.log([baby]);

class继承语法简单易懂,操作更方便,但是并不是所有的浏览器都支持class关键字。

上边两种组合继承不是我自己敲的,因为原型链继承和构造函数继承看懂了,上边两个这个就懂了,今天就这样了,继续努力。

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ღ故里᭄ꦿ࿐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值