【面试题】简单的说说对原型链的了解

简介: 【面试题】简单的说说对原型链的了解

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全

前言

作为Javascript的基础之一,原型一直贯穿我们的JS代码并且成为面试的常考问题。用我自己的话去理解,原型就是类,通过创建(new)一个实例的方法去继承这个类的属性跟方法,而原型链就是一个不断继承的链条,到了顶级(Object)会用null去终止这个链条的延伸。

原型

什么是原型

The prototype is an object that is associated with every functions and objects by default in JavaScript, where function's prototype property is accessible and modifiable and object's prototype property (aka attribute) is not visible. Every function includes prototype object by default

用官方的解释就是

在JavaScript中,prototype对象的继承性是实现面向对象的一个重要机制。

每个函数就是一个对象,函数对象都有一个子对象 prototype对象,类是以函数的形式来定义的。prototype表示该函数的原型,也表示一个类的成员的集合

用java的类概念去理解

初学的时候,网上不少人用一张错综复杂的图去诠释原型链的关系,然而对于涉猎未深的同学,这无疑是灾难般的入门方法。而我期望各位先把概念理清,再用一张最基础的图循序渐进地理解原型链的关系。

学过面向对象的同学都会用类去定义一个通用的类型,如此一来,我们创建一个新的实例,便继承了类(Kitten)的方法

public class Kitten{
    int kittenAge;
    public kitten(age){
      kittenAge = age;
    }
    public void roar() {
      System.out.println("喵喵喵"); 
    }
}
复制代码

在这里直接创建了一个实例(example),可以看到实例继承到了父类(Kitten)的属性跟方法

Kitten example = new Kitten(12);
System.out.println(example.kittenAge);  // 12
example.roar(); // 喵喵喵
复制代码

用函数实现构造函数

Javascript并没有class这种关键字,虽然我是用class去理解构造函数,但实际上Javascript是用函数去实现

补充:ES后补充了class的写法,但这里只用function做栗子

function Kitten(age){
    this.kittenAge = age
    this.say = function(){
        console.log('喵喵喵')
    }
}
const example = new Kitten(33)
console.log(example.kittenAge) // 33
复制代码

example 就是构造函数(Kitten)的实例,它继承了Kitten的属性跟方法

理解prototype跟_proto_,constructor

先了解几个规则:

  1. 引用类型,都有一个隐性原型( __proto__)的属性,指向一个普通的对象
  2. 引用类型的隐性原型指向其构造函数的显性原型(prototype)
  3. 在获取对象的属性时,如果它内部没有这个属性,它会去隐性原型去寻找(也就是其构造函数的显性原型)

prototype的中文翻译就是原型,一般称之为显性原型,这个属性只是添加给构造函数用,可以通过prototype去增加属性或方法,实例没有显性原型这个属性. 而_proto_被称为隐性原型,所有引用类型都有隐形原型

要充分了解原型链,我们要特别留意上述的规则2:

function Kitten(age){
    this.kittenAge = age
    this.say = function(){
        console.log('喵喵喵')
    }
}
const example = new Kitten(33)
const obj = {};
example.__proto__ === Kitten.prototype
obj.__proto__ === Object.prototype
复制代码

以及规则3:obj没有toString()的方法,它会沿着构造函数Object的显性原型去寻找

const obj = {a : 1}
obj.toString() // [object Object]
复制代码

constructor

这个单词的直译就是构造函数,上面我们用prototype指向构造函数的原型,那么这里就可以用constructor去指向原型的构造函数,:

function Kitten(age){
    this.kittenAge = age
    this.say = function(){
        console.log('喵喵喵')
    }
}
const example = new Kitten(33)
Kitten.prototype.constructor === Kitten // true
复制代码

用一张最直观的图来显示三者的关系 来源于juejin.cn/post/684490…

必须留意的是,最后的Object的prototype的隐形原型__proto__指向null,是为避免出现原型链的死循环

理解instanceof

想起我刚入行时面试官问我的一个问题

“用什么方法可以判断数据类型?”

“typeof”

“那可以判断出普通对象(Object)跟数组(Array)吗?”

“可以”

结果就是面试官留下了冷冷的不屑,然后我一结束面试马上check了一下。typeof只能判断基本类型,但不能精确判断引用类型。这时候instanceof就发挥作用了,而它就是利用原型链的原理,一直往上寻找,当链上出现xxx.prototype(如Object.prototype)时,就会判断为true

代码举例

const person = new Person()
const arr = [1,2,3]
console.log(person instanceof Person) // true
console.log(arr instanceof Array) // true
console.log(Person instanceof Object) // true
console.log(person instanceof Object) // true
复制代码

上面用了四个instanceof方法去判断对象跟数组

  1. 根据原型链Person.prototype === person.__proto__,结果为true
  2. Array.prototype === arr.__proto__,结果为true
  3. Object.prototype === Person.__proto__,结果为true
  4. Object.prototype === person.__proto__.__proto__ 沿着原型链一直向上寻找,找到Object.prototype就判断为true

除此之外,Set,Map,Sympol等新加入的ES数据类型也能用原型链instanceof的方法去判断。

手写instanceof

好了,了解清楚之后,写一个笔试常见JS

function instanceof(target, class) {
  // 参数检查
  if(!target || !class || !target.__proto__ || !class.prototype){
    return false;
  }
  let current = target;
  while(current) {   // 一直往原型链上面找
    if(current.__proto__ === class.prototype) {
      return true;    // 找到了返回true
    }
    current = current.__proto__;
  }
  return false;     // 没找到返回false
}
function Parent() {}
function Child() {}
Child.prototype.__proto__ = Parent.prototype;
const obj = new Child();
console.log(myInstanceof(obj, Child) );   // true
console.log(myInstanceof(obj, Parent) );   // true
console.log(myInstanceof({}, Parent) );   // false
复制代码

总结

  1. 搞清楚原型,实例,构造函数的关系
  2. 理解Instanceof的原理跟用法
  3. 明白Object.prototype.__proto__ === null 是为了杜绝原型链死循环的设定
  4. 通过原型链 清晰理解子类继承父类的原理,如图所示,新建一个对象实例时,就能继承父类的属性跟方法。

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全

相关文章
|
弹性计算
阿里云免费企业邮箱申请流程
免费版的阿里云企业邮箱给你一个很好的体验,享受中小企业发展时期的优惠扶持。那怎么才能申请阿里云免费的企业邮箱呢??
43464 0
|
8月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
561 26
|
6月前
|
Java 关系型数据库 MySQL
深入解析 @Transactional——Spring 事务管理的核心
本文深入解析了 Spring Boot 中 `@Transactional` 的工作机制、常见陷阱及最佳实践。作为事务管理的核心注解,`@Transactional` 确保数据库操作的原子性,避免数据不一致问题。文章通过示例讲解了其基本用法、默认回滚规则(仅未捕获的运行时异常触发回滚)、因 `try-catch` 或方法访问修饰符不当导致失效的情况,以及数据库引擎对事务的支持要求。最后总结了使用 `@Transactional` 的五大最佳实践,帮助开发者规避常见问题,提升项目稳定性与可靠性。
945 12
|
存储 SQL 关系型数据库
MySQL高级篇——索引失效的11种情况
索引优化思路、要尽量满足全值匹配、最佳左前缀法则、主键插入顺序尽量自增、计算、函数导致索引失效、类型转换(手动或自动)导致索引失效、范围条件右边的列索引失效、不等于符号导致索引失效、is not null、not like无法使用索引、左模糊查询导致索引失效、“OR”前后存在非索引列,导致索引失效、不同字符集导致索引失败,建议utf8mb4
MySQL高级篇——索引失效的11种情况
|
存储 Java Serverless
【数据结构】哈希表&二叉搜索树详解
本文详细介绍了二叉搜索树和哈希表这两种数据结构。二叉搜索树是一种特殊二叉树,具有左子树节点值小于根节点、右子树节点值大于根节点的特点,并且不允许键值重复。文章给出了插入、删除和搜索等方法的具体实现。哈希表则通过哈希函数将键名映射为数组下标,实现快速查找,其插入、删除和查找操作时间复杂度理想情况下为O(1)。文中还讨论了哈希函数的设计原则、哈希冲突的解决方法及哈希表的实现细节。
262 8
【数据结构】哈希表&二叉搜索树详解
|
缓存 Java 编译器
JRE、JDK、JVM 和 JIT 之间的区别详解
【8月更文挑战第22天】
663 0
|
人工智能 弹性计算 定位技术
【云故事探索】NO.4: 千寻位置,时空智能赋能行业数字化转型
千寻位置,成立于2015年,利用北斗卫星系统及全球5000多座增强站,提供厘米级定位服务。该公司借助阿里云的计算能力,为汽车、农业等多个行业提供高精度时空智能解决方案,推动行业转型升级。千寻已完成超130亿元估值的A轮融资,展现了其在时空智能领域的领先地位。通过云上部署,千寻优化服务质量和市场扩展,应对突发流量,计划进一步全球化并应用AI技术。阿里云的支持对于千寻的成功至关重要,双方合作将时空智能服务推向国际。
【云故事探索】NO.4: 千寻位置,时空智能赋能行业数字化转型
|
人工智能 物联网 人机交互
未来智能家居技术的发展趋势与挑战
【2月更文挑战第10天】随着人工智能、物联网等技术的快速发展,智能家居正在成为现代生活中不可或缺的一部分。本文探讨了未来智能家居技术的发展趋势与挑战,分析了人机交互、数据安全、生态整合等方面的关键问题,并提出了相应的解决方案和展望。
|
SQL 存储 Java
Mybatis之自定义映射resultMap
【1月更文挑战第3天】 一、resultMap处理字段和属性的映射关系 二、多对一映射处理 1、级联方式处理映射关系 2、使用association处理映射关系 3、分步查询 1. 查询员工信息 2. 查询部门信息 三、一对多映射处理 1、collection 2、分步查询 3. 查询部门信息 4. 根据部门id查询部门中的所有员工 四、延迟加载
334 2
Mybatis之自定义映射resultMap
|
Java 数据库连接 API
Spring系列(一)之基础介绍及IoC的三种注入方式
Spring系列(一)之基础介绍及IoC的三种注入方式