JavaScript中的原型和原型链

本文详细解释了JavaScript中的__proto__,prototype和constructor的概念,以及它们在原型链中的作用。通过示例代码和图解分析了Animal构造函数及其原型对象Animal.prototype的关系,强调了每个对象如何通过__proto__链接到原型链,并介绍了new操作符的工作原理。
摘要由CSDN通过智能技术生成

在 JavaScript 中,每个对象都具有一个隐式原型__proto__,这个原型还能有他自己的原型,以此类推,就会形成一条原型链。要理解 JavaScript 中的原型和原型链,清晰知道__proto__prototype以及constructor概念至关重要。

本文围绕以下代码进行原型及原型链讲解

// 函数
function Animal(name){
	this.name = name
}

// 在Animal原型对象上加入 animalSay 方法
Animal.prototype.animalSay = function(say){
	console.log(`${this.name}说:${say}`)
}

let cat = new Animal('小猫')
let dog = new Animal('小狗')

console.log(cat.__proto__)

概念

__proto__ 隐式原型 ⬇

__proto__是原型链查询实际用到的,它总是指向prototype原型对象,也就是构造函数的原型对象。由构造函数创建的对象的属性,不是对象本身的属性。

在一般的打印中时常可以见到[[prototype]],它指向该对象的原型,与__proto__的指向一致,__proto__之所以叫 隐式原型,是因为它能间接访问对象的原型,而不需要显式地使用 Object.getPrototypeOf() 方法或直接访问 [[Prototype]] 属性。

prototype 显式原型(原型对象) ⬇

prototype函数独有的属性,它总是从一个函数指向一个对象,含义就是某个函数的原型对象。说白了就是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法,我们把这个的一个对象称作原型对象。

constructor 构造 ⬇

constructor构造,它存在于原型对象中,每个函数都有一个原型对象,原型对象中又有个constructor属性,指向创建函数的本身(谁构造的它就指向谁)。

图解关系

JS原型链图一

先看看Animal.prototype内部关系 ⬇
请添加图片描述
Animal.prototype内部存在__proto__,说明原型对象prototype也是经过某个函数构造出来的(后面可以知道是Object函数)
请添加图片描述
继续往深处探究,当前位置为Object.prototype中,对原型链有过了解的朋友可能已经知道,Object.prototype.__proto__ === null是的没错,Object的原型对象位于原型链的顶部,它的__proto__指向null,所以在上图中打开__proto__属性一看,理应是一个null,事实真是如此吗?。

请添加图片描述
好嘛,博主骗人…先别急,这里是通过打印 console.log(car.__proto__)慢慢展开显示的结果。我们可以尝试直接打印console.log(cat.__proto__.__proto__)或者console.log(Object.prototype),可看下图 ⬇
请添加图片描述
???这算什么事?直接打印和展开观察的结果不一致,人要晕了。其实这里跟ECMAScript 规范有关,本篇文章我们只需要确定 Object.prototype.__proto__ === null就好了。另外感兴趣的朋友可以看看这篇文章,参杂一些个人推测,有问题的地方还望指出,篇幅不长 ➡ 看我胡说八道

总结

  1. 其实从图解关系的第一幅图可以发现,除了null,每个实例,每个原型对象,每个函数都有一个__proto__隐式原型属性,它总是指向一个原型对象prototype,就像文章开头说的一个个__proto__将原型对象串联起来,就是一条原型链。
  2. __proto__是由构造函数创建出来的对象的属性,除了null,每个实例,每个原型对象,每个函数都有一个__proto__隐式原型属性,这也侧面说明,每个实例,每个原型对象,每个函数都不是凭空出现的无中生有的。举个例子,看图一Object函数__proto__指向Function的原型对象,说明Object这种 js 内部函数是由Function函数创造的,而Function函数中也有__proto__,输出验证它指向自己的原型对象,创建它的是它自己,是不是有点悖论,但事实是如此。
  3. 在图解关系部分,我也是着重介绍了图一左半部分的原型关系,还挖出个坑来,尝试打印以下,把图一的右半部分管理理顺,会对你对原型链的理解大有帮助。

原型链一般会运用到我们查找特定属性的时候,这个特定属性可以是普通的属性,也可以是数组啊,或是函数,或是方法,他就会依据__proto__去这个对象里面去找,源对象里面没找到,就会去源对象的原型对象里面去找,还没找到,就会去原型对象的原型对象去找,以此类推,这个操作被委托在整个原型链上,这个就是原型链了

拓展 new操作符

面试有可能要知道…

在上面原型链介绍的时候,就会有new这个概念,new这个操作符,就是给构造函数创建一个实例。函数有个特性,当函数返回一个 原始数据 的时候,这个返回值是没有作用的,但当这个返回值是 一个对象 的时候,这个返回值又会被正常使用。所以说,new操作符在创建实例的时候就会有这方面的处理。

那么总的来说new做了什么工作呢?⬇

  1. 创建了一个实例
  2. 将实例的proto和构造函数的原型对象连接起来
  3. 将构造函数的this绑定到新实例中
  4. 根据构造函数返回类型作判断,如果是原始值就忽略,是对象就是引用
// 手写 new 操作符
/**
* @param func 构造函数
* @param ...args 其他参数,拓展运算符
*/
function myNew(func,...args){
    // 创建了一个实例
    const newObj = {}
    // 将实例的proto和构造函数的原型对象连接起来
    newObj.__proto__ = func.prototype
    // 将构造函数的this绑定到新实例中
    let result = func.apply(newObj,args)
    // 根据构造函数返回类型作判断,如果是原始值就忽略,是对象就是引用
    return result typeof Object ? result : newObj
}

文章有问题之处还望评论斧正!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值