继承
原型链继承
function Person() {
this.name = 'zs',
this.friends = []
}
Person.prototype.eating = function () {
console.log(this.name + 'eating');
}
function Student() {
this.id = 111
}
Student.prototype = new Person()
// 等于
// var p = new Person()
// Student.prototype = p
Student.prototype.studying = function () {
console.log((this.name + 'studying'));
}
var s1 = new Student()
var s2 = new Student()
// 会找到原型上的friends添加
s1.friends.push = 'ls'
// 不会找到原型上面添加,自己添加
s1.age = 18
s2.name = 'ww'
console.log(s1);
console.log(s2);
console.log(s1.eating());
console.log(s2.eating());
console.log(s1.friends);
console.log(s2.friends);
原型链继承弊端:
- 打印对象,继承的属性看不到
- 可能会修改源数据的值
- 传入参数不好处理
借用构造函数继承
使用构造函数继承,需要使用 call() 函数
function Person(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
Person.prototype.eating = function () {
console.log(this.name + 'eating');
}
function Student(name, age, friends) {
// 借用Person构造函数中的赋值代码this.name=name等 为自己赋值
Person.call(this, name, age, friends)
this.id = 111
}
Student.prototype = new Person()
// 等于
// var p = new Person() 在这个步骤p对象(也就是s1的原型对象)会多出一些属性 name=undefined ...
// Student.prototype = p
var s1 = new Student('zs', 18, 'ls')
var s2 = new Student('ww', 20, 'll')
console.log(s1);
console.log(s2);
优点:
- 解决了子类构造函数向父类构造函数中传递参数
- 可以实现多继承(
call或者apply多个父类)缺点:
- 方法都在构造函数中定义,无法复用,
Person被调用了两次- 不能继承原型上的属性和方法,只能继承父类的实例属性和方法,
s1的原型对象会多出一些属性,但是这些属性没有必要存在
寄生式继承
var personObj = {
running: function () {
console.log('running');
}
}
function createStudent(name) {
var stu = Object.create(personObj)
stu.name = name
stuObj.studying = function () {
console.log('studying');
}
return stu
}
var stuObj = createStudent('zs')
var stuObj1 = createStudent('ls')
var stuObj2 = createStudent('ww')
原型式继承
我们想要实现的是构造函数的继承,但是我们先看下面的实现对象继承的例子,了解原型式继承的一些原理
// 把info的原型对象绑定到obj上
var obj = {
name: 'zs',
age: 18
}
// 方法1
function createObject1(o) {
var newObj = {}
Object.setPrototypeOf(newObj, o)
return newObj
}
// 方法2
function createObject2(o) {
function Fn() {
Fn.prototype = o
var newObj = new Fn()
// 相当于 newObj.__proto__ = Fn.prototype = o
return newObj
}
}
// var info = createObject1(obj)
// 方法3 目前ECMA给我们提供了Object.create()方法,实现的功能与上面的一样
var info = Object.create(obj)
console.log(info);
console.log(info.__proto__);
寄生组合式继承
完整的继承方案
function createObject(o) {
function Fn() { }
Fn.prototype = o
return new Fn()
}
function inheritPrototype(SubType, SuperType) {
// Object.create会返回一个对象,这个对象指向Person.prototype,相当于上面的createObject函数 所以也可以使用createObject函数来替代create
// SubType.prototype = Object.create(SuperType.prototype)
SubType.prototype = createObject(SuperType.prototype)
Object.defineProperty(SubType.prototype, 'constructor', {
enumerable: false,
configurable: true,
writable: true,
writable: true,
value: Student
})
}
function Person(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
Person.prototype.eating = function () {
console.log(this.name + '-eating');
}
function Student(name, age, friends, id, score) {
// 借用Person构造函数中的赋值代码this.name=name等 为自己赋值
Person.call(this, name, age, friends)
this.id = id
this.score = score
}
inheritPrototype(Student, Person)
Student.prototype.studying = function () {
console.log('studying');
}
var s1 = new Student('zs', 18, 'ls', 111, 100)
var s2 = new Student('ww', 20, 'll', 222, 90)
console.log(s1);
console.log(s2);
s1.eating()
s1.studying()
console.log(s1.constructor.name === s1.__proto__.constructor.name);
原型对象继承
如果直接将父构造函数的原型赋值给子构造函数原型,子构造原型发生改变会直接改变父构造函数原型,不建议使用
son.prototype = father.prototype
var son = new son('小明', 20)
son.show1()
son.show2()
建议使用下面的写法
son.prototype = new father()
son.prototype.constructor = son
console.log(son.prototype);
var son = new son('小明', 20)
son.show1()
son.show2()
注意:第一条语句只是给
son.prototype重新赋值,但是缺失的原型的constructor构造方法,所以需要第二条语句son.prototype.constructor = son,如下打印的原型(画方括的地方没有constructor)在new一个新的实例的时候,会把这个实例的对象打印出来
类继承
也可以继承静态方法
类继承的时候,子类必须先调用
super初始化父类实例
class Father {
// 创建构造函数 相当于 C++ 的构造函数 Student()
constructor(name, age) {
this.name = name
this.age = age
}
// 创建成员方法
sayHello(msg) {
console.log(this.name + msg);
}
handler() {
console.log('1');
console.log('2');
console.log('3');
}
}
class Son extends Father {
constructor(name, age, gender) {
super(name, age)
this.gender = gender
}
handler() {
// 调用父类的方法
super.handler()
console.log('4');
console.log('5');
console.log('6');
}
}
var s1 = new Son('张三', 20, '男')
s1.sayHello('你好')
console.log('性别: ' + s1.gender);
s1.handler()