一、全局对象 window
ECMAScript 规定全局对象叫做 global,但是浏览器把 window 作为全局对象(浏览器先存在的),window 就是一个哈希表,有很多属性。
window 的属性就是全局变量。这些全局变量分为两种:
-
一种是 ECMAScript 规定的:
- global.parseInt
- global.parseFloat
- global.Number
- global.String
- global.Boolean
- global.Object
-
一种是浏览器自己加的属性:
- window.alert
- window.prompt
- window.comfirm
- window.console.log
- window.console.dir
- window.document
- window.document.createElement
- window.document.getElementById
二、全局函数
- Number
Number(1)
传给它一个东西变成数字var n = new Number(1)
创建一个Number
对象1
与new Number(1)
的区别:内存不同
n1 在控制台打出的结果就是 1,n2 实际上是一个 hash
,可以看到有好几个方法函数。
这个地方就有个疑问了,n1 其实也可以使用 toString()
方法,那干嘛还需要 new Number()
呢?
这里就要说到历史了,Brendan Eich在发明 js 的时候被公司要求JS要像Java,所以他模仿javavar n = new Number(1);
申明 Number
。 这样 n 是一个对象,有自己的属性,但太麻烦,于是他又写了一种 var n =1;
但是有个缺点,这样的话就没有 toString()
方法,基本类型是没有属性的。又想用简单的类型,又想用对象的那些 toString()
方法,于是他想了一个妙计,做一个临时的转换。首先声明一个临时的对象 temp = new Number(n)
,也就是说 temp
是 n 的一个复杂类型的封装,然后 toString()
实际上是 temp.toString()
,然后把 temp.toString()
的值作为 n.toString()
的值。再把 temp
干掉,这个 temp
就好像没有存在过一样。但是它临时把 toString()
给弄过来了。
var n=1;n.toString(); //n用toString时,本质是下面这两行,实质n是没有属性的var temp = new Number(1); //声明了一个临时变量temp,并将n作为Number对象赋给它temp.toString(); //用temp来执行toString,执行完后temp就被抹杀了n.xxx = 2; //可以的n.xxx //undefined,因为xxx是存在temp的,上一句的temp执行完后就被抹杀,所以xxx也被抹杀了,再去调的时候又是一个新的临时对象复制代码
- String
- 声明的形式一种是
var s = 'sldkjslkdjskldj'
- 另一种是
var s2 = new String(s)
- 它们的区别是第一种是基本类型的
string
,直接存在 Stack(栈内存),第二种是把它变成了对象之后的hash
。
- 声明的形式一种是
var s = 'abc'; // 原理和 Number 一样var s2 = new String(s);s2[0] // 'a',因为 String() 对象有 hash 的属性s[0] // 'a',s 也能调用这个属性,但 s 实质没有属性,这个原理和上面 Number 一样s.charCodeAt(0) // 97,a 的十进制 unicode 码s.charCodeAt(0).toString(16) //'61',a 的十六进制 unicode 码' abc '.trim() // 'abc',去掉字符串两边的空格var s1 = 'Hello's.concat(s1) // abcHello,连接两个字符串s1.slice(0,2) // 'He',slice 是切片,切下 0 到 2 之前的内容,包前不包后s1.replace('e','o') // 'Hollo',替换(注意 S1 还是原来的 S1,replace 是得到一个新的字符串 'Hollo' )s1.length // 5复制代码
- Boolean
- 声明的形式一种是
var b = true
- 另一种是
var b2 = new Boolean(true)
创建一个Boolean
对象 - 不加
new
是用来做转换用的,new
是用来生成对象的
- 声明的形式一种是
var b = falsevar b1 = new Boolean(false)if(b){console.log('true')}if(b1){console.log('true')} // true, 只会输出 b1,因为所有对象都是 true复制代码
- Object
var o1 = {}
var o2 = new Object()
- o1 和 o2 没区别,但并不是相等的
o1 === o2 //false,因为比较的是内存地址,而地址不同复制代码
三、公用属性(原型)
所有对象都有 toString
和 valueOf
属性,那么我们是否有必要给每个对象一个 toString
和 valueOf
呢?
明显不需要,JS 的做法是把 toString
和 valueOf
放在一个对象里(暂且叫做公用属性组成的对象), 然后让每一个对象的 __proto__
存储这个「公用属性组成的对象」的地址。
怎么知道 o1 可以 toString()
呢?当在写 o1.toString()
的时候,首先看 o1 是不是对象,如果不是对象就先包装成一个对象,做一个临时的转换。如果是对象的话,就去看有没有 toString()
这个 key。当发现没有,然后就进入公用属性看看有没有,那有于是就调用这个 toString()
。
同时 Number
对象还有它特有公用属性,所以它比 Object
要多一个公用属性库,并且会先访问 Number
的原型(公用属性库)。
同理,String
、Boolean
也有自己的公用属性,都会先指向自己的公用属性,再指向 Object
的公用属性,Object
的 __proto__
指向 null
,所以说 Object
的公用属性是所有对象的公用属性。
什么是原型链呢?
如果我们看上面其中的一条线,S1 它有自己的属性,然后它作为
String
的公用属性是在这条线的上一个节点。如果还要再找呢,就是String
作为对象,因为String
也是对象(我们可以把一个字符串转换成对象),如果把它看成一个对象的公用属性再往上找。所以这一条线就像个链子一样,链子上面有 3 个节点,这就叫做原型链。
Object
、String
、Number
和 Boolean
的公用属性在哪里?这些东西都不是凭空产生的,是浏览器一开始就把这些给准备好了。这些公用属性都是一个 hash
,若没被引用的话会被当作垃圾回收。那谁在引用它呢,这个东西叫做 prototype
, 平常对象的原型是 Object.prototype
在引用。
Number.prototype.__proto__ === Object.prototype //true,是把Object.prototype的地址赋给Number的__proto__属性了var n1 = new Number(1);n1.__proto__ === Number.prototype //true,是把Number.prototype的地址赋给n1的__proto__属性了n1.__proto__.__proto__ === Object.prototype //true复制代码
__proto__和prototype的区别
打开浏览器就相当于生成了一个 window,里面存了 Number
、String
、Boolean
和 Object
这几个函数,对象里的 prototype
属性里存了它们原型的地址,在你写:
var s = new String('hello'); //就会把 String.prototype 里的地址放入 s 的 __proto__ 属性里复制代码
这样用 s 就能调用 String
的原型了,String.prototype
是 String
的公用属性的引用,s.__proto__
是 String
的公用属性的引用。
总结:
var 对象 = new 函数() // 函数可以是 Number/String/Boolean/Object对象.__proto__ === 函数.prototype //true复制代码
__proto__
是对象的属性,prototype
是函数对象的属性
然后可以推出:
函数.prototype.__proto__ === obj.prototype函数.__proto__ === Function.prototypeFunction.__proto__ === Function.prototypeFunction.prototype.__proto__ === obj.prototype复制代码