JavaScript原型&原型链到底是什么

先来张图

如果这张图能看懂的话,就不需要浪费时间往下看了,直接毕业。


有点蒙的话没关系,把下面内容搞懂之后,再回头看看。

先了解两个概念

什么是构造函数?

        答:如果一个函数通过new 关键字调用的话,这个函数就是构造函数。并且我们规定首字母得大写(当然首字母不大写也没问题但不规范)。通过new关键字调用函数会返回一个对象。记住这一点,很重要。

什么是对象?

        在我刚学js的时候,我常听到的一句话就是在js中万物皆对象,后来看到一本书《你不知道的js》,才明白这种说法其实是不对的。

        对象是JavaScript的核心概念之一,是一种最常用的数据类型。数据类型可以分为两大类:简单基本类型和复杂基本类型

        简单基本类型(string、number、boolean、null、undefined)本身并不是对象。null有时会被当成一种对象类型,但是这其实是语言本身的一个bug,即对null执行typeof null时会返回字符串”object“。

        原理是这样的,不同的对象在底层都表示为二进制,在JavaScript中二进制前三位都为0的话会被判断为object类型,null的二进制表示是全0,自然前三位也是0,所以执行typeof时会返回“object”。

        js中有许多特殊的对象子类型,我们称之为复杂基本类型。比如函数就是对象的一个子类型(从技术角度来说就是”可调用对象“),包括数组也是对象的一种类型。


关于原型中的几个关键属性,如下:

一、prototype

        js中所有 函数 都有一个属性 prototype,指向一个对象,这个对象就是该函数的原型对象

        比如我写了一个函数:function test(){}

        想查看或者操作这个函数的原型,则通过test.prototype得到该函数的原型

        当然,js内置的函数对象的原型也同理,例如,Object.prototype,Function.prototype,Array.prototype,Number.prototype等等。。分别是这些函数的原型对象        

 二、__proto__

        js中所有 对象 (除了null)(包括原型对象,也就是某函数的prototype) 都具有一个__proto__属性,该属性指向了该对象构造函数的原型。

        注意!不是该对象的原型,普通对象自身没有原型,是构造函数的原型,哪个函数构造的该对象,则指向哪个函数的原型

        比如我定义了一个对象:let obj = {} ,或者更直观定义 let obj = new Object();

        想查看或者操作这个对象的原型,则可以通过obj.__proto__得到该对象构造函数的原型。

        因此,obj.__proto__ 就是 Object.prototype。他们指向的是同一个原型对象

        同理,随便定义一个数组对象,let arr = [], arr.__proto__ 就是Array.prototype

        再同理,随便定义一个自定义函数对象,function test(){}, test.__proto__ 就是Function.prototype(这里应该能够发现,函数对象自己也有原型,这是与其他对象不同的地方,别忘了,函数是可以作为构造函数,构建对象的哟)

        知道了这些有什么用呢,可以看下面这波操作

        假如我们在Object.prototype上定义了一个属性,Object.prototype.makabaka = "玛卡巴卡"

        则我们定义的所有对象,let obj1 = {} ; let obj2 = {} ,都可以通过调用__proto__.makabaka,拿到”玛卡巴卡“

        例如:

                obj1.__proto__.makabaka === "玛卡巴卡"

                obj2.__proto__.makabaka === "玛卡巴卡"

        你注意啦!这里有个神奇操作,__proto__是可以不写的。

                obj1.makabaka === "玛卡巴卡"

                obj2.makabaka === "玛卡巴卡"

        如果不写__proto__的话js会先在obj1自身上找一下有没有makabaka属性,如果js发现obj1上没有makabaka属性,就会通过obj1.__proto__.makabaka,去obj1的原型上找这个属性,这就是传说中的原型链。

三、constructor

        js中所有 原型对象 (prototype)都有一个constructor属性,该属性指向了原型对象的构造函数。

        既然可以通过函数得到原型,那么也可以通过原型得到构造函数

        还是写一个函数:function test(){}

        他的原型对象为 test.prototype,所以test.prototype.constructor 的结果为 test(){}

        我们使用更多的可能是一个普通对象调用constructor,来通过这个属性判断,是否是某个函数构建的。

        例如:let a = []; a.constucotr 的结果为Array()

        注意了,数组对象a本身是没有constucotr属性的,这个属性是在原型上的,上面讲过了,如果对象本身没有该属性,会获取a.__proto__.constucotr,得到该原型对象的constucotr属性,进一步得到构造函数。

        不过由于prototype是可以随时修改的,不太安全,也不建议用这个来判断,建议使用instanceof来判断某个对象跟构造函数的关系。这里只需要了解他的作用就好。


我们再来归类总结一下,通过几个实例再来理解一下。

一、所有对象(包括Function),他们的__proto__属性指向的是Function.prototype(Function函数的原型)。(函数也是对象,因此所有对象都有__proto__属性)

// 下面判断结果都为true,(Object、Number、String等内置对象,都是通过Function构造生成的)
Object.__proto__ === Function.prototype 
Number.__proto__ === Function.prototype
String.__proto__ === Function.prototype
Array.__proto__ === Function.prototype
Function.__proto__ === Function.prototype
function test() {}
test.__proto__ === Function.prototype
....

二、所有对象原型(prototype)的__proto__都是指向Object.prototype的

// 下面判断结果都为true
Function.prototype.__proto__ === Object.prototype
Number.prototype.__proto__ === Object.prototype
String.prototype.__proto__ === Object.prototype
Array.prototype.__proto__ === Object.prototype
function test() {}
test.prototype.__proto__ === Object.prototype
...

三、自定义对象实例的__proto__指向构造函数的原型(谁创造的我,我就指向谁的原型)

// 下面判断结果都为true
let obj = {};    // let obj = new Object()
obj.__proto__ === Object.prototype

function Test(){}
let obj2 = new Test()
obj2.__proto__ === Test.prototype

let arr = [];    //let arr = new Array()
arr.__proto__ === Array.prototype
...

四、Object.prototype.__proto__ 指向null

Object.prototype.__proto__ === null

将原型与原型链方面的知识凝结成一张图:

最后,在上面所有例子的基础上,最后再来看一下完整原型链查找过程

Object.prototype.makabaka = "玛卡巴卡"
function Test(){}
let obj = new Test()

obj.makabaka === "玛卡巴卡"

// 查找路径如下
obj.makabaka    // 在obj上没找到玛卡巴卡

// obj.__proto__ === Test.prototype
obj.__proto__.makabaka    // 在Test.prototype 上没找到玛卡巴卡

// obj.__proto__.__proto__ === Test.prototype.__proto__ === Object.prototype
obj.__proto__.__proto__.makabaka    // 终于在Object.prototype上找到了玛卡巴卡

 相信看到这里再看文章开头的图片,就会一目了然了

当然,关于原型链这部分知识,远不止本文所讲这些,像js中类的概念就是在原型的基础上建立的。

参考文档:JavaScript之彻底理解原型与原型链 - 掘金

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值