ECMAScript規(guī)定全區(qū)對(duì)象叫做global,但是瀏覽器把window作為全局對(duì)象(瀏覽器先存在)window就是一個(gè)哈希表,有很多屬性,window的屬性就是全局變量。
window下面的屬性分為兩類,第一類是ECMAScript規(guī)定必須有的函數(shù),第二類是私有的(瀏覽器專有,標(biāo)準(zhǔn)不一)。
簡單類型與對(duì)象的區(qū)別:
//全局函數(shù)
//1.Number
var n = new Number(1)//創(chuàng)建一個(gè)Number對(duì)象
1與new Number(1)的區(qū)別是什么?看內(nèi)存圖
//2.String
var s = new String('hello')//創(chuàng)建一個(gè)Number對(duì)象
'hello'與new String('hello')的區(qū)別是什么?看內(nèi)存圖
//3.boolean
var b = new Boolean(true)//創(chuàng)建一個(gè)Number對(duì)象
true與new Boolean(true)的區(qū)別是什么?看內(nèi)存圖
//4.Object
var o1 = {}
var o2 = new Object()
o1和o2沒區(qū)別
簡單數(shù)據(jù)類型和通過new 構(gòu)造函數(shù)創(chuàng)建的數(shù)據(jù)對(duì)象(除了對(duì)象)區(qū)別在于內(nèi)存地址不同,前者存放在stack中,而后者存放在heap里。

將數(shù)值通過
Number包裝成對(duì)象,內(nèi)存里面就有許多操作該數(shù)值的方法。但簡單數(shù)據(jù)也可以調(diào)用這些辦法,那為什么還需要將數(shù)值包裝成對(duì)象呢?答:歷史原因,在發(fā)明JS時(shí)模仿JAVA聲明數(shù)據(jù)(
new關(guān)鍵字,滿足老板需求),第二種可以直接聲明var a =1。 由于第二種方法無法使用toString等方法(只有對(duì)象才能使用方法,簡單數(shù)據(jù)類型不可以),創(chuàng)始人使用了一個(gè)妙計(jì)(臨時(shí)轉(zhuǎn)換)當(dāng)寫n.toString時(shí),隱式的聲明一個(gè)temp為 new Number(n),然后再調(diào)用n.toString的值為temp,再銷毀temp數(shù)據(jù)。臨時(shí)的轉(zhuǎn)換,用完就銷毀了。所以給簡單數(shù)據(jù)類型加屬性讀取時(shí)將會(huì)報(bào)錯(cuò),這種方法受到了開發(fā)人員的歡迎。
公有的屬性藏在哪?
所有的對(duì)象都有toString和valueOf屬性,那么我們是否有必要給每個(gè)對(duì)象一個(gè)toString和valueOf呢?
明顯不需要(內(nèi)存不允許),JS的做法是把toString和valueOf放在一個(gè)對(duì)象里,暫且叫做公有屬性組成的對(duì)象(原型)。
對(duì)象分為普通對(duì)象和函數(shù)對(duì)象,
Object和Function是js自帶的函數(shù)對(duì)象,每個(gè)對(duì)象都有原型(null和undefined除外)可以理解為對(duì)象的默認(rèn)屬性和方法。
字符串對(duì)象
通過new string()方法轉(zhuǎn)換成的字符串對(duì)象是一個(gè)哈希,每一個(gè)字符對(duì)應(yīng)相應(yīng)的索引值,使用方括號(hào)或者charAt(索引值)來獲取相對(duì)應(yīng)的字符,charCodeAt(索引值)獲取對(duì)應(yīng)索引的值的編碼。繼承了String對(duì)象的方法。
獲取一個(gè)數(shù)字對(duì)應(yīng)的進(jìn)制值使用toString()方法,如(100).toString(16)//64 可以將字符的編碼轉(zhuǎn)化為其他進(jìn)制的編碼
布爾值對(duì)象
簡單數(shù)據(jù)類型false和布爾對(duì)象false在被轉(zhuǎn)換時(shí)是不相等的。
為什么不相等?雖然兩個(gè)對(duì)象都沒有名字,但存放在heap中的地址值不同,自然不會(huì)相等,請(qǐng)牢記5個(gè)假值,對(duì)象經(jīng)過布爾轉(zhuǎn)換為真值。
結(jié)論:所有新聲明的對(duì)象都是不相等的。除非將一個(gè)對(duì)象的地址值賦值給另一個(gè)變量。
Number String Boolean Object通過以上幾種方式創(chuàng)建的對(duì)象都具有toString、valueof方法,這兩個(gè)方法哪里來的呢?如果在每個(gè)新生成對(duì)象里面都定義這兩個(gè)方法太占內(nèi)存。實(shí)現(xiàn)繼承的方法:原型鏈
要清楚原型鏈,首先先弄清楚對(duì)象。
普通對(duì)象,有____proto____屬性(指向其原型鏈),沒有prototype屬性。
原型對(duì)象,構(gòu)造函數(shù).prototype原型對(duì)象還有constructor屬性指向構(gòu)造函數(shù)對(duì)象
函數(shù)對(duì)象,通過new Function()創(chuàng)建的都是函數(shù)對(duì)象。用有prototype、____proto____屬性(指向原型對(duì)象)
原型鏈概念
ECMAScript中描述了原型鏈的機(jī)制,將原型鏈作為實(shí)現(xiàn)繼承的主要方式。其基本思想是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。
在js中,每個(gè)對(duì)象都有一個(gè)指向它的原型對(duì)象的內(nèi)部鏈接,這個(gè)原型對(duì)象又有自己的原型,直到某個(gè)對(duì)象的原型為null為止(也就是不再有原型指向),組成這條鏈的最后一環(huán)。這種一級(jí)一級(jí)的鏈接結(jié)構(gòu)就稱為原型鏈。
JS對(duì)象有一個(gè)指向一個(gè)原型對(duì)象的鏈,當(dāng)試圖訪問一個(gè)對(duì)象的屬性時(shí),它不僅僅在該對(duì)象上搜尋,還會(huì)搜尋該對(duì)象的原型,以及該對(duì)象原型的原型,依此層層向上搜索,直到找到一個(gè)名字匹配的屬性或到達(dá)原型鏈的末尾。
所有引用類型默認(rèn)都繼承了
Object,而這個(gè)繼承也是通過原型鏈實(shí)現(xiàn)的,所有函數(shù)的默認(rèn)原型都是Object的實(shí)例,因此默認(rèn)原型都會(huì)包含一個(gè)內(nèi)部指針,指向Object.prototype。這也正是所有自定義類型都會(huì)繼承toString(),valueOf()等默認(rèn)方法的根本原因,所有函數(shù)的原型最頂端都有一個(gè)最終的對(duì)象原型,存儲(chǔ)著對(duì)象本身自帶方法和屬性,當(dāng)調(diào)用實(shí)例上的toString()等方法時(shí),實(shí)際上是調(diào)用的是保存在Object.prototype中的那個(gè)方法。
簡單回顧一下構(gòu)造函數(shù)、原型和實(shí)例之間的關(guān)系:每創(chuàng)建一個(gè)函數(shù)都有一個(gè)prototype屬性指向通過該構(gòu)造函數(shù)創(chuàng)建實(shí)例對(duì)象的原型對(duì)象,原型對(duì)象是包含特定類型的所有實(shí)例共享的屬性和方法,都包含了一個(gè)指向構(gòu)造函數(shù)的指針,而實(shí)例都包含一個(gè)指向原型對(duì)象的____proto____內(nèi)部指針。


下面是一個(gè)簡單的例子:
//原型鏈實(shí)例
//構(gòu)造函數(shù)Animal
function Animal(){
this.type = "animal"
}
//Animal構(gòu)造函數(shù)定義原型方法
Animal.prototype.getType = function(){
return this.type
}
//Dog構(gòu)造函數(shù)
function Dog(){
this.name = 'dog'
}
//Dog構(gòu)造函數(shù)定義原型方法
Dog.prototype.getName = function(){
return this.name
}
//Dog原型等于構(gòu)造函數(shù)Animal實(shí)例(實(shí)現(xiàn)繼承)
Dog.prototype = new Animal()
//構(gòu)建新實(shí)例
var xiaohuang = new Dog()
實(shí)例、構(gòu)造函數(shù)、原型之間的關(guān)系。
xiaohuang.__proto__ === Dog.prototype
Dog.prototype.__proto__ === Animal.prototype
Animal.prototype === Object.prototype
Object.prototype.proto === null
//總結(jié):xiaohuang這個(gè)Dog的實(shí)例繼承了Animal,Animal繼承了Object
console.log(xiaohuang.type)//'animal'
console.log(xiaohuang.name)//'dog'


總結(jié):

兩個(gè)構(gòu)造函數(shù),將其中一個(gè)構(gòu)造函數(shù)的實(shí)例賦值給另一個(gè)構(gòu)造函數(shù)的原型,這樣另一個(gè)原型就有了指向前面那一個(gè)構(gòu)造函數(shù)的原型的指針(創(chuàng)建的實(shí)例也會(huì)有同樣的原型鏈),查找屬性和方法會(huì)根據(jù)原型鏈來進(jìn)行搜索,先搜索實(shí)例,再搜索原型,最后是原型的原型.....,注意此時(shí)實(shí)例的constructor指向了最開始的構(gòu)造函數(shù),而不是創(chuàng)建實(shí)例的函數(shù)(為了代碼的嚴(yán)謹(jǐn),可以設(shè)置為創(chuàng)建此實(shí)例的函數(shù))。實(shí)現(xiàn)的本質(zhì)是重寫原型對(duì)象,代之以一個(gè)新類型的實(shí)例。原型鏈繼承不僅會(huì)繼承原型上面的屬性和方法,還會(huì)繼承實(shí)例上的方法和屬性。
Object原型對(duì)象,怎么讓每個(gè)對(duì)象都指向這個(gè)共有屬性?
答:每個(gè)對(duì)象都有一個(gè)__proto__屬性指向了共有屬性。
對(duì)象和數(shù)值對(duì)象:
var o1 = new Number(1);
var o2 =new Object(1);
o1===o2 //false
o1.toString()===o2.toString() //true
兩個(gè)數(shù)值對(duì)象不相等,因?yàn)閟tack存放的heap地址值不同,但使用的toString函數(shù)是公共屬性(Number的原型的屬性),因此相等(new Object(1)自動(dòng)轉(zhuǎn)換成了數(shù)值對(duì)象)。

Object()函數(shù)不加new操作符會(huì)根據(jù)傳入的參數(shù)生成相應(yīng)的數(shù)據(jù)類型的對(duì)象,對(duì)于Object函數(shù)來說,加不加new都是一樣的效果。
String()函數(shù)不加new操作符是將你給定的參數(shù)變成string常量(基本類型,非對(duì)象),而加上new會(huì)生成String對(duì)象(復(fù)雜類型,字符串對(duì)象),除了Object函數(shù)以外,其他的標(biāo)準(zhǔn)庫函數(shù)都是一樣的效果。
數(shù)值的toString()方法和對(duì)象的toString()方法并不相等,因?yàn)榍罢呖梢约舆M(jìn)制參數(shù)而且會(huì)被先搜尋到。
調(diào)用方法時(shí),會(huì)經(jīng)過一層一層的查找。
Number String Boolean Object都有自己的共有屬性,____proto____都是先指向了自己的共有屬性,共有屬性的__proto____再指向了對(duì)象。
如果對(duì)象的共有屬性沒有被引用的話,必將會(huì)被回收,
object.prototype指向(引用)這個(gè)共有屬性(原型)
當(dāng)你聲明一個(gè)對(duì)象時(shí),JS引擎除了在棧內(nèi)存里面存放一個(gè)hash之外,還將
____proto____指向了你該有的共有屬性(原型)。
不寫代碼就有prototype
在沒有代碼的時(shí)候,Number.prototype、Object.prototype、String.prototype、Boolean.prototype都引用了各自的共有屬性(保證不被垃圾回收機(jī)制回收),是JS定義的,不可更改的。而你的對(duì)象____proto____則指向了對(duì)應(yīng)數(shù)據(jù)類型的prototype。

初始化數(shù)據(jù)類型里面還有一個(gè)Function.prototype,存儲(chǔ)了函數(shù)的原型(共有方法),Function也是一個(gè)對(duì)象,Function.__proto__指向了Function.prototype,使用函數(shù)初始化對(duì)象時(shí)(new Function()),該函數(shù)對(duì)象的____proto____就指向了Function.prototype(因?yàn)镕unction是Object的構(gòu)造函數(shù)),而Function.prototype里面的____proto____就指向了Object.prototype。

繼承方式:子類構(gòu)造函數(shù).prototype= new 父類構(gòu)造函數(shù) ,簡單說就是將父類的實(shí)例賦值給子類的原型,這樣子類的____proto____屬性
目前組合繼承用的最多,構(gòu)造函數(shù)模式定義實(shí)例屬性(不可共享),原型模式則定義方法和共享的屬性。
現(xiàn)在你明白什么是原型和原型鏈了嗎?:)