JS面向对象中的继承

本文详细介绍了JavaScript中的原型链、原型式继承、Object.create()方法以及构造函数实现继承等概念,阐述了它们的工作原理和优缺点。通过示例代码展示了如何在不同场景下实现对象间的继承,并讨论了组合式继承作为常见的解决方案。同时,提到了在实际开发中,由于原型链和构造函数继承存在的问题,开发者通常会选择组合多种继承方式来达到最佳效果。
摘要由CSDN通过智能技术生成

继承

原型链

原型链是什么

构造函数或构造器具有prototype属性,对象具有__proto__属性,这就是之前学习的原型

如果构造函数或对象A,A的原型指向构造函数或对象B,B的原型再指向构造函数或对象C,以此类推,最终的构造函数或对象的原型指向Object的原型。由此形成一条链状结构,被称之为原型链。

按照上述的描述,在B中定义的属性或方法,可以直接在A中使用并不需要定义。这就是继承,它允许每个对象来访问其原型链上的任何属性或方法。

原型链是ECMAScript标准中指定的默认实现继承的方式。

代码:

// 原型链
function A(){
    this.a = 'a';
}
// 通过构造函数创建对象
var a = new A();

function B(){
    this.b = 'b';
}
// 将B的原型指向对象a
B.prototype = a;
// 通过构造函数创建对象
var b = new B();

console.log(b.b);// b
console.log(b.a);// a

function C(){
    this.c = 'c';
}
// 将C的原型指向对象b
C.prototype = b;
// 通过构造函数创建对象
var c = new C();

console.log(c.c);// c
console.log(c.b);// b
console.log(c.a);// a

效果:
在这里插入图片描述

在这里插入图片描述

只继承于原型

出于对效率的考虑,尽可能地将属性和方法添加到原型上,

可以采用以下方式:

  • 不要为继承关系单独创建新对象。
  • 尽量减少运行时的方法搜索

代码:

// 原型链
function A(){
    // 将自有属性改写为原型属性
    // this.a = 'a';
}
A.prototype.a = 'a';

function B(){
    // this.b = 'b';
}

// 将B的原型指向
B.prototype = A.prototype;

B.prototype.b = 'b';
/*B.prototype = {
    b : 'b'
}*/

function C(){
    this.c = 'c';
}
// 将C的原型指向
C.prototype = B.prototype;

var c = new C();
console.log(c.c);// c
console.log(c.b);//b
console.log(c.a);// a

效果:
在这里插入图片描述

原型链实现继承的问题

原型链虽然很强大,用它可以实现JavaScript中的继承,但同时也存在着一些问题。

  • 原型链实际上实在多个构造函数或对象之间共享属性和方法。
  • 创建子类的对象时,不能向父级的构造函数传递任何参数。

综上所述,在实际开发中很少会单独使用原型链。

代码:

// 原型链
function A(){
    // 将自有属性改写为原型属性
    // this.a = 'a';
}
A.prototype.a = 'a';

function B(){
    // this.b = 'b';
}

// 将B的原型指向
B.prototype = A.prototype;

B.prototype.b = 'b';

function C(){
    // this.c = 'c';
}
// 将C的原型指向
C.prototype = B.prototype;
C.prototype.c = 'c';

var c = new C();
console.log(c.c);// c
console.log(c.b);// b
console.log(c.a);// a

var a = new A();

console.log(a.a);
console.log(a.b);
console.log(a.c);

var b = new B();

console.log(b.a);
console.log(b.b);
console.log(b.c);

效果:
在这里插入图片描述

原型式继承

所谓原型式继承,就是定义一个函数,该函数中创建一个临时性的构造函数,将作为函数传入的对象作为这个构造函数的原型,最后返回这个构造函数的实例对象。

function object(o){
	function F(){
	F.prototype = o;
	return new F();
	}
}

代码:

/*
    定义一个函数 - 用于实现对象之间的继承
     参数
       obj - 表示继承关系中的父级对象
       prop - 对象格式,表示继承关系中的子级对象的属性和方法
  */
function fn(obj, porp){
    // 定义一个临时的构造函数
    function Fun(){
        // 遍历对象的属性和方法
        for (var attrName in porp) {
            // var attrValue = porp[attrName];
            this[attrName] = porp[attrName];
        }
    }
    // 将函数的参数作为构造函数的原型
    Fun.prototype = obj;
    // 将构造函数创建的对象进行返回
    return new Fun();
}
var obj = {
    name : '肖德硕'
}
// 调用函数
var result = fn(obj, {
    age : 18,
    sayMe : function(){
        console.log('this is function');
    }
});
console.log(result.age);
result.sayMe();

效果:
在这里插入图片描述

Object.create()方法
Object.create(proto,[propertiesObject])
  • proto:新创建对象的原型对象。
  • propertiesObject:可选。需要传入一个对象,该对象的属性类型参照[Object.defineProperties()]的第二个参数。如果该参数被指定且不为 [undefined],该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。
// 利用Object.create()方法实现继承
var obj = {
    name : '张无忌'
}
var newObj = Object.create(obj, {
    age : {
        value : 18
    },
    sayMe : {
        value : function(){
            console.log('this is function');
        }
    }
});
console.log(newObj.age);
newObj.sayMe();

效果:
在这里插入图片描述

借助构造函数实现继承

无论是原型链还是原型继承,都具有相同的问题。想要解决这样的问题的话,可以借助构造函数(也可以叫做伪造对象或者经典继承)。

这种方式实现非常简单,就是在子对象的构造函数中调用父对象的构造函数。具体可以通过调用apply()和call()方法实现。

apply()和call()方法都允许传递指定某个对象的this。对于继承来讲,可以实现在子对象的构造函数中调用父对象的构造对象时,将子对象的this和父对象的this绑定在一起。

代码:

// 定义父级对象的构造函数
function Parent(){
    this.parent = 'parent';
}

// 定义子级对象的构造函数
function Child(){
    // 调用父级对象的构造函数 -> 使用apply()或call()方法
    Parent.call(this);

    this.child = 'child';
}
// 创建子级对象
var child = new Child();
console.log(child);//Child { parent: 'parent', child: 'child' }

在这里插入图片描述

组合式继承

组合继承,也叫做伪经典继承,指的是将原型链或原型式继承和借助构造函数的技术组合在一起,发挥二者长处的一种继承方式。

具体实现的思路就是:

  • 使用原型链或原型式继承实现对原型的属性和方法的继承。
  • 通过借助构造函数实现对实例对象的属性的继承。

这样,既通过在原型上定义方法实现了函数的重用,又可以保证每个对象都有自己的专有属性。

代码:

function Parent(){
    // 构造函数的自有属性
    this.name = '肖德硕';
}
// 构造函数的原型属性
Parent.prototype.age = 18;

function Child(){
    // 继承父级构造函数中的自有属性
    Parent.call(this);

    this.job = '学生';
}
// 继承父级构造函数中的原型属性
Child.prototype = Parent.prototype;

var child = new Child();

console.log(child.job);
console.log(child.age);
console.log(child.name);

效果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值