javascript面向对象

1.类与实例

1.1 类的声明

function Animal() {
    this.name = 'name';
}

1.2 ES6中类的声明

class Animal2{
    constructor() {
        this.name = name;
    }
}

1.3 类的实例化

new Animal(); //没有参数的话,括号可以不要

2.类与继承

2.1 借助构造函数实现继承

function Parent1() {
    this.name = 'parent1';
}
function Child1() {
    Parent1.call(this); 
    this.type = 'child1';
}

缺点:只实现了部分继承,继承不到父类原型上的方法。

测试代码如下,运行时会报错。child1没有继承到父级原型上的方法。

function Parent1() {
    this.name = 'parent1';
}
Parent1.prototype.say = function() {}
function Child1() {
    Parent1.call(this); 
    this.type = 'child1';
}
console.log(new Child1(), new Child1().say());

2.2 借助原型链实现继承

这种方式可以解决第一种方法无法继承父级对象原型上方法的问题。

function Parent2() {
    this.name = 'parent2';
}
function Child2() {
    this.type = 'child2';
}
Child2.prototype = new Parent2();
缺点:改一个对象属性,另一个也跟着改变。原型对象是共用的。
function Parent2() {
    this.name = 'parent2';
    this.play = [1, 2, 3];
}
function Child2() {
    this.type = 'child2';
}
Child2.prototype = new Parent2();
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]

2.3 组合方式

方案一二的结合版,可以解决部分继承和原型对象共用的问题。是最通用的方式。

function Parent3() {
    this.name = 'Parent3';
}
function Child3() {
    Parent3.call(this);
    this.type = 'child3';
}
Child3.prototype = new Parent3();
缺点:父类构造函数执行了两次,是没有必要的。
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);

2.4 组合继承的优化一

优化:父类构造函数只执行了一次

function Parent4() {
    this.name = 'Parent4';
}
function Child4() {
    Parent4.call(this);
    this.type = 'child4';
}
Child4.prototype =Parent4.prototype;

缺点:不能区分是直接由parent4实例化的还是child4实例化的。

用instanceof判断:

function Parent4() {
    this.name = 'Parent4';
}
function Child4() {
    Parent4.call(this);
    this.type = 'child4';
}
Child4.prototype =Parent4.prototype;
var s4 = new Child4();
var s5 = new Child4();
console.log(s4 instanceof Child4, s4 instanceof Parent4); //true true

用constructor构造器判断,它是Parent4的实例。这显然不是我们想要的。此时不难判看出,上一个组合方法中也会是这样的结果。它们直接拿的是父类的实例,它们没有自己的constructor,它的constructor是从父类实例继承的,也就是原型链的上一级拿来的,所以是父类的constructor。

console.log(s4.constructor);  // Parent4对象

2.5 组合继承优化二

function Parent5() {
    this.name = 'Parent5';
}
function Child5() {
    Parent5.call(this);
    this.type = 'child5';
}
Child5.prototype =Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;

通过创建中间对象的方法,就把两个原型对象区分开,这个中间对象还具备一个特性,它的原型对象是父类的原型对象。这样在原型链上就连起来了,通过再给child5这个原型对象上的constructor作修改,那么就能正确区分父类和子类构造函数了。

至此,问题就全部解决了。

var s7 = new Child5();
console.log(s7 instanceof Child5, s7 instanceof Parent5);
console.log(s7.constructor);

控制台打印结果:

原文地址:https://www.cnblogs.com/PeriHe/p/8568573.html