JavaScript面向对象知识点串联

JavaScript面向对象知识点串联

本文我会尽可能的把面试有关的问题都加入进来,并把学习踩坑的地方告诉大家。(知识点可以点击我给的连接)

面试问题

  1. 解释一下原型链
  2. instanceof的原理
  3. typeof的相关问题
  4. new的原理
  5. this指向问题
  6. 任何有关bind、call、apply的问题
  7. ES5继承的方法

前置知识

要理解原型链,那我们就得先理解什么是面向对象,它的三个基本特征:封装、继承、多态_

❗❗❗巨坑注意:一定要区分好继承和实例化的区别,特别是像js这样的基于原型继承的语言,我就踩过😫,导致开始学时特别痛苦。

原型和原型链的理解

原型和面向编程

相信很多同学都或多或少的接触过一定面向对象编程,也清楚的知道面向对象对于编程开发的重要性,那为什么到了JavaScript,我们说到面向对象就得说到原型呢?

这要从JavaScript的源头说起。JavaScript是一种动态类型语言,区别于c++和Java,它实现继承的方法并不是靠类,而是靠原型。

又一个坑:你可能会说ES6中不是有类吗?

其实不然,ES6中的类并不是原生支持的类,而是一个语法糖(底层并不是像c++那样,而是对JS(ES6之前的语法,以下都是这个意思)模拟继承的一种封装,这也是为什么再使用ES6时,我们需要一些工具(babel)来将ES6转换为ES5。

原型和原型链

话归原题,那到底什么是原型和原型链呢?这就应该从实例化开始谈起。

这里我就不细说了,可以看阮一峰的教程:面向对象编程 - 实例对象与 new 命令 - 《阮一峰 JavaScript 教程》 - 书栈网 · BookStack

到这里我可以认为你已经知道了构造函数和new的使用,也知道了prototype

理解时,我们可以把原型认为是类。

之前我迷惑的地方

函数为什么是把原型传给对象,而不是本身传给对象?

或者问为什么老是遇见构造函数里有原型,而原型里面又有构造函数

1
2
3
4
5
function Person(name, age) {
this.name = name;
this.age = age;
}
console.log(Person.prototype.constructor === Person); // true

答:函数本身就是一种数据类型,它有自己的任务。它来当构造函数相当于就是为了建立一个原型和对象的桥梁,相当于它生产了一个(不严谨),然后把这个交给了新的实例。这也就解释了为什么构造函数的原型里面还要加一个构造函数,因为这个构造函数是给新生成的对象用的。

pass

(日后完善)

让我们打开浏览器,按一下f12,再控制台中输入Object(),回车。我们就能看见一个空的对象。

image-20231124134648166

这就是JavaScript对象的结构了。

我们先看一下重点,第一个prototype就是我们一直提到的原型,那为什么又要提到原型链呢?我们又来点开__proto__,我们可以看见

!(C:\Users\27421\AppData\Roaming\Typora\typora-user-images\image-20231124135440074.png)

原型链中就是实例对象原型对象之间的链接

原型(prototype,《高达00》曲

array原型,

基于原型和基于类

继承

原本不难的继承,放到js里面居然变成了一个大关。

js里面有很多办法实现继承,去翻翻js的教材,可以看到很多方法可以模拟继承。

其实反过来想,如果没有静态语言给的继承这个概念,js是不是会灵活的多?

所以我们要做自己(😊,反讽ing)

ES6提供了很好用的class语法糖。

这里就不谈js实现继承了,感觉这玩意就是为了考而考。

那么继续开始踩坑。

首先是对super的理解,第一次遇见super是在react的类组件里,class里面的constructor里面突兀出现一个super(),我是一点都无法理解这个取名的意义的。这里还是推荐看阮一峰的ES6教程

PS:每次遇见新的语句,特别是在开发时遇见,不要去猜这个怎么用,而是马上去修补这块知识点,你这次猜对了,后面八成还有其他问题,跟谎言一样,应该杜绝第一次欺骗

对组合继承和寄生组合的理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 组合继承
// function NOO() { }
// console.log(NOO.prototype);
// // zono = new NOO();
// // console.log(zono);
// // zono.consctructor = NOO;
// function NOO2() {}
// NOO2.prototype = new NOO();
// NOO2.prototype.constructor = NOO2;
// console.log(NOO2.prototype);
// console.log(NOO2.prototype === NOO.prototype);

//寄生组合继承
function NOO() {}
console.log(NOO.prototype);
function NOO2() {}

function inheritPrototype(subType, superType) {
//使用寄生继承
//就是把原型对象赋值给子类的原型对象,减少了一次调用父类构造函数
//原版代码
// NOO2.prototype = new NOO();//这里会调用一次父类构造函数,修改后就无需调用了
// NOO2.prototype.constructor = NOO2;
const prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}

inheritPrototype(NOO2, NOO);

console.log(NOO2.prototype);
console.log(NOO2.prototype === NOO.prototype);
debugger;