JS里的对象和原型
本博客主要讲以下几部分
全局对象
window
全局函数
公用属性是什么
重要公式
全局对象window
ECMAScript 规定全局对象叫做 global
,但是浏览器把 window
作为全局对象(浏览器先存在的)
window
就是一个哈希表,有很多属性。
window
的属性就是全局变量。
这些全局变量分为两种:
-
一种是
ECMAScript
规定的
global.parseInt
global.parseFloat
global.Number
global.String
global.Boolean
global.Object
-
一种是浏览器自己加的属性(私有)
history //浏览器历史,也叫BOM
window.alert
window.prompt
window.comfirm
window.console.log
window.console.dir
window.document //有规范,叫DOM(W3C规定)
window.document.createElement
window.document.getElementById
所有 API 都可以在 MDN 里找到详细的资料。
全局函数
-
Number
先看以下代码
var n = 1
var n1 = new Number(1) //创建一个 Number 对象
那么问题来了,
n
与 n1
的区别是什么?
这时画一个内存图就清楚了
这样可以看出,
n
和n1
的区别是内存上是不一样的那么
Number(1)
里写了什么呢?valueOf
,toString
...这些都是Number()
函数内置的操作符,也就是说,如果包装成对象的话,n1
就有更多便捷的操作给你用,而n
就只有数字1
-
临时转换
这是为什么呢,因为JS发明时,JS之父的BOSS要求JS要长得像Java
所以就有了Number()
函数,但是实际上Number()
基本没用,大家都喜欢直接用var n = 1
,但是n.toString
因为不是对象,所以不能直接转换
这时,临时转换就出现了
临时转换就是在想要调用n
的toString
操作时,创建一个临时对象,用这个对象的toString()
方法去操作n
,临时转换后就会把那个临时对象抹杀
掉
用内存图来理解是这样的:
所以现在,就算我们只写
var n = 1
,之后也能使用复杂Number()
的所有功能
但是要注意的是,n
本身是没有toString()
函数的,只是利用来临时变量的方法,请看下面这题
var n = 1
n.xxx = 2
那么,n.xxx的值是什么呢?
临时转换2
因为n
只是将2存到临时变量的xxx
里,所以n.xxx的值是undefined
-
String
var s = 'asdfg'
var s1 = new String(asdfg)
和之前理解Number()
一样,s
之所以可以直接Number(s)
,是因为浏览器创建了一个临时对象,然后's'调用了这个对象的'Number()'方法,使用完后这个临时对象就被抹杀了
这里要提一句的是,代码中的new
如果不加,那就是用作转换用的。如果加了,就是生成对象。
-
Boolean
接下来看这一题
var f = false
var f1 = new Boolean(false)
if(f){console.log(1)} // 打印1
if(f1){console.log(2)} // 打印2
请问这题浏览器会打印出什么呢?
我们画个内存图
因为
f=false
,所以1
是不会打印出来的因为
f1
实际上是一个对象,所以浏览器会打印出2
-
Object
var o1 = {}
var o2 = new Object()
其实o1
与o2
的方式根本没区别,只是他们的值内存地址不一样而已
所以Object()
基本没用
-
小结
看到这里,我们可以知道四个全局函数的作用和相应内存的改变了
这些全局函数只是将值由基本类型变成了对象而已
公用属性(原型)
所有对象都有 toString 和 valueOf 属性,那么我们是否有必要给每个对象一个 toString 和 valueOf 呢?
JS 的做法是把 toString 和 valueOf 放在一个对象里(暂且叫做公用属性组成的对象)
然后让每一个对象的 __proto__
存储这个公用属性组成的对象的地址。
-
这里可以看图来理解
在看这张图前,我们先考虑一下浏览器的垃圾回收机制,垃圾回收会回收没有被引用的对象。
这些全局对象如果不被引用的话,就会被浏览器回收掉,如何避免这些全局属性被回收掉呢?
浏览器在打开的时候,就会创建一个名为window
的全局属性,这个全局属性包含了所有全局函数的地址值。
这些地址又引用了该函数特有的公共属性(对象),每个函数特有的公共属性(对象)又用_prote_
来储存Object
对象的地址
-
举个例子
n.toString()
流程是这样的
1.浏览器先看n
是不是对象,不是做临时转换。
2.是的话就先去看看n
对应的函数公共类型里找有没有toString()
这个操作符
3.如果没有,就进入_prote_
对应的公共属性里找有没有toString()
操作符
4.有的话,就调用这个toString()
这样的,形成的穿过多个节点的流程,就叫原型链
-
内存图理解
var o1 = new Number(8)
var o2 = 8
o1 === o2 // false
o1.toString === o2.toString // true
因为他们调用的方法是一样的
内存图
他们的数据类型是不一样的,但是他们调用的共同属性的方法(对象)是一样的
重要公式
由此,我们可以得出一个公式,结合原型解释图理解
var 对象 = new 函数()
对象.__proto__ === 对象的构造函数.prototype
由这个公式,我们可以再推论
var number = new Number()
number.__proto__ = Number.prototype
Number.__proto__ = Function.prototype // 因为 Number 是 Function 的实例
var object = new Object()
object.__proto__ = Object.prototype
Object.__proto__ = Function.prototype // 因为 Object 是 Function 的实例
var function = new Function()
function.__proto__ = Function.prototype
Function.__proto__ == Function.prototye // 因为 Function 是 Function 的实例!
推论图
这个公式如果你能读懂的话,你也就懂JS里的对象到底是什么了