编辑
2023-07-10
JS基础
00
请注意,本文编写于 512 天前,最后修改于 496 天前,其中某些信息可能已经过时。

目录

原型
继承
总结

JavaScript是一种基于原型的语言,它没有类的概念,而是通过原型对象来实现对象之间的继承和共享属性和方法。本文将介绍JS中的原型和继承的基本概念和用法,以及如何通过构造函数和new操作符来创建一类具有相同结构和行为的对象,并通过设置一个对象的原型对象来实现子类继承父类的功能。如果你想了解JS中的原型和继承是如何工作的,以及如何利用它们来编写更优雅和高效的代码,那么本文值得一读。

JavaScript是一种基于原型的语言,这意味着它没有类的概念,而是通过原型对象来实现对象之间的继承和共享属性。本文将介绍JS中的原型和继承的基本概念和用法。

原型

原型是一个对象,它可以作为其他对象的模板,提供一些共有的属性和方法。在JS中,每个对象都有一个内部属性[[Prototype]],它指向该对象的原型对象。我们可以通过Object.getPrototypeOf(obj)或者obj.__proto__(不推荐)来获取一个对象的原型对象。

例如,我们创建一个普通对象obj,它的原型对象就是Object.prototype,这是所有对象的默认原型对象。

js
let obj = {name: "Alice", age: 20}; console.log(Object.getPrototypeOf(obj) === Object.prototype); // true console.log(obj.__proto__ === Object.prototype); // true

我们也可以通过Object.create(proto)来创建一个以指定对象为原型的新对象。例如,我们创建一个以obj为原型的新对象obj2

js
let obj2 = Object.create(obj); console.log(Object.getPrototypeOf(obj2) === obj); // true console.log(obj2.__proto__ === obj); // true

我们可以通过原型链来访问一个对象的属性和方法。原型链是一系列的原型对象,从当前对象开始,一直到Object.prototype结束(Object.prototype的原型是null)。当我们访问一个对象的某个属性或方法时,JS会先在当前对象上查找,如果找不到,就会沿着原型链向上查找,直到找到或者到达原型链的末端。

例如,我们访问obj2.name时,JS会先在obj2上查找,发现没有这个属性,就会沿着原型链向上查找,在obj上找到了这个属性,并返回其值。

js
console.log(obj2.name); // Alice

我们也可以通过修改一个对象的原型来改变其继承的属性和方法。例如,我们给obj添加一个新的属性gender,那么所有以obj为原型的对象都会继承这个属性。

js
obj.gender = "female"; console.log(obj2.gender); // female

但是,如果我们给一个对象添加或修改一个与其原型同名的属性或方法,那么就会在该对象上创建一个自有的属性或方法,覆盖掉原型上的同名属性或方法。例如,我们给obj2添加一个新的属性name,那么就会在obj2上创建一个自有的属性name,覆盖掉原型上的同名属性。

js
obj2.name = "Bob"; console.log(obj2.name); // Bob console.log(obj.name); // Alice

继承

继承是一种实现代码复用和抽象化的机制,在JS中,我们可以通过原型来实现继承。我们可以通过构造函数和new操作符来创建具有相同结构和行为的一类对象,并通过修改构造函数的prototype属性来指定这类对象共享的原型对象。

例如,我们定义一个构造函数Person,用来创建具有nameage属性和sayHello方法的人类对象,并给Person.prototype添加这些属性和方法。

js
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { console.log("Hello, I'm " + this.name); };

然后我们用new操作符来创建两个Person实例p1p2,并调用它们的sayHello方法。

js
let p1 = new Person("Alice", 20); let p2 = new Person("Bob", 21); p1.sayHello(); // Hello, I'm Alice p2.sayHello(); // Hello, I'm Bob

我们可以看到,p1p2都有自有的nameage属性,但是它们共享同一个sayHello方法,这个方法是定义在Person.prototype上的。我们也可以看到,p1p2的原型对象都是Person.prototype

js
console.log(p1.sayHello === p2.sayHello); // true console.log(p1.sayHello === Person.prototype.sayHello); // true console.log(Object.getPrototypeOf(p1) === Person.prototype); // true console.log(Object.getPrototypeOf(p2) === Person.prototype); // true

我们也可以通过修改Person.prototype来改变所有Person实例共享的属性和方法。例如,我们给Person.prototype添加一个新的属性gender,并给sayHello方法添加一句话。

js
Person.prototype.gender = "unknown"; Person.prototype.sayHello = function() { console.log("Hello, I'm " + this.name + ", and my gender is " + this.gender); };

然后我们再次调用p1p2sayHello方法,可以看到输出结果发生了变化。

js
p1.sayHello(); // Hello, I'm Alice, and my gender is unknown p2.sayHello(); // Hello, I'm Bob, and my gender is unknown

我们也可以通过原型来实现子类继承父类的功能。我们可以通过Object.create(proto)或者Object.setPrototypeOf(obj, proto)来设置一个对象的原型对象。例如,我们定义一个构造函数Student,用来创建具有nameagescore属性的学生类对象,并让Student.prototype继承自Person.prototype

js
function Student(name, age, score) { Person.call(this, name, age); // 调用父类构造函数,继承父类属性 this.score = score; // 添加子类属性 } Student.prototype = Object.create(Person.prototype); // 设置子类原型为父类原型的副本,继承父类方法 Student.prototype.constructor = Student; // 修复子类构造函数指向

然后我们用new操作符来创建一个Student实例s,并调用它的sayHello方法。

js
let s = new Student("Charlie", 22, 90); s.sayHello(); // Hello, I'm Charlie, and my gender is unknown

我们可以看到,s继承了Person类的nameagegender属性和sayHello方法,同时也有自有的score属性。我们也可以看到,s的原型对象是Student.prototype,而Student.prototype的原型对象是Person.prototype,形成了一个原型链。

js
console.log(s.name); // Charlie console.log(s.age); // 22 console.log(s.gender); // unknown console.log(s.score); // 90 console.log(Object.getPrototypeOf(s) === Student.prototype); // true console.log(Object.getPrototypeOf(Student.prototype) === Person.prototype); // true

我们也可以通过修改Student.prototype来给子类添加或覆盖父类的属性和方法。例如,我们给Student.prototype添加一个新的方法study,并覆盖父类的sayHello方法。

js
Student.prototype.study = function() { console.log("I'm studying hard to get a high score."); }; Student.prototype.sayHello = function() { console.log("Hi, I'm " + this.name + ", and my score is " + this.score); };

然后我们再次调用ssayHellostudy方法,可以看到输出结果发生了变化。

js
s.sayHello(); // Hi, I'm Charlie, and my score is 90 s.study(); // I'm studying hard to get a high score.

总结

JS中的原型和继承是一种基于对象而非类的编程范式,它通过原型对象来实现对象之间的属性和方法的共享和继承。我们可以通过构造函数和new操作符来创建一类具有相同结构和行为的对象,并通过修改构造函数的prototype属性来指定这类对象共享的原型对象。

我们也可以通过原型链来访问或修改一个对象继承自其原型对象的属性和方法。我们也可以通过设置一个对象的原型对象来实现子类继承父类的功能。这种方式可以实现代码的复用和抽象化,但也要注意避免原型污染和性能损耗的问题。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:CreatorRay

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!