阅读 247

简单类型与复杂类型及原型链

这两天在学习原型链相关的知识,就来随便谈谈简单类型与复杂类型、原型链的一些体会

1 简单类型与复杂类型

我们知道在JavaScript中声明变量有几种方式,拿数值来做例子

var n1 = 1;
var n2 = new Number(1);
复制代码

那么这两种声明方式有什么区别呢?首先来打印这两个变量来看看


从图中可以看到n1就只有一个数值,而n2除了有一个私有值1之外,还有一个__proto__,里面放了很多东西。内存图如下

从图中我们可以看到n1是直接存放在栈内存中的一个值,而n2其实是存放在堆内存中,栈内存中只存放了一个地址,指向堆内存
那既然有这些差别,那我们为什么在实际的使用过程中没有感觉呢?n2所有的函数等等,用n1也全部都可以使用,看起来好像和内存图表示的不一样啊?

n1.toString(); // "1"
复制代码

n1看起来好像只有一个数值,并没有那些函数,为什么可以调用呢?其实这是由于JavaScript的独特设计造成的
当执行上面那段代码的时候,其实JavaScript重新声明一个临时对象temp,利用new Number()的方式为temp赋值为1,那么temp就和n2的存储方式一样的了,再将temp.toString()的值赋给n1.toString(),然后清除temp,好像temp从未存在过,而n1也拥有了看似只有n2才有的属性

接着执行下面的语句会发生什么?

n1.name = 'yyzcl';
n1.name // ?
复制代码


从图中看明明n1.name赋值成功了,调用的时候怎么就没有呢?
其中的过程看可以看看内存图

这是n1.name = 'yyzcl';执行时的场景,JavaScript声明了一个临时对象来存放n1.name的值,所以并未报错。而当它执行n1.name时,由于临时对象在赋值语句之后随即被清除了,n1实际上还是只存在于栈内存中。
当要搜寻n1.name的值时,再创建一个临时对象,在临时对象中去搜索.name这个值,由于新建的临时对象并没有.name这个值,所以会返回undefined


2 原型与原型链

从上面我们可以知道,当利用

var n1 = new Number();
var n2 = new Number();
……
复制代码

这类语法声明多个变量时,如果要在堆内存中为每个变量开辟一个空间,单独存放每个变量的属性,那么就太浪费内存空间了。JavaScript就设计了一个特性,在内存中开辟一个空间存放变量共同的属性,然后每个变量独特的属性分开开辟空间存放,并存放一个地址,指向相同存放变量共同属性的地址。


这里就涉及到原型链了,以数值字符串对象为例来看,声明一个变量,首先这个变量的私有属性有一个空间,然后还指向变量所属数据类型的一个共有属性空间,这个共有属性空间还指向Object的共有属性空间,具体关系如图所示

每个类型的共有属性会存放到一起,并用一个地址指向它,这样既可以节省空间,还不影响使用。

数值共有属性就是数值的原型,对象共同属性就是对象的原型


其实每个原型还有一个`constructor`属性,它指向引用它的上一级原型,例如:数值原型的`constructor`就指向具体的数值变量

总结一下,由相互关联的原型组成的链状结构就是原型链

3 __proto__与prototype

首先扔一张图


给一个公式

var 对象 = new 函数()
对象.__proto__ === 函数.prototype

函数.prototype也可以看做一个对象,于是就有了

对象.__proto__.__proto__ === 函数.prototype.__proto__ === 函数.prototype.prototype


一直可以追寻到`Object.prototype`就结束了,因为`Object.prototype.__proto__`的值为`null`

如果有错误或者不严谨的地方,欢迎给予指正,十分感谢
关注下面的标签,发现更多相似文章
评论