弄懂js的原型和原型链

一直以来,js的原型和原型链这个概念性的问题一直讲不清,今天看了一篇较好的文章跟着走一遍之后发现有那么点明白了,下面是根据自己的理解再结合网上的文章做出的一些总结,有问题的地方随时纠正:smile:

四个原则

  1. 引用类型都具有对象特性, 即可自由扩展属性.
  2. 引用类型都有一个隐式原型__proto__属性, 属性值是一个普通的对象
  3. 引用类型,隐式原型__proto__的属性值指向它的构造函数的显示原型prototype属性值
  4. 当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性, 那么它会去它的隐式原型__proto__(也就是它的构造函数的显示原型prototype)中寻找

引用类型: Object,Array,Function,Date,RegExp. 这里我们姑且称__proto__ 为隐式原型

规则一

引用类型,都具有对象属性,即可自由扩展属性:

js 复制代码
const obj = {};
const arr = [];
const fn = function() {}

obj.a = 1;
arr.a = 1;
fn.a = 1;

console.log(obj.a) // 1
console.log(arr.a) // 1
console.log(fn.a) // 1

规则二

引用类型都有一个隐式原型__proto__属性, 这个属性值是一个普通的对象

js 复制代码
const obj = {}
const arr = []
const fn = function() {}

console.log(obj.__proto__)
console.log(arr.__proto__)
console.log(fn.__proto__)

规则三

引用类型, 隐式原型__proto__的属性值指向它的构造函数的显示原型prototype的属性值

js 复制代码
const obj = {}
const arr = []
const fn = function() {}

obj.__proto__ === Object.prototype // true
arr.__proto__ === Array.prototype // true
fn.__proto__ === Funtion.prototype // true

规则四

当你试图得到一个对象的某个属性时, 如果这个对象本身没有这个属性,那么它会去他的隐式原型__proto__(也就是它的构造函数的显示原型prototype)中寻找

js 复制代码
const obj = {a: 1}
obj.toString
// ƒ toString() { [native code] }

首先, obj对象并没有toString属性, 之所以能够获取到toString属性, 是遵循了第四条规则,从它的构造函数Objectprototype中去获取

一个特征

js 复制代码
function Person(name) {
    this.name = name;
    // return this;
}

const nick = new Person('nick');

nick.toString
// ƒ toString() { [native code] }

按理说, nickPerson构造函数生成的实例, 而 Personprototype并没有toString方法, 为什么nick能获取toString方法呢?

这里就引出了原型链的概念了,nick实例先从自身出发检讨自己, 发现并没有toSTring方法.找不到就一直往上走,找Person构造函数的prototype属性,还是没找到.而prototype也是一个对象嘛,那么对象的构造函数就是Object,所以就找到了Object.prototype下的toSting方法.这种寻找的过程就形成了原型链的概念, 这就是我理解的原型链的一个过程.

Javascript中万物皆对象.从上述情况来看,好像是这么个道理

一张图片(原型链图解)

最后一个null ,设计上是为了避免死循环而是设置的, Object.prototype的隐式原型(Object.prototype.__proto__)指向null

一个方法

instanceof 运算符用于测试构造函数的prototype属性是否出现在对象原型链中的任何位置.

instanceof 的简易手写板如下

js 复制代码
// 变量R的原型 存在于 变量L 的原型链上
function instance_of (L, R) {
    // 验证如果为基本数据类型就直接返回false
    const baseType = ['string', 'number', 'boolean', 'undefined', 'symbol'];
    
    if (baseType.includes(typeof(L))) {
        return false;
    }
    
    // 取R的显示原型
    let RP = R.prototype;
    // 取L的隐式原型
    L = L.__proto__;
    
    while(true) {
        // 找到最顶层
        if (L === null) {
            return false
        }
        // 严格相等
        if (L === RP) {
            return true;
        }
        // 没找到继续向上一下原型链查找
        L = L.__proto__;
    }
}

我们再来看下面这段代码:

js 复制代码
function Foo(name) {
    this.name = name
}

var f = new Foo('nick')

f instanceof Foo // true
f instanceof Object // true

上述代码判断流程大致如下:

  1. f instanceof Foo : f的隐式原型__proto__Foo.prototype 是相等的,所以返回true
  2. f instanceof Object: f的隐式原型__proto__Object.prototype不等, 所以继续往上走, f的隐式原型__proto__指向Foo.prototype, 所以继续使用Foo.prototype.__proto__去对比Object.prototype, 这会就相等了, 因为Foo.prototype就是一个普通对象

再次验证万物皆对象

总结

通过四个特征,一个例子,一张图片,一个方法,应该对原型和原型链的关系有了大概的认证,我的认知就是: 原型链就是一个过程,原型就是原型链这个过程中的一个单位,贯穿整个原型链.

参考链接

相关推荐
烬羽2 小时前
React Router v7 完全指南:一个 Demo 吃透前端路由
javascript·react.js·前端框架
烬羽2 小时前
从"整个页面刷新"到"丝滑切换":手写一个 HashRouter 彻底搞懂前端路由
javascript·前端框架·全栈
用户938515635072 小时前
从零理解 React Router v6:每一个 API 都是怎么工作的
前端·javascript·全栈
用户059540174464 小时前
Redis缓存回滚踩坑实录:一个配置失误,让我在凌晨3点丢了3000条数据
前端·css
kyriewen5 小时前
别再这样写条件渲染了——你的React组件里藏着这5种定时炸弹
前端·javascript·react.js
IT_陈寒5 小时前
Redis缓存击穿把我坑惨了,原来这样设过期时间才靠谱
前端·人工智能·后端
用户938515635075 小时前
从"坐电梯"到"前端路由"——深入理解 Hash 路由原理
前端·typescript·全栈
梦想CAD控件6 小时前
网页端CAD的图形选择、编辑与夹点操作教程
前端·javascript·node.js
妙码生花6 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十二):管理员权限检查中间件,AI 随意放行预闯大祸
前端·后端·go
月月大王的3D日记6 小时前
Three.js 入门系列(8):六种光源全解析 —— 关灯了,开光!
前端·javascript