概述:类的声明 生成实例 如何实现继承 继承的几种方式
声明类的方式有两种:
// 类的声明
function Animal () {
this.name = 'name';
}
//es6中 类的声明
class Animal2{
constructor (){//构造函数
this.name = name
}
}
//实例化-如果构造函数没有参数,实例后的括号可省去
console.log(new Animal(), new Animal2());
如何实现继承
继承方式一:借助构造函数实现继承
// 继承方式一:借助构造函数实现继承
function Parent1(){
this.name = 'parent1';
}
function Child1(){
Parent1.call(this);//apply 改变函数运行上下文。这里将父构造函数Parent1的this指向子构造函数Child1类的实例上,从而使父类执行时,属性都挂载到子构造函数Child1类的实例上
this.type = 'child1';
}
console.log(new Child1)//无参数时()可省略
缺点:这种方式实现的继承原理是改变Parent1构造函数运行时的this指向,但是Parent1 原型链上的内容并没有被 Child1 继承,因而并没有实现真正的继承,只是实现了构造函数上属性和方法的部分继承。在原代码基础上进行进一步验证:
function Parent1(){
this.name = 'parent1';
}
Parent1.prototype.say = function () {};
function Child1(){
Parent1.call(this);//apply 改变函数运行上下文。这里将父构造函数Parent1的this指向子构造函数Child1类的实例上,从而使父类执行时,属性都挂载到子构造函数Child1类的实例上
this.type = 'child1';
}
console.log(new Child1().say());//报错 Uncaught TypeError: (intermediate value).say is not a function
继承方式二:借助原型链实现继承
//继承方式二:借助原型链实现继承
function Parent2 () {
this.name = 'parent2'
}
function Child2 () {
this.type = 'child2'
}
//每个函数都有一个prototype属性(原型对象),这个属性的意义在于能使该构造函数的实例能访问构造函数的原型对象
Child2.prototype = new Parent2();
console.log(new Child2)
缺点:由于实例对象原型链上的原型对象共同,修改任意其中一个实例对象,会使其它实例对象也发生改变。例如:在第二种继承方式的基础上进行如下操作
//继承方式二:借助原型链实现继承
function Parent2 () {
this.name = 'parent2'
this.play = [1, 2, 3]
}
function Child2 () {
this.type = 'child2'
}
//每个函数都有一个prototype属性(原型对象),这个属性的意义在于能使该构造函数的实例能访问构造函数的原型对象
Child2.prototype = new Parent2();
console.log(new Child2)
var s1 = new Child2();
var s2 = new Child2();
s1.play.push(4)
console.log(s1.play, s2.play)//[1, 2, 3, 4] [1, 2, 3, 4]
s1.__proto__ === s2.__proto__ //true
继承方式三:组合方式
// 第三种继承方式:组合方式
function Parent3 (){
this.name = 'parent3'
this.play = [1, 2, 3]
}
function Child3 (){
Parent3.call(this);
this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play)
缺点:构造函数每实例化一次都要进行一次执行,而此方法中,构造函数进行了多次执行
//组合继承的优化1
function Parent4 (){
this.name = 'parent4'
this.play = [1, 2, 3]
}
function Child4 (){
Parent4.call(this);
this.type = 'child4';
}
Child4.prototype = Parent4.prototype;//引用同一个对象,constructor 相等,无法区分实例是由子类还是父类创建的
var s5 = new Child4();
var s6 = new Child4();
s5.play.push(4);
console.log(s5, s6)
console.log(s5 instanceof Child4, s5 instanceof Parent4)//true -判断s5是不是 Child4 构造函数的实例
//继承的本质是原型链。怎么区分一个对象是由子类实例化还是由父类实例化的?
console.log(s5.constructor);//指向父类
// 组合继承优化2
function Parent5 (){
this.name = 'parent5'
this.play = [1, 2, 3]
}
function Child5 (){
Parent5.call(this);
this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype); //创建中间对象
Child5.prototype.constructor = Child5;
var s7 = new Child5();
console.log(s7 instanceof Child5, s7 instanceof Parent5)
console.log(s7.constructor)
//Object.create 创建对象的特点:创建一个新对象,使用现有的对象来提供新创建的对象的__proto__