专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

图解JavaScript中的原型链及内存表现

1、 什么是原型链

要理解原型链,绕不开constructor、prototype、__proto__这几个核心的知识点,它们的关系如下:

75_1.png上面的图是一个最简单的原型链,先有一个直观的认识。下面将围绕上面3个点一步步对原型链抽丝剥茧,最后在来总结究竟什么是原型链,自然就清晰了。

2、 课前预习

再正式进入主题之前,先了解几个知识点

2.1 函数对象与普通对象

JavaScript 中,万物皆对象,但对象也是有区别的。分为普通对象和函数对象,Object 、Function 是 JS 自带的函数对象

  • 函数对象
//f1,f2,归根结底都是通过 new Function()的方式进行创建的
function f1(){};//console.log(typeof f1)==function 
var f2 = function(){};//同上
var f3 = new Function('str','console.log(str)');//同上

  • 普通对象
var o1 = {}; // console.log(typeof o1) == object 
var o2 = new Object();//同上
var o3 = new f1();//同上

简单的说,凡是使用 function 关键字或 Function 构造函数创建的对象都是函数对象,其他的都是普通对象

75_2.png

2.2 几个专业术语

  • 显式原型:prototype
  • 隐式原型:__ proto __
  • 原型对象:每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象

3、 prototype和contructor

  • prototype指向函数的原型对象,只有函数或者说函数对象才拥有该属性。
  • contructor指向原型对象的构造函数。

可能很多人对什么是原型对象比较迷惑,个人是按照下面的模版记得:

原型对象 = 构造函数名.prototype

//Person.prototype就是原型对象
function Person() {}
Person.prototype = {
   name:  'Zaxlct',
   sayName: function() {
   }
}

3.1 prototype和contructor关系

主要从三个纬度来分析:自定义的构造函数(Person)、Object函数、Function函数

75_3.png

// 可以思考一下打印结果
function Person() {}
console.log(Person.prototype)//[object Object]
console.log(Person.prototype.constructor)//function Person() {}

3.2 prototype创建时机

在定义函数时自动添加的, 默认值是一个空Object对象

//定义构造函数
function Fn() {   // 内部语句: this.prototype = {}
}

3.3 prototype作用

  • 通过给 Person.prototype 设置属性和方法之后,Person 的实例都会继承相应的属性和方法,所有的实列例共享一份,不会开辟新的空间。
  • prototype用来实现基于原型的继承与属性的共享

4、 proto

JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象。

由于js中是没有类的概念,而为了实现继承,通过 __ proto __ 将对象和原型联系起来组成原型链,就可以让对象访问到不属于自己的属性。

4.1 函数和对象的关系

所有函数都是Function的实例(包含Function),所有构造器都继承了Function.prototype的属性及方法。

75_4.png

4.2 原型对象间的关系

所有的原型对象__proto__最终指向了Object.prototype(除了Object.prototype的__proto__之外)。

js原型链最终指向的是Object原型对象

75_5.png

4.3 proto创建时机

对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值

function Fn() {  } 
// 内部语句: this.__proto__ = Fn.prototype
var fn = new Fn()  

4.4 总结

  • 当你new一个构造函数的时候,创建一个函数实例,那么 『 函数实例.__ proto __ === 该构造函数.prototype 』
  • 所有的函数都是由Function构造出来的,那么 『被构造出来的其他函数.__ proto __ === Function.prototype 』
  • 所有的构造函数的原型对象都是由Object构造出来的,那么 『所有的构造函数.prototype.__ proto __ === Object.prototype 』

5、 完整原型链结构图

75_6.png

  • 所有的实例对象都有__proto__属性, 它指向的就是原型对象
  • 这样通过__proto__属性就形成了一个链的结构—->原型链
  • 当查找对象内部的属性方法时, js引擎自动沿着这个原型链查找
  • 当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作

6、 原型链的查找和内存表现

 function Person() {
    this.test1 = function () {
      console.log('test1()')
    }
  }
  console.log(Person.prototype)
  Person.prototype.test2 = function () {
    console.log('test2()')
  }
  Object.prototype.test3 = function () {
    console.log('test3()')
  }
  const  personObj = new Person()
  personObj.test1()//test1
  personObj.test2()//test2
  personObj.test3()//test3

对应到内存中的原型链如下图:

75_7.png

7、总结

  • 显式原型与隐式原型的关系
    • 函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
    • 实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
    • 原型对象即为当前实例对象的父对象
  • 原型链的作用
    • 避免相同函数重复声明,减少空间占用,节省内存
    • 实现继承
    • 构造函数的所有实例都可以访问构造函数原型中的方法和属性

PS:初入前端,有问题欢迎指出和探讨

参考文章:

75_8.png

文章永久链接:https://tech.souyunku.com/31633

未经允许不得转载:搜云库技术团队 » 图解JavaScript中的原型链及内存表现

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们