prototype 是遗产,proto 是族谱:一文吃透 JS 原型链

JavaScript 原型系统核心解析

引言:JS 面向对象的独特哲学

在传统的面向对象语言中,对象是基于 "类"(Class) 创建的

  • 类是模板,定义了属性和方法;
  • 对象是类的实例 ,通过 new Class() 生成;
  • 继承是"血缘关系"------子类继承父类,结构静态且明确。

而 JavaScript 采用 原型式面向对象(Prototype-based OOP)

  • 没有"类"的概念 (ES6 的 class 只是语法糖);
  • 对象直接从 另一个对象(原型) 继承属性和方法;
  • 每个对象都有一个内部链接([[Prototype]]),指向它的原型;
  • 继承是"委托关系"------找不到属性时,自动向原型查找。

构造函数与实例创建

js 复制代码
function Person(name,age){
    this.name =name;
    this.age =age;
}
Person.prototype.speci = '人类'
  • new 的作用:创建一个新的对象,并返回
  • this 的指向:this会在new这个新对象时绑定到新创建的对象上
  • 实例私有属性的定义:在 JavaScript 中,实例私有属性是指直接定义在对象自身上、不被原型共享的属性。在上面的代码中,name和age是私有的,而speci则是所有对象实例所共有的属性

对象字面量的创建就像是传统工匠

构造函数就像是一个模具,可以快速的创建的出相同的对象实例

prototype 是什么?

  • 定义:他是所有函数独有的一个特殊属性
  • 作用:用来存储所有实例对象所共有的属性和方法
  • 结构:默认包含constructor,指向该实例原型的构造函数,__proto__指向对象原型
js 复制代码
{
constructor:...
__proto__:....
}

实例如何访问原型? __ proto __ 与原型链

js 复制代码
function Person(name,age){
    this.name =name;
    this.age =age;
}
Person.prototype.speci = '人类'
const person1 =new Person('张三',18);
const person2 =new Person('李四',19);
console.log(person1);
console.log(person1.__proto__);
  • __ proto__是什么 :是 JavaScript 中每个对象都具有的一个内部属性 ,它指向该对象的原型(prototype) ,也就是这个对象"从哪里继承属性和方法"。 等我们通过__proto__去进行访问时,可以得到该实例对象所继承的原型

但是,值得注意的是__proto__在现在并不推荐使用,__proto__[[Prototype]] 的 getter/setter。 而在现在的编程当中,我们更推荐使用Object.getPrototypeOfObject.setPrototypeOf 来取代 __proto__ 去 get/set 原型

prototype 与 __proto__的本质区别

  • 归属的对象不同:prototype是只有函数才具有的属性,而__proto__是所有对象都具有的一个内部属性
  • 所代表的含义不同:prototype是对象的原型,它包含了该构造函数所有实例对象的共有属性和方法,是一个对象,而__proto__ 是指向实例对象原型的链路,指向了实例对象的构造函数的prototype
  • 二者的关系 :如图,实例.proto ==构造函数.prototype

覆盖 prototype 的常见问题

  • constructor 丢失 考虑下面这段代码:
js 复制代码
  function Person(name,age){
            this.name =name;
            this.age =age;
        }
        Person.prototype={
            speci:'人类',
        }
        var person1 =new Person('张三',18);
        console.log(Person.prototype.constructor == Person);
        

打印的结果是什么 ? 你以为是ture吗?实际上却是false!!!

按照一贯的思维来说,Person对象原型的构造函数确实是Person(),但是观察上述代码,我们修改了prototype,并把他赋值了一个普通的对象,但是我们没有重写构造函数 ,这就导致了实际上person.protope已经变成了Object的原型,所以才会导致false的结果

  • 如何正确重写并修复
    • 方案一:重写构造函数
      在修改prototype时在代码中添加constructor:Person;就能重新告诉引擎当前这个实例的原型依旧是Person
    • 方案二:只添加,不覆盖
      例如:
ini 复制代码
Person.prototype.speci='人类';

这样的操作只会在原有的基础上扩展出speci属性,而不会覆盖掉整个的prototype

函数也是对象:FunctionObject 的关系

  • Person.__proto__ === Function.prototype
    考虑下面这段代码:
js 复制代码
 function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        Person.prototype = {
            speci:"人类",
            
        }
        // 对象的原型对象是?
        console.log(Object.getPrototypeOf(Person)==Person.prototype);//flase
        

我的理解

Person是一个构造函数(所以他是Function的实例),它的__proto__指向的是函数的的原型,也就是Function.prototype

Function则是Object的一个实例,它的__proto__指向了Object.prototype

  • 原型链的完整路径
javascript 复制代码
Person
  └─ __proto__ → Function.prototype
                   └─ __proto__ → Object.prototype
                                    └─ __proto__ → null

原型链的顶点:Object.prototype

所有对象都沿着原型链最终指向Object.prototype,而Object[[Prototype]]指向的null,这意味着它是所有对象的终点

理解 JS 原型的关键要点

终极记忆法:
prototype是父亲给后代的实际财富,它代表着所有后代所实际拥有的共有属性和方法
__proto__更像是一份DNA的鉴定,它指明了实例对象的父亲或者说祖宗是谁,我继承的谁的财富

虽然在现代的javaScript我们可以使用class语法,但它实际上只是有一个语法糖,它的底层原理依然是构造函数 + prototype,清晰的理解prototype和__proto__有助于后面更高效的学习JS

相关推荐
倾墨19 分钟前
Bytebot源码学习
前端
用户938169125536023 分钟前
VUE3项目--集成Sass
前端
S***H28333 分钟前
Vue语音识别案例
前端·vue.js·语音识别
啦啦9118861 小时前
【版本更新】Edge 浏览器 v142.0.3595.94 绿色增强版+官方安装包
前端·edge
蚂蚁集团数据体验技术1 小时前
一个可以补充 Mermaid 的可视化组件库 Infographic
前端·javascript·llm
LQW_home2 小时前
前端展示 接受springboot Flux数据demo
前端·css·css3
q***d1732 小时前
前端增强现实案例
前端·ar
IT_陈寒2 小时前
Vite 3.0 重磅升级:5个你必须掌握的优化技巧和实战应用
前端·人工智能·后端
JarvanMo2 小时前
Flutter 3.38 + Firebase:2025 年开发者必看的新变化
前端