详细解读JS原型链

JavaScript012

详细解读JS原型链,第1张

举例如下:

1.普通JS对象,obj1的构造函数是Object,所以obj1原型链如下

2.普通函数,fun1的构造函数是Function,所以fun1的原型链如下

3.构造函数生成的实例对象,person1的原型链如下

上面说的都是原型链的默认指向,但是原型链的指向其实是可以手动更改,或者通过其他方式更改的,下篇再总结一下更改原型链的方法。

1.、js的对象可以分为函数对象和普通对象,每个对象都有 proto 属性,但是只有函数对象才有prototype属性(prototype=原型对象)

2、Object是最高级的函数对象,Function、Array、RegExp、Date、Boolean、Number、String等等是第二高级的函数对象,我们自己手写的function是再低一级的函数对象

3、 proto 是一个对象,它指向某一个prototype,并且有2个属性:constructor和 proto

先来看第一种写法

我们声明了一个构造函数Person,并为它的2个实例都定义了sayName方法

如果我们希望所有的实例都有sayName方法,减少重复的定义

可以像下面这么写:(第二种写法)

我们在构造函数Person中定义了一个数据成员和一个函数成员

每当我们创造一个新的Person实例时,都会在内存中单独为它分配私有的空间

也就是说,p1和p2都有自己的name属性和sayName方法

这个name属性和sayName方法是实例自己私有的

在控制台直接打印p1得到如下结果:(第一、第二种写法是等价的)

再来看第三种写法:

这样的话,每个Person实例依然都会有name属性和sayName方法

不同的地方在哪呢?

在控制台直接打印p1得到如下结果:

不难发现,当前这种写法是将sayName定义在Person的原型对象上的

也就是说,sayName方法不再是每个Person实例私有的,而是所有Person实例共享的

这样做 既不影响每个实例打印/输出自己的name值,又可以节省内存

还是相同的例子

当我们在程序中调用 p1.sayName方法时,编译器会 沿着原型链(自底向上) 地寻找原型对象上是否有这个方法

如上图所示,编译器会先从p1自身(最外层)开始找

然后去 p1. proto (即Person的原型对象 = 外层红色方框)里面找

最后再到 p1. proto . proto (即Object的原型对象 = 内层红色方框)里面找

首先,我们先来看一张图:

这个绕来绕去的线是不是很恶心呢?那就先别管这些了,等整个逻辑我们梳理完就自然而然的会明白了~

一、 什么是原型链?

二、原型指针

    我们知道了原型的概念,接下来我们就照着上面的图来具体分析一下原型的指针;中间最上面蓝色模块标注的构造函数Foo, 里面有两个属性: _proto_ 和 prototype, 这两个很容易使人混淆,先说说prototype:

prototype:

    prototype属性,它是 函数所独有的 ,它是从 一个函数指向一个对象 。它的含义是 函数的原型对象 ,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)

__proto__:

    __proto__ 是原型链查询中实际用到的,它总是指向 prototype,换句话说就是指向构造函数的原型对象,它是 对象独有的。 注意,为什么Foo构造也有这个属性呢,因为再js的宇宙里万物皆对象,包括函数;

根据以上的概括我们能知道Foo构造函数_proto_指向的是他的构造函数的原型对象,它的构造函数是Function, 也就是说Foo的_proto_指向Function.prototype,  我们再看到左边绿色的a和b函数的_proto_指像的是Foo.prototype,因为他们是通过 new Foo实例化出来的,它们的构造函数就是Foo(), 即a._proto_ = Foo.prototype;  接着我们来看看最右边紫色的模块Function.prororype, 它的_proto_指针指向的是Object.prototype,Object._proto_又为null.。于是我们就可以得出:在原型链中的指向是,函数 → 构造行数  → Function.prototype → Object.protype → null

constructor:

    我们看到途中最中间灰色模块有一个constructor属性,这个又是做什么用的呢?

    此外,我们还可以使用constructor属性,所有的实例对象都可以访问constructor属性,constructor属性是创建实例对象的函数的引用。我们可以使用constructor属性验证实例的原型类型(与操作符instanceof非常类似)。

    由于constructor属性仅仅是原始构造函数的引用,因此我们可以使用该属性创建新的对象,如:

    

    通过第一个对象实例化对象的constuctor方法创建第2个实例化对象,说明创建的新对象ninja2 是Ninja的实例,由于ninja和ninja2不是同一个对象可以得出它们是两个截然不同的实例;

结论:

        1、__proto__ 是原型链查询中实际用到的,它总是指向 prototype;

        2、prototype 是函数所独有的 , 在定义构造函数时自动创建,它总是被 __proto__ 所指。

所有对象都有__proto__属性,函数这个特殊对象除了具有__proto__属性,还有特有的原型属性prototype。prototype对象默认有两个属性,constructor属性和__proto__属性。prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式。constructor,这个属性包含了一个指针,指回原构造函数。