JS原型链那些事

本文最后更新于:2023年8月7日 凌晨

在使用JavaScript的时候,可能会经常看见控制台输出信息里有__proto__或者prototype;又或者是你在使用字符串的时候用到一些方法,你会好奇它究竟是写在哪里;又或者是你对JavaScript里面没有类的产生疑惑

几种函数

作为普通函数

1
2
3
4
//定义函数
function foo(){}
//调用函数
foo();

作为构造函数

当一个函数被用来创建新对象的时候,我们会叫他为构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
//按照惯例,作为构造函数的函数名首字母需要大写
function Foo(){}
const obj = new Foo();

//当我们使用new操作符的时候,实际上会进行下面几个步骤
//创建一个新的对象
const obj = {};
//原型链连接/对象关联
obj.__proto__ = Foo.prototype;
//把新对象作为函数的上下文
Foo.apply(obj, arguments);
//Foo不返回对象,所以返回新对象
return obj;

作为对象

1
2
3
function foo(){}
foo.name = "tom";
foo['age'] = 20;

以上这三种使用方式中,以普通函数来调用的方式十分常见,不再赘述。下面要讲的就是当一个函数被作为构造函数来使用和被作为对象来使用的时候,分别是什么样的,以及它们之间与原型链的关系是什么样的

__proto__prototype

对象有一个特殊的__proto__的内置属性,其实它就是对其他对象的引用。几乎所有的对象在创建这个属性的时候都会被赋予一个非空的值。需要注意的是,这个属性可以为空,虽然少见。它是实现原型链的关键,因为它会指向构造函数的原型对象,即 prototype 属性上的对象,而构造函数原型对象上的__proto__ 又指向上一级,即:构造函数.prototype.__proto__ === 上一级构造函数.prototype,以此类推层层往上,就形成了我们所说的原型链。

prototype函数特有的一个属性,它是构造函数的原型对象。JavaScript 是一种基于原型的语言,每个对象都拥有一个原型对象,对象以其原型为模板,从原型继承方法和属性。

先来看看下面的代码

1
2
3
4
var obj = {
a = 2;
};
obj.a; //2

相信大家都能一下子把结果说出来,结果是2

我们再来看看

1
2
3
4
5
6
var anotherObj = {
a = 2;
};
//创建一个关联到anotherObj的对象
var obj = Object.create(anotherObj);
obj.a; //2

显然,此时obj上并没有a属性,那么这个值是哪里来的?这就是我们即将要说的原型链

当你试图引用对象的属性时会触发get操作:

  1. 对于默认的get操作,第一步是检查对象本身是否有这个属性,如果有的话就直接使用。
  2. 但是如果a属性不在对象本身,就会继续访问原型链(???.__proto__)找到这个属性直至跑完整条原型链

哪里是原型链的尽头

由于所有普通(内置)对象都源于Object.prototype,所以他们的原型链最终都会指向Object.prototype。而Object.prototype.__proto__null,这不就断了吗。

函数和对象

基本

首先我们要知道下面几个关系

  1. 所有的原型对象都是由Object创建出来的
  2. 所有函数对象都是由Function创建出来的。Function是一个构造函数,通过new调用可以生成函数对象,即我们一般自定义的那种函数。所以Fucntion这个构造函数的prototype是所有函数的__proto__

另外,__proto__是对象的属性;prototype是函数的属性

1
2
3
Function.prototype.__proto__ === Object.prototype

Object.__proto__ === Function.prototype

分析以上两行代码:

  1. 看到Function.prototype,说明此时Function是当成构造函数来使用的。前面说过构造函数都由Object创建出来的
  2. 看到Object.__proto__,说明此时Object是被当前对象来使用的,前面说过函数对象都是由Function创建出来的

特例

Object和Function既是对象,又是函数,两者内部同时含有__proto__prototype属性,他们关系较为复杂,以下做归纳。

Function.prototype指向”内置函数“。而Object.prototype指向”根源对象“

1
2
3
4
5
6
Object.__proto__ === Function.prototype //true
Object.__proto__ === Function.__proto__//true
Object.prototype === Function.prototype.__proto__ // true
//因此
Function instanceof Object //true
Object instanceof Function //true

image-20200922205901071

梳理

proto

  1. 函数prototype属性指向原型对象

  2. 所有的原型对象都是由Object创建出来的

    • Foo.prototype.__proto__ === Object.prototype
    • Function.prototype.__proto__ === Object.prototype
  3. 所有函数对象都是由Function创建出来的

    • Foo.__proto__ === Function.prototype
    • Function.__proto__ === Function.prototype
    • Object.__proto__ === Function.prototype
  4. 普通对象指向构造函数的原型

    • Obj.__proto__ === Foo.prototype

::: right

部分内容参考自不要做切图仔dingpanqing3307山水有轻音

:::


JS原型链那些事
https://chanx.tech/2020/a030ee3fe414/
作者
ischanx
发布于
2020年9月22日
更新于
2023年8月7日
许可协议