ECMAScript 规定全局对象叫做 global,但是浏览器把 window 作为全局对象(浏览器先存在的),window 就是一个哈希表,有很多属性。
window 的属性就是全局变量。这些全局变量分为两种:
1.一种是 ECMAScript 规定的:
global.parseIntglobal.parseFloatglobal.Numberglobal.Stringglobal.Booleanglobal.Object2.一种是浏览器自己加的属性:
window.alertwindow.promptwindow.comfirmwindow.console.logwindow.console.dirwindow.documentwindow.document.createElementwindow.document.getElementByIdNumber
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 // 5Boolean
声明的形式一种是 var b = true另一种是 var b2 = new Boolean(true) 创建一个 Boolean 对象不加 new 是用来做转换用的,new 是用来生成对象的 var b = false var b1 = new Boolean(false) if(b){console.log('true')} if(b1){console.log('true')} // true, 只会输出 b1,因为所有对象都是 trueObject
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.prototype Function.__proto__ === Function.prototype Function.prototype.__proto__ === obj.prototype