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

相关推荐
howcode15 小时前
年度总结——Git提交量戳破了我的副业窘境
前端·后端·程序员
恋猫de小郭15 小时前
OpenAI :你不需要跨平台框架,只需要在 Android 和 iOS 上使用 Codex
android·前端·openai
Moonbeam Community15 小时前
应用爆发,DeFi先行
javascript·ide·web3·区块链·polkadot
2301_7965125215 小时前
使用状态管理、持久化存储或者利用现有的库来辅助React Native鸿蒙跨平台开发开发一个允许用户撤销删除的操作
javascript·react native·react.js
全马必破三15 小时前
浏览器原理知识点总结
前端·浏览器
零Suger15 小时前
React 组件通信
前端·react.js·前端框架
LYFlied15 小时前
【每日算法】 LeetCode 394. 字符串解码
前端·数据结构·算法·leetcode·面试·职场和发展
前端不太难15 小时前
RN Navigation vs Vue Router 的架构对比
javascript·vue.js·架构
硕子鸽16 小时前
UniApp + Dify 实战:详解 SSE 流式响应的解析与前端渲染
前端·uni-app·dify