JavaScript--从原型原型链聊到继承🧑‍💻

本文主要复习原型和原型链 ,希望能够对其有更深的认识,此外并会向外延申,探讨JavaScript中的继承方式

构造函数和方法

在谈原型和原型链之前,我们首先聊一聊构造函数和方法的区别!

我认为这是一个理论概念上的不同,我们由浅入深逐步理解:

  1. 在传统的面向对象编程的Java中,我们如何创建一个对象(以下所涉及的对象均指面向对象编程中对象的含义)?
    • 写一个类,确定其成员变量、构造函数,并为其添加方法。
    • 然后new出来,我们就会得到一个该类的对象。
  2. 但是在JavaScript中,早期是没有class关键字的,那我们是如何创建一个对象的呢?
    • 使用函数(当然不可以是箭头函数),利用this关键字为函数中的成员变量以及方法进行初始化。
    • 然后new出来,我们就能得到一个构造函数为该函数的对象。
js 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.sayHello = function () {
    console.log(this);
    console.log('Hello I am ' + this.name);
  }
}

因此在JavaScript中,构造函数和方法应该区分开来:

  • '构造函数'的概念可以理解为一个类,在你执行'构造函数'时(new)来实例化一个对象。
  • 方法的概念应该就是处理某些业务逻辑的函数。

再想一想:

  • 在Java中一个类写好了,他就放在那里,你不new他,他就只是一个文件、一串代码。当你真正实例化他的时候,他在在内存中开辟一块空间来存储一个实例。(这里的表达并不准确,并不是文件/代码,也会被初始化赋予一些信息)
    • 在JavaScript中其实也差不多,如上所述的代码,我不new他是什么?了解V8的同学就能做出回答,尽管对整个函数进行了解析和编译(生成了作用域等信息)并存储在堆内存中了,但是他并没有被调用,他就是静静的放在那而已。

因为早期Javascript设计之初就是宣称面向对象,但是由于各种原因并不像Java那样强类型,有明确的类概念。为了解决这个问题,JavaScript的设计师引入了原型和原型链的概念来解决这个问题。

原型和原型链

原型prototype 是一个对象,他存储了当前这个实例对象的构造函数;prototype只存在于函数中

console.log(Person.prototype) ==>

根据这个规定,我们可以知道Person === Person.prototype.constructor

原型链 :是一个已经被构造的对象中可以逐步找到构造该对象以及构造该对象的对象...这样的一条链路

这个链条是哪来的?在JavaScript中,每个对象都有__proto__这个属性,这个属性也是一个对象 ,存放的就是当前对象原型 ===> 实例对象的__proto__ === 构造函数的prototype

js 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.sayHello = function () {
    console.log(this);
    console.log('Hello I am ' + this.name);
  }
}
Person.prototype.live = true;
const p = new Person('ys', 18);
console.log(p, p.__proto__ === Person.prototype, Person.prototype);

上述代码中我们使用Person.prototype.live = true; 在构造函数Person的原型上增添了一个属性live。 那么我们就应该在Person的实例p上,通过__proto__找到这个属性,结果也是如此:

我们再向下分析一层:

框住的这个是什么? 他也是通过__proto__获取的,那么他一定是的原型,之前我们说过原型prototype是一个对象,那么他就应该是对象(Object)的__proto__;

答案也确实如此:p.__proto__.__proto__ === Object.prototype

总结一下:

  • 原型prototype是函数的一个属性,其中包括了这个函数的构造函数以及这个函数所拥有的属性和方法
  • __proto__是对象的一个属性,它指向该对象的原型,由一层一层的__proto所连接的就是原型链

继承

思考一下原型链有什么作用呢?我们可以通过原型链逐步找到这个对象构造函数的方法/属性。有点继承 的意思。实际确实如此,自从ES6之后,JS引入了class关键字,能够让我们更好的进行面向对象编程:

js 复制代码
class Person {
  name;
  age;
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHi() {
    console.log('Hi I am ' + this.name);
  }
}

class Student extends Person {
  grade;

  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade;
  }

  study() {
    console.log(this.name + " is study in grade " + this.grade);
  }
}

对了,要提一句的是,每个类中的方法都将存在于这个类的prototype上;

执行如下代码得到结果:

js 复制代码
const me = new Student('ys', 18, 3);
console.log(Student.prototype, me);
  • Student.prototype是Student的原型,包含方法以及构造函数
js 复制代码
console.log(Student.prototype.__proto__ === Person.prototype); //true
console.log(me.__proto__ === Student.prototype);  //true
//原型链体现在此处,父类的方法/属性可以在__proto__组成的链上找到!
console.log(me.__proto__.__proto__ === Person.prototype)  //true 

至此原型和原型链我们应该已经了解的差不多了,你应该已经可以自己进行一些分析了。

扩展

除了上述所说的以外,还有两个关注点:

  • FunctionObject;
  • Javascript中继承的方法;

FunctionObject

JS中有这样两个函数:Function Object

我们常说JS中一切都是对象,并且我们在打印出一个对象时总是可以一直展开,这些都是为什么呢?

打印结果是两个函数,说明他们肯定都有prototype

我们使用console.dir()FunctionObject 都打开打印出来看看:

神奇的事情发生了: 他俩都有prototype__proto__,在这里我们不能再利用之前说的prototype都在函数上,__proto__都在对象上了,因为他俩已经足够底层,再遵从这些规定就不能满足设计需求了,因此我们应该特殊的看待他们。

直接看图

Function.prototype === Function.__proto__ === Object.prototype

说实话,这里我还没有很是理解,感觉还是有点抽象,他这样设计的意义是什么,欢迎大家在评论区讨论。

下图是JS部分内建对象的引用:

Javascript中继承的方法

经过上面的学习,我们实际上可以发现:JS中的继承实际上就是子类的原型的__proto__指向父类的原型

但是在JS中又拥有很多种继承方式:

  • 原型链继承
  • 原型式继承
  • 组合继承
  • 构造函数继承
  • 寄生式继承
  • 寄生组合继承

拥有这么多方法是因为可能有些情况我们不希望继承的属性被子类修改。有时候又不需要继承方法,有时候要共享方法/属性....

这些看到我都头大了,要我说class就够了,真到什么时候需要进行性能优化再想这些吧。

但是如果面试要问那真没办法,不过我觉得思想是最重要的,JS中的继承就是前面说的prototype,我们只要想办法把他维护好应该就能达到预期的继承效果了。

如果有必要,我后面会将这里完善🤩

相关推荐
new出一个对象2 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
你挚爱的强哥3 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
y先森4 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy4 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189114 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿5 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡6 小时前
commitlint校验git提交信息
前端
虾球xz6 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇6 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒7 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript