继承
首先要知道在js中是不支持继承的,但是我们可以借助其他的方式来模拟这种过程。
在实现继承的时候,我们要遵循一个原则,属性写在构造函数内,方法写在原型上,为什么要这么做呢?
因为属性如果写在原型链上,每个new出来的对象都会使用这一个属性,每个属性都是一样的,如果不是刻意而为之,那么最好是写在构造函数内,这样每次new出来对象,每个对象的属性都相互不影响,就像vue中data为什么是一个函数一样。因为他需要返回一个新的对象。差不多可以这么理解。
那么方法为什么要写在原型上呢,方法干的事情,都是一样的,所以为了复用,我才考虑写在原型上,因为如果写在构造函数内,每次new都会生成一个新的方法,这样会造成内存的浪费,所以我们基本上约定俗成。
原型链继承
// 父类: 定义共用的属性和方法
function Person() {
this.name = "malong";
}
Person.prototype.sayName = function(){
console.log(this.name)
}
// 子类
function Student(sId) {
this.sId = sId;
}
// 原型链继承
Student.prototype = new Person();
// 我们无法初始化继承过来的name属性
var stu1 = new Student(1);
var stu2 = new Student(2);
// 原型链继承弊端1:
// 打印stu1,stu2对象,继承来的属性是看不到的,因为在prototype里面
console.log(stu1,stu2)
// 原型链继承弊端2:
// 直接修改对象上继承的属性, 是给本对象添加了一个新属性
stu1.name = "malong1"
stu2.name = "malong2"
// 修改继承过来的(prototype)属性, 对象之间会相互受到影响
stu1.prototype.name = '修改了'
console.log(stu2.prototype.name)
// 总结:我们发现使用原型链继承,无法继承属性,属性还是在原型链上,不能让自己的属性进行初始化
// 不能继承构造函数上的属性和方法,只能继承原型上的属性和方法
对象冒充继承
// 父类: 定义共用的属性和方法
function Person(name,age) {
this.name = name;
this.age = age;
}
// 这里没有被继承
Person.prototype.test = "test";
Person.prototype.sayName = function() {
console.log(this.name);
}
// 子类
function Student(name,age,sId) {
Person.apply(this,[name,age]);
this.sId = sId;
}
var stu = new Student("malong",18,001);
console.log(stu);
// 总结: 无法继承原型(prototype)上的属性和方法,只能继承构造函数内部的属性和方法
组合继承(对象冒充+原型链)
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.test = "test";
Person.prototype.sayName = function() {
console.log(this.name)
}
function Student(name,age,sId) {
// 对象冒充
Person.apply(this,[name,age]); //第一次调用
this.sId = sId;
}
// 原型链
Student.prototype = new Person(); //第二次调用
var stu = new Student("malong",18,001)
// 总结: 1.Person函数至少被调用了两次
// 2.stu的原型对象上会多出一些属性, 但是这些属性是没有存在的必要
寄生式继承-对象
// 寄生式继承:一定要是一个对象,不能是函数
var base = {
name: "base",
say: function () {
console.log(this.name)
}
}
function inherit(base, name, age) {
var newObj = {};
Object.setPrototypeOf(newObj, base)
newObj.name = name;
newObj.age = age;
newObj.test = function () {
console.log(this.name)
}
return newObj;
}
// 这种写法和上面的写法一样
function inherit1(base, name, age) {
var newObj = Object.create(base)
newObj.name = name;
newObj.age = age;
newObj.test = function () {
console.log(this.name)
}
return newObj;
}
var obj1 = inherit(base,"malong",18)
//总结: 这种方式的prototype 是Object 和工厂函数类似 我们在打印对象时,对象的类型都是Object类型
console.log(obj1)
寄生式组合继承
// 父类
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.getName = function () {
console.log(this.name)
}
// 子类
function Student(sId, name, age) {
Person.apply(this, [name, age]);
this.sId = sId;
}
function inheritPrototype(base, child) {
child.prototype = Object.create(base.prototype);
Object.defineProperty(child, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: base
});
}
inheritPrototype(Person, Student);
let stu = new Student(1, "malong", 18);
console.log(stu)
//总结:没有缺点
ES6的class继承
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
run() {
console.log("person run run run")
}
}
class Student extends Person {
constructor(name, age, sId) {
super(name, age);
this.sId = sId;
}
eat() {
console.log("student go go")
}
}
var stu = new Student("zhangsan", 18, 001)
console.log(stu)