***构造函数继承
属性继承:利用call()方法改变超类型构造函数this指向
// 属性继承
function Father(U_name, age, sex) {
this.U_name = U_name;
this.age = age;
this.sex = sex
console.log(this); // this指向Son
}
function Son(U_name, age, sex) {
Father.call(this, U_name, age, sex) //改变Father this指向
}
let son = new Son('张三', 18, 'sex')
console.log(son);
这时候我们看到打印结果:通过Son构造函数实力出来的对象已经拿到了Father构造函数里的属性
但是这种方式也有一个缺点 就是继承不了超类型构造函数里边的方法;
function Father(U_name, age, sex) {
this.U_name = U_name;
this.age = age;
this.sex = sex
}
Father.prototype.sing = function() {
console.log('lalalala');
}
function Son(U_name, age, sex) {
Father.call(this, U_name, age, sex)
}
let son = new Son('张三', 18, 'sex')
console.log(son);
那如果要把超类型构造函数里边的方法也继承过来就可以用下边这种方法:
方法继承:利用原型对象prototype继承方法
function Father(U_name, age, sex) {
this.U_name = U_name;
this.age = age;
this.sex = sex
}
Father.prototype.sing = function() {
console.log('lalalala');
}
function Son(U_name, age, sex) {
Father.call(this, U_name, age, sex)
}
Son.prototype = new Father()
Son.prototype.constructor = Son
let son = new Son('张三', 18, '男')
son.sing()
console.log(son);
这个原理就是让子类构造函数的原型对象等于超类型构造函数实例出来的对象,通过子类型构造函数的原型对象下的对象原型可以找到超类型构造函数实例对象的对象原型,继而访问它里面的方法。
***类的继承
1.利用 extends 关键字继承
语法:class 子类 extendx 父类
// 父类
class Father {
constructor(age, sex) {
this.age = age
this.sex = sex
}
sing() {
console.log('lalalalala');
}
}
// 子类
class Son extends Father {}
let obj = new Son(12, '男')
obj.sing
console.log(obj);
2.利用super访问父类属性方法
super()相当于Parent.prototype.constructor.call(this) 将父类this指向子类
class Father {
constructor(age, sex) {
this.age = age
this.sex = sex
}
sing() {
console.log(this.age * this.sex);
}
}
class Son extends Father { // extends 子类可以调用父类的方法
constructor(age, sex, weight) {
super(age, sex) //访问超类型构造函数
this.weight = weight
}
run() {
console.log(this.age - this.sex);
}
}
let obj = new Son(22, 20, 150)
obj.sing() // 440
obj.run() // 22
!!! : 在这里要特别注意 super关键字一定要在this之前 因为子类没有自己的this对象,而是继承父亲的this对象。如果不调用super,子类就得不到this对象。
***原型链
prototype 原型对象
当我们通过构造函数实例多个对象时,构造函数都会开辟一个新的空间去存放这些方法,就会催在很严重的内存浪费,那这个时候就出现了一个叫做prototype原型对象的东西,专门开辟一个空间去存放这些共有属性和方法,因为他是一个对象,所以称它为对象原型。每一个构造函数都有prototype这个属性,那么通过构造函数实例出来的对象都会共享这个方法。
具体语法如下:
通过打印台可以看到 实例出来的对象拿到了这个方法,构造函数的prototype里也存在这个方法。
--proto--
对象都会有一个属性__proto__,指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为有__proto__原型的存在。那我们看下构造函数的prototype是不是指向实例对象的__proto__.
显然返回值是true 这也能说通为什么实例对象能拿到构造函数的prototype里的方法了。
constructor
当然每一个构造函数里的方法肯定不止一种 但是我们要一个一个去通过下边这种方式去写的话就太麻烦了
// 添加方法
Person.prototype.sing = function() {
console.log('lalalala');
}
Person.prototype.jump = function() {
console.log('jumpjumpjumpjump');
}
Person.prototype.sleep = function() {
console.log('huhuhuuhuhuhu');
}
那我们可以通过下边去给他添加多个方法
Person.prototype = {
sing: function() {
console.log('lalalalalal');
},
jump: function() {
console.log('jumpjumpjumpjump');
},
sleep: function() {
console.log('huhuhuhuhuhuhuhuhu');
}
}
但是又会出现一个问题 他的constructor 指向的不是他的构造函数了,因为构造函数的prototype是一个对象,我们上边的写法等于是给他重新赋值,那他之前的里边的东西就会被覆盖掉。
这时候我们就要让他的constructor 重新指向他的构造函数
Person.prototype = {
constructon: Person,
sing: function() {
console.log('lalalalalal');
},
jump: function() {
console.log('jumpjumpjumpjump');
},
sleep: function() {
console.log('huhuhuhuhuhuhuhuhu');
}
}
他就指回他的构造函数了。那么contructor的作用就是记录该对象是那个构造函数实例化出来的。
上边说到每个对象都有一个对象原型__proto__ 那构造函数的原型对象prototype肯定也有一个,那他就指向大对象Object的原型对象Object.prototype,那Object.prototype也是一个对象,他也有对象原型__proto__,那在往上找就是null 。这就是原型链。
call(),apply(),bind()的区别
call() 一般用于继承 他可以改变this指向 后边传入参数
function Father(age, sex) {
this.age = age
this.sex = sex
}
function Son(age, sex, weight) {
Father.call(this, age, sex) //将Father this指向 Son
this.weight = weight
}
var zap = new Son('zhangsan', 32, 120)
console.log(zap);
apply()一般用于数组,也可以改变this指向 第一个参数和call()一样 改变this指向,但是后边的参数是数组
bind() 和call()bind() 一样改变this指向,最大的区别就是不会调用函数。