当前位置: 首页 > news >正文

大白话javascript如何通过原型链实现对象的继承,并指出这种继承方式的优缺点

大白话javascript如何通过原型链实现对象的继承,并指出这种继承方式的优缺点

在JavaScript里,通过原型链实现对象继承就好比是孩子继承父母的财产和特性一样。以下是对其实现方式及优缺点的介绍:

实现方式

  • 首先得有一个“父母”对象,也就是父类函数。比如有个Animal函数来表示动物,它里面有一些动物共有的特性和行为,像eat方法。
function Animal() {this.eat = function() {console.log('吃东西');};
}
  • 然后再弄一个“孩子”对象,就是子类函数,比如Dog函数表示狗,它要继承Animal的特性和行为。
function Dog() {
}
  • 关键的一步来了,要把Dog的原型prototype指向Animal的一个实例,这样Dog就可以通过原型链找到Animal的所有东西。
Dog.prototype = new Animal();
  • 有时候还得给Dog的构造函数指回Dog自己,不然可能会出问题。
Dog.prototype.constructor = Dog;
  • 最后,就可以创建Dog的实例,这个实例就会有Animaleat方法了。
let myDog = new Dog();
myDog.eat(); 

优点

  • 代码复用:就像很多孩子可以共用父母传下来的好东西一样,多个子类可以共享父类的属性和方法,不用每个子类都重新写一遍,节省了代码量,也让代码看起来更简洁。
  • 关系清晰:通过原型链,很清楚地能看出来哪些对象是从哪个父类继承来的,就像家族树一样,一目了然,方便理解和维护代码。
  • 灵活扩展:如果想给父类添加新的功能,只要在父类上添加就可以了,所有子类都能自动继承这个新功能,就像父母学会了新技能,孩子们也能跟着受益。

缺点

  • 共享问题:子类共享父类的属性,如果不小心在一个子类实例中修改了父类的属性,可能会影响到其他子类实例,就像几个孩子共用一个玩具,一个孩子把玩具弄坏了,其他孩子也玩不了了。
function Animal() {this.foods = ['肉', '骨头'];
}function Dog() {
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;function Cat() {
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;let myDog = new Dog();
let myCat = new Cat();myDog.foods.push('狗粮');
console.log(myCat.foods); 
// 这里会输出['肉', '骨头', '狗粮'],这可能不是我们想要的结果
  • 实例化问题:在创建子类实例时,不能向父类构造函数传参数,不太灵活。就像孩子出生的时候,没办法给父母一些特定的信息来决定孩子怎么成长。
  • 性能问题:当查找一个属性或方法时,要沿着原型链一级一级地找,如果原型链很长,就会比较耗时,影响性能,就像要在一个很长的家族树里找一个东西,肯定要花不少时间。

在JavaScript中,如何通过原型链实现对象的继承?

咱们来好好唠唠在 JavaScript 里咋通过原型链实现对象的继承。你可以把这个过程想象成家族传承,老一辈把自己的本事和财产传给下一代,下一代就有了上一辈的东西,还能发展自己的特色。

啥是原型链和继承

在 JavaScript 里,每个对象都有一个隐藏的“小本本”,叫原型(prototype)。这个原型也是个对象,它也有自己的原型,就这样一层一层往上找,形成了一个链条,这就是原型链。而继承呢,就是一个对象可以用另一个对象的属性和方法,就像孩子能用父母的东西一样。

实现步骤

1. 先有个“老一辈”(父类)

咱们得先创建一个对象,把它当成“老一辈”,这个对象有一些属性和方法,就像老一辈有自己的本事和财产。

// 定义一个构造函数,当作“老一辈”,这里代表动物
function Animal() {// 给“老一辈”添加一个属性,代表动物的种类this.kind = '动物';// 给“老一辈”添加一个方法,代表动物吃东西的行为this.eat = function() {console.log('这个' + this.kind + '正在吃东西');};
}
2. 再弄个“小一辈”(子类)

接着创建另一个对象,把它当成“小一辈”,它要继承“老一辈”的东西。

// 定义一个构造函数,当作“小一辈”,这里代表狗
function Dog() {// 给“小一辈”添加自己特有的属性,代表狗的名字this.name = '旺财';
}
3. 让“小一辈”继承“老一辈”

这一步是关键,要让“小一辈”的原型指向“老一辈”的一个实例,这样“小一辈”就能通过原型链找到“老一辈”的属性和方法了。

// 让 Dog 的原型指向 Animal 的一个实例,实现继承
Dog.prototype = new Animal();
4. 修正“小一辈”的构造函数指向

在 JavaScript 里,每个对象都有一个构造函数的指向。当我们把 Dog 的原型指向 Animal 的实例后,Dog 的构造函数就变成 Animal 了,这不对,得把它指回 Dog 自己。

// 把 Dog 的构造函数指回 Dog 自己
Dog.prototype.constructor = Dog;
5. 创建“小一辈”的实例并使用

现在可以创建“小一辈”的实例了,这个实例就会有“老一辈”的属性和方法,还能有自己的属性和方法。

// 创建一个 Dog 的实例
let myDog = new Dog();// 访问“小一辈”自己的属性
console.log(myDog.name); // 访问从“老一辈”继承来的属性
console.log(myDog.kind); // 调用从“老一辈”继承来的方法
myDog.eat(); 

完整代码示例

// 定义“老一辈”构造函数
function Animal() {this.kind = '动物';this.eat = function() {console.log('这个' + this.kind + '正在吃东西');};
}// 定义“小一辈”构造函数
function Dog() {this.name = '旺财';
}// 让“小一辈”继承“老一辈”
Dog.prototype = new Animal();// 修正“小一辈”的构造函数指向
Dog.prototype.constructor = Dog;// 创建“小一辈”的实例
let myDog = new Dog();// 访问属性和调用方法
console.log(myDog.name); 
console.log(myDog.kind); 
myDog.eat(); 

通过以上步骤,就实现了通过原型链让 Dog 对象继承 Animal 对象的属性和方法。简单来说,就是让“小一辈”的原型指向“老一辈”的实例,这样“小一辈”就能顺着原型链找到“老一辈”的东西啦。

在JavaScript中,对象原型链的作用是什么?

咱来好好说说 JavaScript 里对象原型链的作用,其实你可以把原型链想象成一个家族传承的链条,每一环都代表着一代家族成员,后面的成员可以继承前面成员的东西,下面我分几个方面详细给你讲讲。

1. 实现继承

这就好比家族里孩子能继承父母的财产和技能。在 JavaScript 里,一个对象可以通过原型链继承另一个对象的属性和方法。

比如说,我们先有一个“动物”对象,它有“吃东西”的方法。然后有个“狗”对象,“狗”对象可以通过原型链和“动物”对象关联起来,这样“狗”对象就能使用“动物”对象的“吃东西”方法啦。

// 定义一个“动物”构造函数
function Animal() {// “动物”有吃东西的方法this.eat = function() {console.log('动物在吃东西');};
}// 定义一个“狗”构造函数
function Dog() {}// 让“狗”的原型指向“动物”的一个实例,建立原型链
Dog.prototype = new Animal();// 创建一个“狗”的实例
let myDog = new Dog();// “狗”实例可以调用从“动物”继承来的吃东西方法
myDog.eat(); 

这里“狗”对象通过原型链继承了“动物”对象的“eat”方法,就像孩子继承了父母的技能一样。

2. 代码复用

原型链能让多个对象共享同一个原型上的属性和方法,避免重复写代码。这就好比家族里大家都用同一个公共仓库里的工具,不用每个人都自己准备一份。

// 定义一个“形状”构造函数
function Shape() {// “形状”有绘制的方法this.draw = function() {console.log('绘制形状');};
}// 定义“圆形”构造函数
function Circle() {}
// 让“圆形”继承“形状”
Circle.prototype = new Shape();// 定义“方形”构造函数
function Square() {}
// 让“方形”继承“形状”
Square.prototype = new Shape();// 创建“圆形”和“方形”的实例
let circle = new Circle();
let square = new Square();// “圆形”和“方形”都可以使用从“形状”继承来的绘制方法
circle.draw(); 
square.draw(); 

这里“圆形”和“方形”对象都通过原型链继承了“形状”对象的“draw”方法,不用在“圆形”和“方形”里分别写“draw”方法的代码,实现了代码复用。

3. 提供默认值

原型链可以给对象提供默认的属性和方法。当对象本身没有这些属性和方法时,就会去原型链上找默认的。这就像你去超市买东西,如果自己没带购物袋,超市会给你提供一个默认的购物袋。

// 定义一个“汽车”构造函数
function Car() {}// 给“汽车”的原型设置默认属性
Car.prototype.color = '白色';
Car.prototype.wheels = 4;// 创建一辆“汽车”实例
let myCar = new Car();// 访问默认属性
console.log(myCar.color); 
console.log(myCar.wheels); 

“myCar”这个实例本身没有定义“color”和“wheels”属性,但通过原型链可以使用“Car”原型上设置的默认值。

4. 动态修改对象行为

原型链具有动态特性,你可以在程序运行的时候修改原型上的属性和方法,这样所有继承自这个原型的对象都会受到影响。这就好比你修改了公共仓库里的东西,所有从这个仓库拿东西的人都会拿到修改后的东西。

// 定义一个“人”构造函数
function Person() {this.name = '张三';
}// 给“人”的原型添加一个方法
Person.prototype.sayHello = function() {console.log('你好,我是' + this.name);
};// 创建一个“人”的实例
let person = new Person();// 调用 sayHello 方法
person.sayHello(); // 在运行时修改原型上的方法
Person.prototype.sayHello = function() {console.log('哈喽呀,我是' + this.name);
};// 再次调用 sayHello 方法,行为已改变
person.sayHello(); 

我们先让“person”调用“sayHello”方法,然后在运行时修改了“Person”原型上的“sayHello”方法,再次调用时,“person”执行的就是修改后的方法,体现了原型链动态修改对象行为的作用。

在JavaScript中,继承的优点和缺点是什么?

在 JavaScript 里,继承就像是家族传承,老一辈的本事、财产啥的传给小一辈,小一辈就不用从头开始学、从头开始攒了。不过呢,这事儿有好处也有坏处,下面给你详细唠唠。

继承的优点

1. 代码复用

这就好比家里有一套木工工具,爸爸用完儿子接着用,不用儿子再去买一套新的。在 JavaScript 里,当多个对象有一些相同的属性和方法时,我们可以把这些相同的部分放到一个父对象里,然后让其他对象继承这个父对象。这样,子对象就不用重复写这些代码了,节省了很多时间和精力,代码也变得更简洁。

比如说,我们有很多不同种类的车,像轿车、SUV、卡车等,它们都有一些共同的属性,比如轮子数量、颜色,还有共同的方法,比如启动、刹车。我们可以创建一个“车”的父对象,把这些共同的属性和方法放在里面,然后让轿车、SUV、卡车这些子对象继承“车”这个父对象。

// 定义一个“车”的父对象
function Car() {this.wheels = 4;this.start = function() {console.log('车启动了');};
}// 定义一个“轿车”的子对象,继承“车”
function Sedan() {}
Sedan.prototype = new Car();// 创建一个轿车实例
let mySedan = new Sedan();
// 可以使用从父对象继承来的属性和方法
console.log(mySedan.wheels); 
mySedan.start(); 
2. 提高可维护性

想象一下,如果家里所有的木工工具都放在一个固定的地方,大家要用的时候直接去拿就行。要是工具坏了,也只需要在这个固定的地方修。在 JavaScript 里,如果多个对象都继承自同一个父对象,当我们需要修改某个共同的属性或方法时,只需要在父对象里修改一次,所有继承自它的子对象都会受到影响。这样就避免了在每个子对象里都去修改代码,大大提高了代码的可维护性。

还是以车为例,如果我们要把所有车的启动方法改成更详细的启动方式,只需要在“车”这个父对象的 start 方法里修改就可以了,不用去每个子对象里改。

3. 建立清晰的层次关系

继承可以让我们很清楚地看到对象之间的层次关系,就像家族的族谱一样,一目了然。通过继承,我们可以把对象按照一定的逻辑进行分类和组织,让代码的结构更加清晰。

比如,我们可以先有一个“动物”的父对象,然后有“哺乳动物”“鸟类”等子对象继承“动物”,再在“哺乳动物”下面有“狗”“猫”等更具体的子对象。这样的层次关系让我们很容易理解每个对象的特点和它们之间的关系。

// 定义“动物”父对象
function Animal() {this.eat = function() {console.log('动物在吃东西');};
}// 定义“哺乳动物”子对象,继承“动物”
function Mammal() {}
Mammal.prototype = new Animal();// 定义“狗”子对象,继承“哺乳动物”
function Dog() {}
Dog.prototype = new Mammal();// 创建一个狗的实例
let myDog = new Dog();
// 可以使用从父对象和祖先对象继承来的方法
myDog.eat(); 

继承的缺点

1. 耦合度高

继承会让父对象和子对象之间的关系变得很紧密,就像一家人绑得太紧,一个人出问题可能会影响到其他人。如果父对象的代码发生了变化,比如修改了某个属性或方法,可能会对所有继承自它的子对象产生影响,甚至导致子对象的代码出现错误。

比如,我们在“车”这个父对象里修改了 start 方法的参数,那么所有继承自“车”的子对象在调用 start 方法时都可能会出错,需要跟着修改。

2. 灵活性降低

一旦子对象继承了父对象,它就会受到父对象的一些限制,就像孩子被父母的规矩束缚一样。子对象很难摆脱父对象的一些属性和方法,即使有些属性和方法对它来说并不适用。

比如,“车”这个父对象里有一个“载人数量”的属性,默认是 5 人。但是对于一些特殊的车,比如赛车,它可能只能载 1 人,这时候赛车这个子对象就很难摆脱父对象的这个默认属性。

3. 性能开销

在 JavaScript 里,通过原型链实现继承时,查找属性和方法需要沿着原型链一级一级地往上找。如果原型链很长,查找的时间就会变长,性能就会受到影响。这就好比在一个很长的家族族谱里找一个人,要花很多时间。

比如,一个对象的原型链有很多层,当我们访问这个对象的某个属性时,JavaScript 要从这个对象开始,一层一层地去它的原型对象里找,直到找到或者到原型链的顶端还没找到为止。

那么,除了原型链,JavaScript 还有哪些实现继承的方式?

在 JavaScript 里,除了原型链继承,还有不少实现继承的方式呢,下面我就用大白话给你详细说说。

构造函数继承

这就好比孩子只继承父母的一部分特定技能。在构造函数继承里,子类的构造函数中调用父类的构造函数,让子类实例拥有父类的属性。

例子
// 定义父类构造函数,就像父母有一些技能和财产
function Animal(name) {this.name = name;this.eat = function() {console.log(this.name + '正在吃东西');};
}// 定义子类构造函数,就像孩子要继承父母的东西
function Dog(name) {// 调用父类构造函数,让 Dog 实例拥有 Animal 的属性和方法Animal.call(this, name);
}// 创建 Dog 的实例
let myDog = new Dog('旺财');
myDog.eat(); 
解释

这里 Dog 是子类,Animal 是父类。在 Dog 构造函数里,用 Animal.call(this, name) 调用了父类 Animal 的构造函数,this 指向 Dog 的实例,name 是传给父类的参数。这样 Dog 的实例就有了 Animal 的属性和方法,就像孩子继承了父母的某些特定技能。

组合继承

它结合了原型链继承和构造函数继承的优点,就像孩子既继承了父母家族的传统技艺(原型链继承),又单独学了父母某些特定的技能(构造函数继承)。

例子
// 父类构造函数
function Animal(name) {this.name = name;this.eat = function() {console.log(this.name + '正在吃东西');};
}// 父类原型方法
Animal.prototype.sayHi = function() {console.log('Hi, 我是' + this.name);
};// 子类构造函数
function Dog(name) {// 构造函数继承Animal.call(this, name);
}// 原型链继承
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;// 创建 Dog 实例
let myDog = new Dog('旺财');
myDog.eat(); 
myDog.sayHi(); 
解释

Dog 构造函数里用 Animal.call(this, name) 实现了构造函数继承,能让每个子类实例都有自己的属性副本。然后通过 Dog.prototype = new Animal() 实现原型链继承,让子类实例可以使用父类原型上的方法,就像孩子既有自己单独学的技能,又能传承家族的传统技艺。

寄生组合继承

它是对组合继承的优化,避免了组合继承中父类构造函数被调用两次的问题,就像改进了孩子继承父母技能的方式,让过程更高效。

例子
// 父类构造函数
function Animal(name) {this.name = name;this.eat = function() {console.log(this.name + '正在吃东西');};
}// 父类原型方法
Animal.prototype.sayHi = function() {console.log('Hi, 我是' + this.name);
};// 子类构造函数
function Dog(name) {// 构造函数继承Animal.call(this, name);
}// 创建一个以 Animal.prototype 为原型的新对象
function createObject(proto) {function F() {}F.prototype = proto;return new F();
}// 寄生组合继承的关键步骤
let prototype = createObject(Animal.prototype);
prototype.constructor = Dog;
Dog.prototype = prototype;// 创建 Dog 实例
let myDog = new Dog('旺财');
myDog.eat(); 
myDog.sayHi(); 
解释

先定义了父类 Animal 和子类 Dog,然后通过 createObject 函数创建一个以 Animal.prototype 为原型的新对象,把这个新对象赋值给 Dog.prototype,并修正 constructor 指向,这样就避免了组合继承中创建父类实例时重复调用父类构造函数的问题,让继承更高效。

类继承(ES6 及以后)

ES6 引入了 classextends 关键字,让继承的写法更像传统面向对象语言,就像有了一套更规范的继承规则。

例子
// 定义父类
class Animal {constructor(name) {this.name = name;}eat() {console.log(this.name + '正在吃东西');}
}// 定义子类,继承父类
class Dog extends Animal {constructor(name) {// 调用父类构造函数super(name);}
}// 创建 Dog 实例
let myDog = new Dog('旺财');
myDog.eat(); 
解释

class 关键字定义类,extends 关键字实现继承。Dog 类继承自 Animal 类,在 Dog 的构造函数里用 super(name) 调用父类的构造函数,把参数传递给父类,就像按照规范让孩子顺利继承父母的东西。


http://www.mrgr.cn/news/92195.html

相关文章:

  • ddd 文章总结分享,ddd实战代码分享, 领域驱动设计java实战源码大全,我看过的ddd java源码
  • C1车证学习笔记
  • (七)趣学设计模式 之 适配器模式!
  • 【算法】二分789. 数的范围
  • Node.js技术原理分析系列——Node.js的perf_hooks模块作用和用法
  • AIGC技术助力空军招飞,近屿智能开启智能人才培育新征程
  • ai-1、人工智能概念与学习方向
  • 毕业项目推荐:基于yolov8/yolov5/yolo11的番茄成熟度检测识别系统(python+卷积神经网络)
  • DLP数据防泄漏产品的关键性能指标
  • 电机控制的空间矢量调制 (SVPWM)
  • 【2025信息安全软考重点考点归纳】实时更新
  • 沁恒CH32V307RCT6烧写hex文件时报错“设置芯片型号失败”
  • 【DeepSeek部署经验】Ollama(运行平台)+DeekSeek(对话模型)+Cherry Studio(可视化对话界面)
  • 机器视觉匹配中使用bpnet匹配,二
  • [Web 安全] Web 安全攻防 - 学习手册
  • 大白话Vue2和Vue3双向数据绑定的原理
  • springboot集成jackson-dataformat-xml实现发送XML请求和XML响应参数处理
  • 毕业项目推荐:基于yolov8/yolo11的水稻叶片病害检测识别系统(python+卷积神经网络)
  • 玩机日记 10 fnOS 开启文件传输服务挂载到手机/电脑,测试传输性能
  • 深入理解Self-Attention - 原理与等价表示