全局對(duì)象與原型鏈

全局對(duì)象

在ECMA Script 標(biāo)準(zhǔn)中,全局對(duì)象叫做 global, 全局對(duì)象不一定是 window,但在瀏覽器(早于ES)環(huán)境中,默認(rèn)的全局對(duì)象就是 window

global.parseInt
global.parseFloat

windows

實(shí)際是一個(gè)哈希表,有許多屬性,我們稱為全局變量。

全局變量

常用全局變量舉例,詳細(xì)使用可查看MDN,實(shí)際應(yīng)用中可以省略 window,比如 window.alert() 可以直接寫(xiě)成 alert()。

ECAM Script 規(guī)定 私有 (chrome/Firefox)
global.parseInt() window.alert (彈框提示)
global.parseFloat() window.prompt(用戶填寫(xiě))
global.Number() window.confirm(確認(rèn))
global.String() window.console.log
global.Boolean() window.console.dir
global.Object() window.document(文檔,DOM,W3C規(guī)定)
global.Symbol() window.document.createElement
global.setTimeout(function(){},time) window.document.getElementById
window.history(瀏覽器,BOM)

ECMA規(guī)定里的幾個(gè)重要函數(shù)/對(duì)象

Number()

  1. 基本類(lèi)型的Number

    var n1 = Number('1') 
    
  2. new創(chuàng)建的實(shí)例對(duì)象

    var n2 = new Number(1)
    
內(nèi)存圖

變量 n1

  • stack棧內(nèi)存:1
  • heap堆內(nèi)存:/

變量 n2

  • stack棧內(nèi)存:Address 44

  • heap堆內(nèi)存:

    Address  44: 
    // A hash
    // Number {1}
    // --------------------------
    // __proto__: Number
    // [[PrimitiveValue]]: 1
    // --------------------------
    // 相關(guān)屬性舉例:
    n2.toString() // "1"
    n2.ValueOf()  // 1
    n2.toExponential()  // 1e+0
    n2.toFixed(2) // 1.000
    

然而在Java Script中,我們沒(méi)必要使用第二種方式申明對(duì)象,盡管 n1 只是一個(gè)簡(jiǎn)單類(lèi)型的數(shù)值,不是一個(gè)對(duì)象,但 n2 所含的這些屬性,在Java Script中,我們也可以和 n2 一樣簡(jiǎn)單通過(guò)點(diǎn)運(yùn)算符(.)得到。

n1.toString()
n1.ValueOf()
n1.toExponential()
n1.toFixed(2)
// 實(shí)際上述屬性不存在,但是JS創(chuàng)建了一個(gè)臨時(shí)對(duì)象
// temp = new Number(n1)
// n1.toString() 實(shí)際上得的的結(jié)果是 temp.toString()的結(jié)果
// temp 是臨時(shí)存在的對(duì)象,用完就會(huì)被抹除

所以,在Java Script中,簡(jiǎn)單類(lèi)型的數(shù)據(jù)也可以添加不存在的屬性,然而實(shí)際是在給臨時(shí)創(chuàng)建的對(duì)象添加屬性,用完就被抹除,所以當(dāng)我們?cè)僭噲D讀取之前添加的屬性時(shí),原來(lái)的臨時(shí)對(duì)象已經(jīng)被抹除,然而在新的臨時(shí)對(duì)象中,該屬性不存在(可內(nèi)存圖輔助加深理解)

n1.balabala = ''
n1.balabala // undefined

String()

  1. 基本類(lèi)型的String

    var s1 = String('qw ') 
    
  2. new創(chuàng)建的實(shí)例對(duì)象

    var s2 = new String(s1)
    console.log(s2)
    // A hash
    // String {"qw "}
    // --------------------------
    // __proto__: String
    // [[PrimitiveValue]]: "qw "
    // 0: "q"
    // 1: "w"
    // 2: " "
    // length: 3
    // --------------------------
    // 相關(guān)屬性舉例:
    s2.charAt(1)  // "w",獲取某一個(gè)索引對(duì)應(yīng)的字符,等價(jià)于 s2[1]
    s2.charAt(3)  // ""
    s2.charCodeAt(1) // 119,獲取某一個(gè)索引對(duì)應(yīng)的字符的編碼碼點(diǎn),十進(jìn)制Unicode
    s2.charCodeAt(3) // NaN
    s2.charCodeAt(1).toString(16) // 77,十六進(jìn)制的Unicode
    s2.trim() // "qw",去掉字符串內(nèi)空格
    s2.concat("asd") // "qw asd",連接字符串
    s2.slice(0,2) // "qw",從s2[0]開(kāi)始算的前兩個(gè)字符串
    s2.replace(" ","e") // "qwe"," "替換為"e"的一個(gè)字符串
    s2.substring(0,2) // "qw",以s2[0]我首字母,s2[2](不包括)結(jié)束的字符串
    s2.includes(" ")  // true,返回一個(gè)布爾值判斷是否" "在s2中
    

更多 String 屬性 可以google搜索 String MDN 學(xué)習(xí)。

Boolean()

  1. 基本類(lèi)型的Boolean

    var b1 = true
    
  2. new創(chuàng)建的實(shí)例對(duì)象

    var b2 = new Boolean(true)
    console.log(b2)
    // A hash
    // Boolean {true}
    // --------------------------
    // __proto__: Boolean
    // [[PrimitiveValue]]: true
    // --------------------------
    // 相關(guān)屬性舉例:
    b2.valueOf()  // true
    b2.toString()  // "true"
    // 注意:b2是對(duì)象,不是falsy值
    

Object()

var o1 = {}
var o2 = new object({})
o1 === o2  
// false
// o1 和 o2 沒(méi)有任何區(qū)別,但并不相等
// 通過(guò)內(nèi)存圖分析,事先申明的對(duì)象,在stack棧內(nèi)存中,兩者在堆內(nèi)存的地址不同
// 自然在heap堆內(nèi)存中,不同地址引用的空對(duì)象也不是同一個(gè)
變量 Stack棧內(nèi)存 Heap堆內(nèi)存
o1 堆內(nèi)存地址1 -------> {}
o2 堆內(nèi)存地址2 -------> {}

往往事先聲明的對(duì)象一般都是不相等的,除非兩者把其中一個(gè)變量堆內(nèi)存的地址()賦給另外一個(gè)變量。

var o1 = {}
var o2 = o1
o1 === o2 
// true
// o1 和 o2 都指向了堆內(nèi)存中同一個(gè)空對(duì)象
// o1的堆內(nèi)存地址賦值給了o2
// 賦值操作后,兩者保存了同一個(gè)堆內(nèi)存對(duì)象地址
// 注意:所以改變?nèi)魏我粋€(gè)變量都會(huì)互相影響,除非對(duì)象地址發(fā)生變化
o2.name = 'aaa'
console.log(o2) // {name: 1}
console.log(o1) // {name: 1}

JavaScript 原型鏈

我們從上述介紹的幾個(gè)函數(shù)對(duì)象(Number(),String()等)中發(fā)現(xiàn) ,這實(shí)例對(duì)象都有一些共有屬性,比如 toString(),valueOf() 等等。如果把這些屬性都當(dāng)作對(duì)象的私有屬性,給每個(gè)對(duì)象單獨(dú)存這些共有的屬性,顯然是十分浪費(fèi)內(nèi)存的。JS的做法是,每一個(gè)實(shí)例對(duì)象都有一個(gè)私有的屬性(__proto__)來(lái)表示這些共有的屬性,但并不直接存這些共有的屬性,而是指向它的構(gòu)造函數(shù)的原型對(duì)像(prototype)。

舉例:

var n1 = new Number()
var n2 = new Number()
n1 === n2 // false
n1.toString === n2.toString // true
n1.__proto__ === n2.__proto__ // true 
n1.__proto__ === Number.prototype // true
// n1,n2都指向一個(gè)原型對(duì)象 Number.prototype

同時(shí),我們也注意到了除了Object對(duì)象之外,比如Number對(duì)象,它的原型對(duì)象(Number.prototype)也有一個(gè)自己的原型對(duì)象。

Number.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ // null

Number對(duì)象的原型對(duì)象是一個(gè)Object對(duì)象,Object對(duì)象的原型對(duì)象是null。

延伸開(kāi)來(lái),每一個(gè)實(shí)例對(duì)象都有一個(gè)__proto__指向它的構(gòu)造函數(shù)的原型對(duì)象(prototype),然后其原型對(duì)象也有一個(gè)自己的原型對(duì)象,這樣層層往上直到Object的原型對(duì)象為null,null沒(méi)有原型對(duì)象,并且我們稱其為原型鏈的最后環(huán)節(jié)。

通過(guò)上文對(duì) 幾個(gè)函數(shù)/對(duì)象的了解 (Number,String,Boolean)我們可以發(fā)現(xiàn),這些對(duì)象都有他們獨(dú)特的屬性。也有一些他們共同擁有的屬性(Object的共有屬性)。上面,我們已經(jīng)分析了Number對(duì)象的原型鏈,可以看到Number對(duì)象的原型對(duì)象是Number.prototype,里面存了Number對(duì)象獨(dú)有的共有屬性。

n1.__proto__ === Number.prototype // true

Number對(duì)象的原型對(duì)象的原型對(duì)象是Object.prototype,里面存了Object對(duì)象普遍有的共有屬性。

n1.__proto__.__proto__ === Object.prototype // true

當(dāng)JS需要調(diào)用某個(gè)對(duì)象的屬性時(shí),它不單單在該對(duì)象上搜尋,同時(shí)也會(huì)搜尋它的原型以及原型的原型,層層往上直到找到或者搜到原型鏈的頂點(diǎn),所以我們才能直接調(diào)用Number對(duì)象的toFixedNumber.prototype中)和toString屬性(Object.prototype中)

參考自:繼承與原型鏈 - JavaScript | MDN

區(qū)分 __proto__prototype

簡(jiǎn)單來(lái)說(shuō)__proto__總是跟在一個(gè)對(duì)象后面,它是用來(lái)表示指向這個(gè)對(duì)象的原型,而prototype,我們可以發(fā)現(xiàn)它總是跟在一個(gè)構(gòu)造函數(shù)的后面,prototype是自帶在瀏覽器中的,即便不寫(xiě)任何代碼,prototype也是存在的,且無(wú)法修改。

構(gòu)造函數(shù)(constructor)

構(gòu)造函數(shù)主要有以下幾個(gè)特點(diǎn)

  1. 首字母大寫(xiě)
  2. new來(lái)調(diào)用生成實(shí)例對(duì)象
  3. 函數(shù)內(nèi)部使用this對(duì)象來(lái)指向要生成的實(shí)例對(duì)象

下表概括了JS中數(shù)據(jù)類(lèi)型的構(gòu)造函數(shù)

數(shù)據(jù)類(lèi)型 構(gòu)造函數(shù)
數(shù)值 Number
字符串 String
布爾值 Boolean
null 無(wú)
undefined 無(wú)
符號(hào) 無(wú)
函數(shù)(對(duì)象) Function
數(shù)組(對(duì)象) Array
普通對(duì)象 Object

結(jié)合這些,我們就能更清晰地解釋原型鏈。

推論

舉例:

var s = new String()
s.__proto__ === String.prototype // true

上述例子中,被構(gòu)造函數(shù)String創(chuàng)造的實(shí)例對(duì)象s__proto__指向Stringprototype屬性。

String.prototype 就表示String的原型對(duì)象。

延伸開(kāi)來(lái),被構(gòu)造函數(shù)Func創(chuàng)造的實(shí)例對(duì)象func__proto__指向Funcprototype屬性。

Func.prototype 就表示Func的原型對(duì)象,也就是

var func = new Func()
func.__proto__ === Func.prototype // true
延伸1

同時(shí)我們注意到構(gòu)造函數(shù)Func的原型對(duì)象 Func.prototype本身也是一個(gè)Object(對(duì)于Number,String等),所以當(dāng)我們把它當(dāng)做構(gòu)造函數(shù)Object的一個(gè)示例對(duì)象,我們可以得到:

舉例:

Number.prototype.__proto__ === Object.prototype // true
Function.prototype.__proto__ === Object.prototype // true

這個(gè)結(jié)果也就解釋了上文中為何Number對(duì)像的原型對(duì)象的__proto__指向了Objectprototype屬性。

延伸2

函數(shù)對(duì)象的構(gòu)造函數(shù)是Function,而構(gòu)造函數(shù)比如Number都是Function的實(shí)例對(duì)象。所以我們可以得到以下結(jié)果:

Number.__proto__ === Function.prototype // true
String.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true
Boolean.__proto__ === Function.prototype // true
Function.__proto__ === Function.prototype // true
// 在這,我們可以把Function函數(shù)本身也看成被其本身創(chuàng)造的一個(gè)實(shí)例對(duì)象
注意

我們要注意的是,Object的原型對(duì)象的__proto__指向的是null而不是Object.prototype

Object.prototype.__proto__ === null // true
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容