这两天在学习原型链相关的知识,就来随便谈谈简单类型与复杂类型、原型链的一些体会
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`
如果有错误或者不严谨的地方,欢迎给予指正,十分感谢