JS进阶内容

本文深入探讨JavaScript中的原型链概念,从函数、原型对象的起源讲起,阐述了原型对象的构造函数和[[Prototype]]属性。讨论了函数与普通对象的区别,以及如何通过__proto__和Object.getPrototypeOf()方法查看原型链。文章通过实例揭示了对象如何通过原型链继承方法,并介绍了IE浏览器下__proto__属性的兼容性问题。
摘要由CSDN通过智能技术生成

原型链

原型对象

起源

所有函数在定义时默认生成一个对象,这个对象就是原型对象,函数通过prototype属性可访问这个对象,即原型对象起源于函数,有函数就一定有原型对象。
函数和对应的原型对象

原型对象里面有什么

思考如下代码:

function A(){};
console.log(A.prototype);//A的原型对象

浏览器控制台运行结果
我们定义了一个空的函数A,里面没有任何内容,但是有函数就一定有原型对象,哪怕你是空的函数,也会自动生成一个对应的原型对象。这个原型对象可以通过调用函数的````prototype```属性访问,于是我们通过打印 A.prototype查看A的原型对象,发现其中有两个属性,分别是constructor[[Prototype]]

constructor

A,prototype.constructor
我们通过展开constructor属性可以发现,这个属性指回了函数A

[[prototype]]]

在这里插入图片描述

通过展开[[prototype]]属性可以发现,它指向了一个对象,这个对象其实还是一个原型对象,证据就是它的constructor属性,我们知道这个属性是原型对象指向其对应的函数的属性,所以通过这个属性我们知道,这个原型对象是定义函数Object()时产生的,不过[[prototype]]]属性并不是只会指向Object原型对象,事实上它可以指向任何一个对象,甚至可以指向null,但这需要人为的操作,默认情况下就是指向Object原型对象,同时我们可以看到Object原型对象还有很多其它的方法和属性,但这其实是后面才加上去的,通过A的原型对象我们知道,一个原型对象在出生时只会有两个属性,constructor[[prototype]],他们分别指向对应的函数和另一个对象,[[prototype[[默认指向Object原型对象

为什么Object原型对象没有[[prototype]]属性

眼尖的你应该发现了,Object原型对象只有constructor属性,却没有[[prototype]]属性,这是为什么?
普通情况下,所有对象(包括原型对象)都有[[prototype]]属性,它指向另一个对象,然后这个对象也有[[prototype]]属性,又指向另一个对象,这种形式就构造出一个链式结构,称之为原型链,而原型链的终点,就是Object原型对象,所以Object原型对象没有[[prototype]]属性,因为它是[[prototype]]链的终点。
原型链

函数,原型对象和对象的关系

思考如下代码

function A(){};
console.log(typeof A);//function
console.log(typeof A.prototype);//Object

浏览器控制台运行结果
typeof是一个操作符,可以输出某个变量是什么类型,从中我们可以看到A是函数,A.prototype是对象,但这是为了满足开发中需要知道一个变量是不是函数的需求,而制造的一个bug!还记得吗?js基础数据类型里面原始类型有:数值(Number),字符串(String),布尔值(Boolean),null,undefined,Symbol(ES6引入),引用数据类型有对象(Object),而任何不是原始类型的值,都是引用类型,即对象。函数(function)不属于上面任意一种原始数据类型,所以函数,是引用类型,即函数是对象。
函数,原型对象都是对象,
对象包含原型对象

原型对象和普通对象的区别

思考如下代码

function A(){};
var B = {};
console.log(A.prototype);//有constructor 和 [[Prorotype]]两个属性的一个对象
console.log(B);//只有[[Prototype]]一个属性的对象

原型对象有一个constructor属性
从中我们可以看到,原型对象与普通对象最显著的区别在于constructor属性,该属性指向其对应的函数,一般情况,该属性指向哪个函数,该对象就是哪个函数的原型对象。所以如果看到一个对象有constructor属性,那它十有八九就是一个原型对象。

函数和普通对象的原型

思考如下代码

function A(){};
var B = {};
console.log(A.__proto__);//指向一个函数
console.log(B.__proto__);//指向Object原型对象

函数的原型指向另一个函数
__proto__是一个非标准属性(只有现代浏览器支持,其他运行环境不一定支持,且ES6并没有将其写入正文,只写入了附录中),可以通过这个属性得到当前对象[[prototyep]]属性指向的原型,从中我们可以发现,函数的[[protoype]]属性指向另一个函数,而普通的对象[[protoype]]属性指向Object原型对象。

总结

所有函数在定义时都会自动生成一个伴生的原型对象,该原型对象默认有constructor[[prototype]]两个属性,其中constructor属性指向对应的函数,[[prototype]]属性指向另一个对象,默认情况下这个对象是Object原型对象,而且Object原型对象没有[[prototype]]属性,因为它是原型链的终点。函数和原型对象都属于对象,而原型对象与普通对象最显著的区别就是constructor属性,函数的[[prototype]]属性指向另一个函数。
函数的原型不同于普通对象

深入原型链

思考如下代码:

var a = {};
console.log(a.fn);//=>undifined
a.fn();//=>报类型错误

浏览器控制台运行结果

这段代码定义了一个空对象a,然后打印一个不存在的属性fn,因为fn不存在,所以默认赋值undefined,然后调用了fn方法,但undefined类型当然无法作为函数调用,于是报了一个类型错误。这当然没有任何问题,但是让我们再思考以下代码。

var a = {};
console.log(a.toString);//ƒ toString() { [native code] }
a.toString();//'[object Object]'

浏览器控制台运行结果
天啦噜!不敢置信,还是一个空对象a,同样没有任何内容,但是神奇的是,当我们打印a.toString属性时,发现竟然不是undefined,而是一个函数,然后我们调用toString方法,它竟然没有报类型错误,而是执行了方法!!那么问题来了,我们明明给a赋值的花括号里面空空如也,那么这个toString这个方法是如何出现在a里面的呢?

真相只有一个,那就是a里面压根没有toString这个方法,这个方法是Object原型对象里面的
那么凭什么a可以调用Object原型对象里面的方法?
a的解释: 因为Object原型对象在我的原型链里面,只要你在我的原型链里面,那么你的属性和方法,都是我的,而我的属性和方法,还是我的[嚣张😎]

查看原型

此时Object原型对象很不服气:凭什么你说我在我就在啊,我还说你在我的原型链里面呢!
a依然一副不可一世的样子😎:“行,要证据是吧,来,我给你!”。
只见a不慌不忙,拿出两个证据。

通过浏览器查看

思考以下代码:

var a = {};
a//=>{}[[Prototype]]: Object

浏览器控制台运行结果
只见a是一个空对象,里面有一个属性[[Prototype]],这个属性可不得了,这个属性指向谁,谁就是当前对象的原型,而当前对象是a,[[prototype]] : 旁边是Object,意思是该对象是Object函数的原型对象,即指向Object原型对象。
只见此时Object原型对象额头上流下一滴冷汗。

通过.__proto__属性查看

思考以下代码

 var a = {};
console.log(a.__proto__);

l浏览器控制台运行结果
此时浏览器没有直接打印出该原型对象对应的函数名,但通过该原型对象的constructor属性我们依然能够断定,该对象就是函数Object()的原型对象。
但谁知,Object原型对象看到__proto__属性后竟然喜笑颜开,只见它拿出大名鼎鼎的IE浏览器,然后将版本调整到10,只见__proto__属性输出了不一样的结果。
将IE版本调整至10
IE打印出来的结果竟然是undefined的!!
IE10无法识别__proto__属性
这是因为__prototype__属性在ES6之前并不是标准方法,在不支持ES6的浏览器中不能使用(而就算是ES6标准只明确规定只有浏览器需要部署这个属性,所以其他运行环境不一定能够使用),所以存在兼容性问题,那有没有一种ES5也能够证明的方法呢?当然有!那就是Object.getPrototypeOf()方法

Object.getPrototypeOf()

Object.getPrototype()方法能够得到一个对象的[[prototype]]属性指向的原型对象。
思考如下代码

var a = {};
console.log(a.__proto__);
Object.getPrototypeOf(a) === Object.prototype;//true

依然是IE10但返回了true
我们通过判断a的原型是否是Object函数的prototype属性指向的Object原型对象,发现其返回true。
我们也可以通过直接打印来查看
constructor
只见其得到了一个对象,该对象的constructor属性指向了Object()函数,这毫无疑问说明了该对象就是Object函数的原型对象,即Object原型对象。
这次使用的是ES5标准里的方法检测出来的,Object原型对象终于无话可说了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值