ECMAScript規(guī)定全區(qū)對象叫做global隘弊,但是瀏覽器把window作為全局對象(瀏覽器先存在)window就是一個哈希表哈踱,有很多屬性,window的屬性就是全局變量梨熙。
window下面的屬性分為兩類开镣,第一類是ECMAScript規(guī)定必須有的函數(shù),第二類是私有的(瀏覽器專有咽扇,標準不一)邪财。
簡單類型與對象的區(qū)別:
//全局函數(shù)
//1.Number
var n = new Number(1)//創(chuàng)建一個Number對象
1與new Number(1)的區(qū)別是什么?看內(nèi)存圖
//2.String
var s = new String('hello')//創(chuàng)建一個Number對象
'hello'與new String('hello')的區(qū)別是什么质欲?看內(nèi)存圖
//3.boolean
var b = new Boolean(true)//創(chuàng)建一個Number對象
true與new Boolean(true)的區(qū)別是什么树埠?看內(nèi)存圖
//4.Object
var o1 = {}
var o2 = new Object()
o1和o2沒區(qū)別
簡單數(shù)據(jù)類型和通過new 構造函數(shù)創(chuàng)建的數(shù)據(jù)對象(除了對象)區(qū)別在于內(nèi)存地址不同,前者存放在stack中,而后者存放在heap里嘶伟。
將數(shù)值通過
Number
包裝成對象弥奸,內(nèi)存里面就有許多操作該數(shù)值的方法。但簡單數(shù)據(jù)也可以調(diào)用這些辦法奋早,那為什么還需要將數(shù)值包裝成對象呢?答:歷史原因赠橙,在發(fā)明JS時模仿JAVA聲明數(shù)據(jù)(
new
關鍵字耽装,滿足老板需求),第二種可以直接聲明var a =1
期揪。 由于第二種方法無法使用toString
等方法(只有對象才能使用方法掉奄,簡單數(shù)據(jù)類型不可以),創(chuàng)始人使用了一個妙計(臨時轉換)當寫n.toString
時凤薛,隱式的聲明一個temp為 new Number(n)
姓建,然后再調(diào)用n.toString的值為temp
,再銷毀temp數(shù)據(jù)缤苫。臨時的轉換速兔,用完就銷毀了。所以給簡單數(shù)據(jù)類型加屬性讀取時將會報錯活玲,這種方法受到了開發(fā)人員的歡迎涣狗。公有的屬性藏在哪?
所有的對象都有toString
和valueOf
屬性舒憾,那么我們是否有必要給每個對象一個toString
和valueOf
呢镀钓?
明顯不需要(內(nèi)存不允許),JS的做法是把toString
和valueOf
放在一個對象里镀迂,暫且叫做公有屬性組成的對象(原型)丁溅。
對象分為普通對象和函數(shù)對象,
Object
和Function
是js自帶的函數(shù)對象探遵,每個對象都有原型(null和undefined除外)可以理解為對象的默認屬性和方法窟赏。
字符串對象
通過new string()
方法轉換成的字符串對象是一個哈希妓柜,每一個字符對應相應的索引值,使用方括號或者charAt(索引值)
來獲取相對應的字符饰序,charCodeAt(索引值)
獲取對應索引的值的編碼领虹。繼承了String
對象的方法。
獲取一個數(shù)字對應的進制值使用toString()
方法求豫,如(100).toString(16)//64
可以將字符的編碼轉化為其他進制的編碼
布爾值對象
簡單數(shù)據(jù)類型false和布爾對象false在被轉換時是不相等的塌衰。
為什么不相等?雖然兩個對象都沒有名字蝠嘉,但存放在heap中的地址值不同最疆,自然不會相等,請牢記5個假值,對象經(jīng)過布爾轉換為真值蚤告。
結論:所有新聲明的對象都是不相等的努酸。除非將一個對象的地址值賦值給另一個變量。
Number
String
Boolean
Object
通過以上幾種方式創(chuàng)建的對象都具有toString
杜恰、valueof
方法获诈,這兩個方法哪里來的呢?如果在每個新生成對象里面都定義這兩個方法太占內(nèi)存心褐。實現(xiàn)繼承的方法:原型鏈
要清楚原型鏈舔涎,首先先弄清楚對象。
普通對象,有____proto____
屬性(指向其原型鏈)逗爹,沒有prototype屬性亡嫌。
原型對象,構造函數(shù).prototype原型對象還有constructor屬性指向構造函數(shù)對象
函數(shù)對象,通過new Function()創(chuàng)建的都是函數(shù)對象掘而。用有prototype
寻仗、____proto____
屬性(指向原型對象)
原型鏈概念
ECMAScript中描述了原型鏈的機制犹菇,將原型鏈作為實現(xiàn)繼承的主要方式叛本。其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法艺玲。
在js中,每個對象都有一個指向它的原型對象的內(nèi)部鏈接女蜈,這個原型對象又有自己的原型持舆,直到某個對象的原型為null為止(也就是不再有原型指向),組成這條鏈的最后一環(huán)伪窖。這種一級一級的鏈接結構就稱為原型鏈逸寓。
JS對象有一個指向一個原型對象的鏈,當試圖訪問一個對象的屬性時覆山,它不僅僅在該對象上搜尋竹伸,還會搜尋該對象的原型,以及該對象原型的原型,依此層層向上搜索勋篓,直到找到一個名字匹配的屬性或到達原型鏈的末尾吧享。
所有引用類型默認都繼承了
Object
,而這個繼承也是通過原型鏈實現(xiàn)的譬嚣,所有函數(shù)的默認原型都是Object
的實例钢颂,因此默認原型都會包含一個內(nèi)部指針,指向Object.prototype
拜银。這也正是所有自定義類型都會繼承toString()
殊鞭,valueOf()
等默認方法的根本原因,所有函數(shù)的原型最頂端都有一個最終的對象原型尼桶,存儲著對象本身自帶方法和屬性操灿,當調(diào)用實例上的toString()
等方法時,實際上是調(diào)用的是保存在Object.prototype
中的那個方法泵督。
簡單回顧一下構造函數(shù)趾盐、原型和實例之間的關系:每創(chuàng)建一個函數(shù)都有一個prototype屬性指向通過該構造函數(shù)創(chuàng)建實例對象的原型對象,原型對象是包含特定類型的所有實例共享的屬性和方法小腊,都包含了一個指向構造函數(shù)的指針救鲤,而實例都包含一個指向原型對象的____proto____
內(nèi)部指針。
下面是一個簡單的例子:
//原型鏈實例
//構造函數(shù)Animal
function Animal(){
this.type = "animal"
}
//Animal構造函數(shù)定義原型方法
Animal.prototype.getType = function(){
return this.type
}
//Dog構造函數(shù)
function Dog(){
this.name = 'dog'
}
//Dog構造函數(shù)定義原型方法
Dog.prototype.getName = function(){
return this.name
}
//Dog原型等于構造函數(shù)Animal實例(實現(xiàn)繼承)
Dog.prototype = new Animal()
//構建新實例
var xiaohuang = new Dog()
實例秩冈、構造函數(shù)蜒简、原型之間的關系。
xiaohuang.__proto__ === Dog.prototype
Dog.prototype.__proto__ === Animal.prototype
Animal.prototype === Object.prototype
Object.prototype.proto === null
//總結:xiaohuang這個Dog的實例繼承了Animal,Animal繼承了Object
console.log(xiaohuang.type)//'animal'
console.log(xiaohuang.name)//'dog'
總結:
兩個構造函數(shù)漩仙,將其中一個構造函數(shù)的實例賦值給另一個構造函數(shù)的原型,這樣另一個原型就有了指向前面那一個構造函數(shù)的原型的指針(創(chuàng)建的實例也會有同樣的原型鏈)犹赖,查找屬性和方法會根據(jù)原型鏈來進行搜索队他,先搜索實例,再搜索原型峻村,最后是原型的原型.....麸折,注意此時實例的constructor指向了最開始的構造函數(shù)垢啼,而不是創(chuàng)建實例的函數(shù)(為了代碼的嚴謹吞瞪,可以設置為創(chuàng)建此實例的函數(shù))惯疙。實現(xiàn)的本質是重寫原型對象对碌,代之以一個新類型的實例朽们。原型鏈繼承不僅會繼承原型上面的屬性和方法,還會繼承實例上的方法和屬性。
Object原型對象,怎么讓每個對象都指向這個共有屬性歹袁?
答:每個對象都有一個__proto__
屬性指向了共有屬性乏矾。
對象和數(shù)值對象:
var o1 = new Number(1);
var o2 =new Object(1);
o1===o2 //false
o1.toString()===o2.toString() //true
兩個數(shù)值對象不相等,因為stack存放的heap地址值不同凄硼,但使用的toString
函數(shù)是公共屬性(Number的原型的屬性)痒给,因此相等(new Object(1)
自動轉換成了數(shù)值對象)。
Object()函數(shù)不加new操作符會根據(jù)傳入的參數(shù)生成相應的數(shù)據(jù)類型的對象,對于Object函數(shù)來說爬橡,加不加new都是一樣的效果。
String()函數(shù)不加new操作符是將你給定的參數(shù)變成string常量(基本類型,非對象),而加上new會生成String對象(復雜類型,字符串對象)搀暑,除了Object函數(shù)以外脉让,其他的標準庫函數(shù)都是一樣的效果埠啃。
數(shù)值的toString()
方法和對象的toString()
方法并不相等博秫,因為前者可以加進制參數(shù)而且會被先搜尋到朴爬。
調(diào)用方法時逸爵,會經(jīng)過一層一層的查找周蹭。
Number String Boolean Object
都有自己的共有屬性,____proto____
都是先指向了自己的共有屬性,共有屬性的__proto____
再指向了對象。如果對象的共有屬性沒有被引用的話囚玫,必將會被回收读规,
object.prototype
指向(引用)這個共有屬性(原型)當你聲明一個對象時抓督,JS引擎除了在棧內(nèi)存里面存放一個hash之外,還將
____proto____
指向了你該有的共有屬性(原型)束亏。
不寫代碼就有prototype
在沒有代碼的時候铃在,Number.prototype
、Object.prototype
碍遍、String.prototype
定铜、Boolean.prototype
都引用了各自的共有屬性(保證不被垃圾回收機制回收),是JS定義的,不可更改的怕敬。而你的對象____proto____
則指向了對應數(shù)據(jù)類型的prototype
揣炕。
初始化數(shù)據(jù)類型里面還有一個Function.prototype
,存儲了函數(shù)的原型(共有方法)东跪,Function
也是一個對象畸陡,Function.__proto__
指向了Function.prototype
鹰溜,使用函數(shù)初始化對象時(new Function()
),該函數(shù)對象的____proto____
就指向了Function.prototype
(因為Function是Object的構造函數(shù))丁恭,而Function.prototype
里面的____proto____
就指向了Object.prototype
曹动。
繼承方式:子類構造函數(shù).prototype= new 父類構造函數(shù) ,簡單說就是將父類的實例賦值給子類的原型涩惑,這樣子類的____proto____
屬性
目前組合繼承用的最多仁期,構造函數(shù)模式定義實例屬性(不可共享),原型模式則定義方法和共享的屬性竭恬。
現(xiàn)在你明白什么是原型和原型鏈了嗎跛蛋?:)