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.getPrototypeOf和Object.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

函数也是对象:Function 与 Object 的关系

  • 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

相关推荐
zxcvb1531 小时前
远程查看本地文档:一条轻量 Web 文件服务的工程化链路
前端·远程查看文档
niucloud-admin1 小时前
NIUCLOUD PHP SAAS上门家政使用文档:城市管理
java·前端·php
程序员大阳1 小时前
副队长JavaScript教程(50)-DOM:网页开发的起点
javascript·dom·es·节点·概念·document·关系
北岛贰1 小时前
啵啵音乐迎来全新升级!
前端
hkNaruto3 小时前
【AI】基于大模型的AI游戏地图生成、控制方案:AI Native Game Harness 迁移操作手册
前端·人工智能·游戏
IT_陈寒3 小时前
Java序列化踩坑记:对象循环引用让我加班两小时
前端·人工智能·后端
Acro4 小时前
快给macOS 上的Grok Bot换一套皮肤
前端
flash俊杰4 小时前
双引擎 OCR 编排:逐页判定、视觉优先、partial 语义与三层超时
前端
计算机魔术师4 小时前
Reddit 每周被 Google 偷走 100 万次点击,CEO 摊牌了
前端
前端繁华如梦4 小时前
用 React Three Fiber 做一栋能编辑的教学楼数字孪生
前端