年紀(jì)大了記性不好(其實(shí)是腦子不好使,但又不想承認(rèn)),有些東西總是容易忘,所以為了便于之后查看干脆記下來(lái),用自己的語(yǔ)言再把一些概念整理一下,都是自己寫(xiě)的,以后再看這些文字也會(huì)有親切感(好像很有道理的亞子)~
基本類型:String、Boolean、Number、Undefined、Null、Symbol
引用類型:Object
下面我會(huì)詳細(xì)說(shuō)一下這七種數(shù)據(jù)類型中可能你不太知道的一些細(xì)節(jié)。
1. String
儲(chǔ)存結(jié)構(gòu)
計(jì)算機(jī)是以二進(jìn)制存儲(chǔ)以及發(fā)送接收數(shù)據(jù)的。二進(jìn)制的 1位,也叫做 1bit 。它是計(jì)算機(jī)內(nèi)部存儲(chǔ)的最基本的單位。
計(jì)算機(jī)只能處理數(shù)字,如果想要處理文本,必須將文本轉(zhuǎn)換為數(shù)字才能處理,在計(jì)算機(jī)中 1bit 能表示2個(gè)狀態(tài)(0或1),1Byte 等于 8bit,所以 1Byte 能表示 2^8 - 1 個(gè)整數(shù),也就是255個(gè)。如果想表示更大的數(shù)字,就需要更多的字節(jié)數(shù),比如 2Byte 能表示 2^16 - 1 ,也就是 65535個(gè)整數(shù)。最早只有127個(gè)字符被編碼到計(jì)算機(jī)里,也就是大小寫(xiě)英文字母、數(shù)字和一些其他字符,這個(gè)編碼表就是 ASCII 表。
但如果要表示中文,那么 1Byte 顯然是不夠的,至少需要 2Byte ,所以中國(guó)制定了 GB2312 編碼,但每個(gè)國(guó)家如果都制定一個(gè)自己的編碼表,那么就沒(méi)辦法正確顯示多語(yǔ)言混合的文本。為了解決這個(gè)問(wèn)題,Unicode 編碼應(yīng)運(yùn)而生。它把所有語(yǔ)言統(tǒng)一到一個(gè)編碼里,采用 2Byte 表示一個(gè)字符,即最多可以表示 2^16 - 1 ,也就是 65535 個(gè)字符。這樣基本上可以覆蓋世界上常用的文字,如果要表示更多的文字,也可以采用 4Byte 進(jìn)行編碼,這是一種通用的編碼規(guī)范 。
JS 中的字符也采用Unicode編碼,也就是js中的中英文字符都占用 2Byte(16bit)大小。
在 JS 中的二進(jìn)制數(shù)據(jù)儲(chǔ)存中,二進(jìn)制前三位為 100
代表字符串
基本包裝類型
在 js 中,只有引用類型才有屬性或者方法,基本類型理論上沒(méi)有屬性或方法,而字符串又屬于基本類型,但為什么字符串能調(diào)用一些屬性和方法呢?
原因是 js 為了方便對(duì)字符串進(jìn)行操作,ECMA 提供了一個(gè)基本包裝類型 String對(duì)象。它是一種特殊的引用類型, 當(dāng) js 引擎需要讀取或操作一個(gè)字符串時(shí),他會(huì)在內(nèi)部創(chuàng)建一個(gè) String類型的包裝對(duì)象實(shí)例,然后調(diào)用這個(gè)實(shí)例的屬性或方法并返回結(jié)果后,再立即清除這個(gè)實(shí)例。
這也是為什么 字符串明明是一個(gè)基本類型 卻能調(diào)用屬性和方法的原因。
幾個(gè)Unicode問(wèn)題總結(jié)
基本概念
Unicode 是目前最常見(jiàn)的字符編碼,它用一個(gè)碼位映射一個(gè)字符。在 js 中,Unicode 碼位范圍為 '\u{0000}'
~ '\u{10ffff}'
,可以表示超過(guò)110萬(wàn)個(gè)字符。格式為 '\u{十六進(jìn)制數(shù)字}'
console.log('\u{0041}') // 'A'
console.log('\u{0061}') // 'a'
console.log('I \u{2661} Hagan') // 'I ? Hagan'
console.log('\u{20bb7}') // '??'
Unicode 最前面的 65536 個(gè)字符位稱為 基本多文種平面,它的碼位范圍為 '\u{0000}'
~ '\u{ffff}'
,最常見(jiàn)的字符都放在這個(gè)平面上。
剩下的字符都放在 輔助平面 上,碼位范圍為 '\u{010000}'
~ '\u{10ffff}'
判斷是否為輔助平面的方法為十六進(jìn)制數(shù)字的位數(shù)是否超過(guò)4位。
字符串長(zhǎng)度問(wèn)題
- 解決代理對(duì)長(zhǎng)度問(wèn)題
在內(nèi)部,JavaScript 將輔助平面內(nèi)的字符表示為代理對(duì),并將單獨(dú)的代理對(duì)分開(kāi)為單獨(dú)的 “字符”,所以代理對(duì)的length屬性可能與我們的預(yù)期不一致,比如:
const str = '\u{20BB7}'
console.log(str) // '??'
console.log(str.length) // 2
而我們想獲取的長(zhǎng)度應(yīng)該為 1,這里可以使用以下方法正確獲取長(zhǎng)度
const str = '\u{20BB7}'
console.log(Array.from(str).length) // 1
- 解決組合標(biāo)記長(zhǎng)度問(wèn)題
\u{0307}
表示 q?? 上面的點(diǎn), \u{0323}
表示 q?? 下面的點(diǎn),這三個(gè)字符共同組成了一個(gè) q?? ,如下代碼
const str = 'q\u{0307}\u{0323}'
console.log(str) // `q??`
console.log(str.length) // 3
我們期待拿到的長(zhǎng)度應(yīng)該為1,可實(shí)際拿到的length為3,這里可以使用以下方法獲取長(zhǎng)度
const str = 'q\u{0307}\u{0323}'
const regex = /(\P{Mark})(\p{Mark}+)/gu
const trim = str.replace(regex, ($0, $1, $2) => $1)
console.log(Array.from(str).length) // 1
注意:此方法無(wú)法處理表情字符序列組合 '???????????'
const getStringLength = function (string) {
const regex = /(\P{Mark})(\p{Mark}+)/gu
const str = string.replace(regex, ($0, $1, $2) => $1)
return Array.from(str).length
}
export default getStringLength
字符串反轉(zhuǎn)
注意:此方法無(wú)法正確處理組合標(biāo)記
const getReverseString = function (string) {
return Array.from(string).reverse().join('')
}
export default getReverseString
一位名叫 Missy Elliot 的聰明的計(jì)算機(jī)科學(xué)家提出了一個(gè)防彈算法來(lái)解決組合標(biāo)記問(wèn)題
根據(jù)碼位獲取字符串
注意:此方法無(wú)法正確處理組合標(biāo)記
String.fromCodePoint(0x20bb7) // '??'
根據(jù)字符串獲取碼位
注意:此方法無(wú)法正確處理組合標(biāo)記
'??'.codePointAt().toString(16) // 20bb7
遍歷字符串
注意:此方法無(wú)法正確處理組合標(biāo)記
for (const item of '??') {
console.log(item) // '??'
}
Number
儲(chǔ)存結(jié)構(gòu)
js采用 IEEE754 標(biāo)準(zhǔn)中的 雙精度浮點(diǎn)數(shù)來(lái)表示一個(gè)數(shù)字,標(biāo)準(zhǔn)規(guī)定雙精度浮點(diǎn)數(shù)采用 64 位存儲(chǔ),即 8 個(gè)字節(jié)表示一個(gè)浮點(diǎn)數(shù)。存儲(chǔ)結(jié)構(gòu)如下圖:

在雙精度浮點(diǎn)數(shù)中,第一位的 1bit符號(hào)位 決定了這個(gè)數(shù)的正負(fù),指數(shù)部分的 11bit 決定數(shù)值大小,小數(shù)部分的 52bit 決定數(shù)值精度。
在 JS 中的二進(jìn)制數(shù)據(jù)儲(chǔ)存中,二進(jìn)制前三位為 010
代表雙精度數(shù)字
數(shù)值范圍
指數(shù)部分為 11bit 也就是 2^11 - 1 = 2047,取中間值進(jìn)行偏移得到 [-1023, 1024],因此這種存儲(chǔ)結(jié)構(gòu)能夠表示的數(shù)值范圍為 2^-1023 至 2^1024,超出這個(gè)范圍無(wú)法表示。轉(zhuǎn)換為科學(xué)計(jì)數(shù)法為:
2^-1023 = 5 × 10^-324
2^1024 = 1.7976931348623157 × 10^308
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
安全整數(shù)
IEEE754 規(guī)定,有效數(shù)字第一位默認(rèn)總是1,但它不保存在 64 位浮點(diǎn)數(shù)之中。所以有效數(shù)字為 52bit + 1 = 53bit。
這意味著js能表示并進(jìn)行精確算術(shù)運(yùn)算的安全整數(shù)范圍為 [-2^53 -1, 2^53 - 1] 即 -9007199254740991 到最大值 9007199254740991 之間的范圍。
Math.pow(2, 53) - 1 // 9007199254740991
-Math.pow(2, 53) - 1 // -9007199254740991
可以通過(guò) Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 來(lái)分別獲取安全整數(shù)最大值和最小值。
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
對(duì)于超過(guò)這個(gè)安全整數(shù)的運(yùn)算,需要使用 BigInt 來(lái)計(jì)算。
console.log(9007199254740991 + 2) // 9007199254740992
console.log(BigInt(9007199254740991) + BigInt(2)) // 9007199254740993n
精度丟失
計(jì)算機(jī)中的數(shù)字都是用二進(jìn)制儲(chǔ)存的,如果要計(jì)算 0.1 + 0.2 那么計(jì)算機(jī)會(huì)分別把 0.1 和 0.2 轉(zhuǎn)成二進(jìn)制,然后相加,最后把相加的結(jié)果轉(zhuǎn)為10進(jìn)制。
但有一些浮點(diǎn)數(shù)轉(zhuǎn)化為二進(jìn)制時(shí)會(huì)出現(xiàn)無(wú)限循環(huán),比如 0.1
0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 10001 無(wú)限循環(huán)
而上面我們說(shuō)過(guò)計(jì)算機(jī)能儲(chǔ)存的小數(shù)位最多只有 53 位,為了盡可能的接近目標(biāo)值,所以采用類似十進(jìn)制四舍五入的方法,在二進(jìn)制中 0舍1入,最終 0.1 儲(chǔ)存到計(jì)算機(jī)中成為以下數(shù)值。
0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 101
0.1 轉(zhuǎn)換成科學(xué)技術(shù)法
(?1)^0 × 2^(-4) × (1.1001100110011001100110011001100110011001100110011010)2
0.2 科學(xué)技術(shù)法表示為
(?1)^0 × 2^(-3) × (1.1001100110011001100110011001100110011001100110011010)2
在浮點(diǎn)數(shù)做加法時(shí)要先進(jìn)行對(duì)位操作,將較小的指數(shù)轉(zhuǎn)化為較大的指數(shù),并將小數(shù)部分右移
(?1)^0 × 2^(-3) × (0.111001100110011001100110011001100110011001100110011010)2
(?1)^0 × 2^(-3) × (1.1001100110011001100110011001100110011001100110011010)2
最終 0.1 + 0.2 在計(jì)算機(jī)中的計(jì)算過(guò)程如下

計(jì)算后 0.1 + 0.2 的結(jié)果為
(?1)^0 × 2^(?2) × (1.0011001100110011001100110011001100110011001100110100)2
然后通過(guò) js 將二進(jìn)制轉(zhuǎn)為10進(jìn)制
(-1)**0 * 2**-2 * (0b10011001100110011001100110011001100110011001100110100 * 2**-52) === 0.30000000000000004 // true
console.log(0.1 + 0.2) ; // 0.30000000000000004
這就是經(jīng)典的 0.30000000000000004 問(wèn)題,0.1 和 0.2 在轉(zhuǎn)換為二進(jìn)制時(shí)由于做了 0舍1入 ,發(fā)生了一次精度丟失,而對(duì)于計(jì)算后的二進(jìn)制又 做了一次 0舍1入 發(fā)生一次精度丟失,因此得到的結(jié)果是不準(zhǔn)確的。
解決辦法:
精度丟失解決辦法就是先將小數(shù)轉(zhuǎn)換成整數(shù),然后用整數(shù)進(jìn)行計(jì)算得到結(jié)果后再轉(zhuǎn)換為小數(shù),用 js 封裝方法如下
math.js
// 判斷number是否為一個(gè)整數(shù)
const isInteger = function (number) {
return Math.floor(number) === number
}
// 四舍五入
const toFixed = function (number, decimalLength = 0) {
var times = Math.pow(10, decimalLength)
var fixed = number * times + 0.5
return parseInt(fixed) / times
}
// 將一個(gè)浮點(diǎn)數(shù)轉(zhuǎn)成整數(shù),返回整數(shù)和倍數(shù)
const toInteger = function (floatNumber) {
const numberInfo = { times: 1, number: 0 }
const isNegative = floatNumber < 0
if (isInteger(floatNumber)) {
numberInfo.number = floatNumber
return numberInfo
}
const stringFloatNumber = String(floatNumber)
const dotPosition = stringFloatNumber.indexOf('.')
const length = stringFloatNumber.substr(dotPosition + 1).length
numberInfo.times = Math.pow(10, length)
numberInfo.number = toFixed(Math.abs(floatNumber) * numberInfo.times)
if (isNegative) numberInfo.number = -numberInfo.number
return numberInfo
}
// 加
export const add = function (number1, number2, decimalLength = 0) {
const { number: num1, times: times1 } = toInteger(number1)
const { number: num2, times: times2 } = toInteger(number2)
const maxTimes = Math.max(times1, times2)
let result
if (times1 === times2) result = (num1 + num2) / maxTimes
if (times1 > times2) result = (num1 + num2 * (times1 / times2)) / maxTimes
if (times1 < times2) result = (num1 * (times2 / times1) + num2) / maxTimes
return toFixed(result, decimalLength)
}
import { add } from './math.js'
console.log(add(0.1, 0.2, 1)) // 0.3
特殊數(shù)值變量
JavaScript 提供了幾個(gè)特殊數(shù)值,用于判斷數(shù)字的邊界和其他特性
Number.MAX_VALUE // JavaScript 中的最大值
Number.MIN_VALUE // JavaScript 中的最小值
Number.MAX_SAFE_INTEGER // 最大安全整數(shù),為 2^53 - 1
Number.MIN_SAFE_INTEGER // 最小安全整數(shù),為 -(2^53 - 1)
Number.POSITIVE_INFINITY // 對(duì)應(yīng) Infinity,代表正無(wú)窮
Number.NEGATIVE_INFINITY // 對(duì)應(yīng) -Infinity,代表負(fù)無(wú)窮
Number.EPSILON // 是一個(gè)極小的值,用于檢測(cè)計(jì)算結(jié)果是否在誤差范圍內(nèi)
Number.NaN // 表示非數(shù)字,NaN與任何值都不相等,包括NaN本身
Infinity // 表示無(wú)窮大,分 正無(wú)窮 Infinity 和 負(fù)無(wú)窮 -Infinity
四舍五入
有時(shí)我們需要對(duì)一些數(shù)字進(jìn)行四舍五入,而這些數(shù)字可能包含小數(shù)
Math.round(number) 方法無(wú)法對(duì)小數(shù)進(jìn)行計(jì)算
Number.toFixed() 方法實(shí)際上采用四舍六入五成雙的規(guī)則實(shí)現(xiàn),存在一些缺陷,具體可看一下這篇文章
以上兩個(gè)方法有時(shí)候無(wú)法滿足我們的需求,所以封裝以下方法
math.js
// 四舍五入
export const toFixed = function (number, decimalLength = 0) {
var times = Math.pow(10, decimalLength)
var fixed = number * times + 0.5
return parseInt(fixed) / times
}
import { toFixed } from './math.js'
toFixed(0.2286298683746, 3) // 0.229
Boolean
儲(chǔ)存結(jié)構(gòu)
在 JS 中的二進(jìn)制數(shù)據(jù)儲(chǔ)存中,二進(jìn)制前三位為 110
代表布爾值
基本概念
Boolean 只有兩個(gè)類型,true、false。在js中所有類型的值都能轉(zhuǎn)換成 Boolean 值。如下代碼
Boolean('') // false // 除了空字符串意外,其他字符串都為true
Boolean(0 || NaN) // false // 除了0與NaN,其他數(shù)字都為true
Boolean(undefined) // false
Boolean(Symbol()) // true
Boolean(null) // false
Boolean({} && []) // true // 所有引用類型都為true
隱式類型轉(zhuǎn)換
當(dāng)使用以下操作符獲取 Boolean 結(jié)果時(shí),在過(guò)程中 js 內(nèi)部會(huì)先進(jìn)行隱式類型轉(zhuǎn)換,再使用轉(zhuǎn)換后的結(jié)果進(jìn)行對(duì)比,最終確定為 true、 false。
>
>=
<
<=
==
!=
if else
while
4 > 3 // 4 > 3 // true
'4' > 3 // Number('4') > 3 // 4 > 3 // true
'a' > 'b' // 'a'.codePointAt() > 'b'.codePointAt() // 97 > 98 // false
true > 2 // Number(true) > 2 // 1 > 2 // false
undefined > 0 // Number(undefined) > 0 // NaN > 0 // false
null > 0 // Number(null) > 0 // NaN > 0 // false
new Date() > 100 // new Date().valueOf() > 100 // 1587608237665 > 100 // true
if (1) { } // Boolean(1) // true
if (!(1 > '10')) { } // !(1 > Number('10')) // !(1 > 10) //!false // true
顯式類型轉(zhuǎn)換
與隱式類型轉(zhuǎn)換相對(duì)應(yīng),當(dāng)使用 !
操作符時(shí),會(huì)將變量強(qiáng)制轉(zhuǎn)換為 Boolean 值,并進(jìn)行取反操作。
!1 // false
!!1 // true
!undefined // true
!!undefined // false
全等操作符
===
!==
全等操作符在進(jìn)行對(duì)比出Boolean結(jié)果時(shí)不會(huì)進(jìn)行隱式類型轉(zhuǎn)換,為了避免因隱式類型轉(zhuǎn)換帶來(lái)的預(yù)期之外的情況,推薦在實(shí)際項(xiàng)目中使用全等操作符來(lái)進(jìn)行對(duì)比。
'1' == 1 // Number('1') == 1 // 1 == 1 // true
'1' === 1 // false
true != 1 // Number(true) != 1 // 1 != 1 // false
true !== 1 // true
邏輯操作符
以下兩個(gè)操作符為邏輯操作符
&&
||
1 && 2 // 2 // 取最后一個(gè)為 true 的值
1 || 2 // 1 // 取第一個(gè)為 true 的值
Symbol
儲(chǔ)存結(jié)構(gòu)
這是 ES6 新增的一種數(shù)據(jù)類型,它的字面意思為,符號(hào),標(biāo)記。代表獨(dú)一無(wú)二的值。
基本概念
Symbol 類型可以作為對(duì)象的 key 值。Symbol 最常用的方式就是作為唯一 id。
class Person {
constructor (name, age) {
this.name = name
this.age = age
this.id = Symbol('身份證號(hào)')
}
}
const hagan1 = new Person('hagan', 25)
hagan1.id // Symbol(身份證號(hào))
const hagan2 = new Person('hagan', 24)
hagan2.id // Symbol(身份證號(hào))
hagan1.id === hagan2.id // false // 身份證號(hào)永遠(yuǎn)唯一
Symbol 也可以當(dāng)成私有變量來(lái)使用,但它并不是真的為私有。
Girl.js
const _age = Symbol('女生的年齡')
export default class {
constructor (name, age) {
this.name = name
this[_age] = age // 女生實(shí)際年齡
}
getAge () {
return 18 // 女生告訴你的年齡
}
}
rita.js
import 'Girl' from './Girl.js'
const rita = new Girl('rita', 28)
// 女生實(shí)際年齡只有她自己知道,而你毫無(wú)辦法,因?yàn)榕肋h(yuǎn) 18 歲
rita.age // undefined
rita[Symbol('女生的年齡')] // undefined
rita.getAge() // 18
// 除非你開(kāi)掛
const [ _age ] = Object.getOwnPropertySymbols(rita)
rita[_age] // 36
Symbol 作為對(duì)象的屬性,不會(huì)被 for in
for of
循環(huán)到,也不會(huì)被 Object.keys()
Object.getOwnPropertyNames()
JSON.stringify()
返回,但是它也不是任何辦法都無(wú)法訪問(wèn), Object.getOwnPropertySymbols(object)
方法能夠獲取到對(duì)象的所有 Symbol 類型的屬性名。
Symbol.for()
Symbol.for()
也可以生成 Symbol 值,他與直接調(diào)用 Symbol()
唯一的區(qū)別就是,Symbol.for()
生成的值不能作為唯一id
const hagan1 = Symbol.for('hagan')
const hagan2 = Symbol.for('hagan')
hagan1 === hagan2 // true
Symbol.keyFor()
返回通過(guò) Symbol.for()
方法創(chuàng)建的 Symbol 類型的 key 值
const hagan = Symbol.for('hagan')
Symbol.keyFor(hagan) // 'hagan'
Undefined
儲(chǔ)存結(jié)構(gòu)
基本類型之一的 Undefined 只擁有一個(gè)值 undefined
,代表未定義的值。
let name
console.log(name) // undefined
const hagan = { }
console.log(hagan.job) // undefined
const arr = [ ]
console.log(arr[0]) // undefined
(function (a, b) {
console.log(a) // 1
console.log(b) // undefined
})(1, 2)
凡是未被定義和賦值的變量、屬性或參數(shù),都默認(rèn)為 undefined
Null
儲(chǔ)存結(jié)構(gòu)
在 JS 中的二進(jìn)制數(shù)據(jù)儲(chǔ)存中,Null 的全部數(shù)位都為 0
基本概念
基本類型之一的 Null 只擁有一個(gè)值 null
,代表空值。表示一個(gè)變量被人為重置為空對(duì)象,在內(nèi)存中的表示就是棧中的變量即不是其他5中基本類型,也沒(méi)有引用類型中指向堆中的指針。當(dāng)一個(gè)引用類型變量被賦值為 null
時(shí),原來(lái)的引用類型對(duì)象在堆中處于游離狀態(tài),GC 會(huì)擇機(jī)回收該對(duì)象并釋放內(nèi)存。因此想要回收哪個(gè)變量。就將它設(shè)為 null
就好了
為什么 typeof null 會(huì)被判斷為 object
在 JS 中。數(shù)據(jù)在底層都是以二進(jìn)制儲(chǔ)存,引用類型的二進(jìn)制前三位為 0
,typeof
是根據(jù)這個(gè)特性來(lái)進(jìn)行判斷類型的工作,可這里有一個(gè)問(wèn)題就是,null
類型所有位數(shù)都為 0
,所以它的前三位也為 0
,所以 null
會(huì)被判斷為 object
Object
基本概念
Object類型 也叫引用類型,是一組沒(méi)有特定順序的值的集合。
儲(chǔ)存結(jié)構(gòu)
在JS中,基本類型的實(shí)際值儲(chǔ)存在 棧 中,而引用類型的實(shí)際值儲(chǔ)存在 堆 中。棧中儲(chǔ)存的只有指向到堆中的 指針,這也是Object類型也被稱為 引用類型 的原因。
在 JS 中的二進(jìn)制數(shù)據(jù)儲(chǔ)存中,二進(jìn)制前三位為 000
代表引用類型
JS中的堆內(nèi)存與棧內(nèi)存
請(qǐng)看下面的代碼
const num = 1
console.log(num) // 1
num = 2 // 報(bào)錯(cuò)
const hagan = { name: 'hagan' }
console.log(hagan) // { name: 'hagan' }
hagan.name = 'han'
console.log(hagan) // { name: 'han' }
const不是定義常量么?為什么還能改?這時(shí)候就要涉及到 JS 中的堆內(nèi)存與棧內(nèi)存了
在js引擎中對(duì)變量的存儲(chǔ)主要有兩種位置,堆內(nèi)存和棧內(nèi)存。
棧內(nèi)存主要用于存儲(chǔ)各種基本類型的變量,包括Boolean、Number、String、Undefined、Null,以及對(duì)象變量的指針。
而堆內(nèi)存主要負(fù)責(zé)像對(duì)象Object這種變量類型的存儲(chǔ),如下圖

引用類型的數(shù)據(jù)的地址指針是存儲(chǔ)于棧中的,當(dāng)我們想要訪問(wèn)引用類型的值的時(shí)候,需要先從棧中獲得對(duì)象的地址指針,然后在通過(guò)地址指針找到堆中的所需要的數(shù)據(jù)。
因此當(dāng)我們定一個(gè) const
常量時(shí),不可改變的只是 棧內(nèi)存 中的數(shù)據(jù),但 堆內(nèi)存 中的數(shù)據(jù)還是可以通過(guò)變量的引用進(jìn)行改變的。
總結(jié):棧內(nèi)存 用來(lái)儲(chǔ)存基本類型,以及引用類型的指針,堆內(nèi)存 用來(lái)儲(chǔ)存引用類型數(shù)據(jù)。
對(duì)象拷貝
深拷貝
const hagan = { name: 'hagan', age: 25 }
const haganDeepClone = JSON.parse(JSON.stringify(hagan))
console.log(haganDeepClone === hagan) // false
淺拷貝
const hagan = { name: 'hagan', age: 25 }
const haganClone = Object.assign({}, hagan)
console.log(haganClone === hagan) // false
數(shù)據(jù)屬性與訪問(wèn)器屬性
數(shù)據(jù)屬性
const hagan = { age: 22 }
Object.defineProperty(hagan, 'name', {
configurable: false, // 能否通過(guò)delete刪除,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
enumerable: false, // for in 能否循環(huán)到,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
writable: false, // 是否可修改,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
value: 'hagan' // 屬性值,默認(rèn)為undefined
})
delete hagan.name
console.log(hagan.name) // 'hagan'
for (let attr in hagan) {
console.log(hagan[attr]) // 22
}
hagan.name = 'rita'
console.log(hagan.name) // 'hagan'
訪問(wèn)器屬性
const hagan = { age: 25 }
Object.defineProperty(hagan, 'name', {
get () {
return this._name
},
set (value) {
this._name = value
}
})
hagan.name = 'hagan'
console.log(hagan) // { age: 25, _name: 'hagan' }
console.log(hagan.name) // 'hagan'
一些Object方法整理
Object.create()
以第一個(gè)參數(shù)為原型,創(chuàng)建新對(duì)象??捎糜谠屠^承
function Animal (type) {
this.type = type
}
Animal.prototype.getType = function () {
return this.type
}
function People (name) {
Animal.call(this, 'people') // 繼承屬性
this.name = name
}
People.prototype = Object.create(Animal.prototype)
People.prototype.getName = function () {
return this.name
}
const hagan = new People('hagan')
hagan.getName() // 'hagan'
hagan.getType() // 'people'
Object.defineProperty()
用于定義對(duì)象的數(shù)據(jù)屬性和訪問(wèn)器屬性
const hagan = { age: 25 }
Object.defineProperty(hagan, 'name', {
get () {
return this._name
},
set (value) {
this._name = value
}
})
hagan.name = 'hagan'
console.log(hagan) // { age: 25, _name: 'hagan' }
console.log(hagan.name) // 'hagan'
Object.defineProperties()
用于定義對(duì)象的數(shù)據(jù)屬性和訪問(wèn)器屬性
const hagan = { age: 25 }
Object.defineProperties(hagan, {
name: {
value: 'hagan'
}
})
hagan.name = 'hagan'
console.log(hagan) // { age: 25, _name: 'hagan' }
console.log(hagan.name) // 'hagan'
Object.getOwnPropertyDescriptor()
獲取對(duì)象某屬性的數(shù)據(jù)屬性或訪問(wèn)器屬性
const hagan = { age: 22 }
Object.defineProperty(hagan, 'name', {
value: 'hagan'
})
Object.getOwnPropertyDescriptor(hagan, 'name') // {value: 'hagan', writable: false, enumerable: false, configurable: false}
Object.getOwnPropertyNames()
獲取所有的屬性名并返回一個(gè)數(shù)組,不包含原型鏈
const hagan = { age: 22 }
Object.defineProperty(hagan, 'name', {
configurable: false, // 能否通過(guò)delete刪除,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
enumerable: false, // for in 能否循環(huán)到,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
writable: false, // 是否可修改,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
value: 'hagan' // 屬性值,默認(rèn)為undefined
})
Object.getOwnPropertyNames(hagan) // ['age', 'name']
Object.keys()
獲取所有的可枚舉屬性名并返回一個(gè)數(shù)組,不包含原型鏈
const hagan = { age: 22 }
Object.defineProperty(hagan, 'name', {
configurable: false, // 能否通過(guò)delete刪除,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
enumerable: false, // for in 能否循環(huán)到,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
writable: false, // 是否可修改,調(diào)用defineProperty前默認(rèn)為true,調(diào)用后默認(rèn)為false
value: 'hagan' // 屬性值,默認(rèn)為undefined
})
Object.keys(hagan) // ['age']
Object.preventExtensions()
使對(duì)象不能添加屬性,但屬性的值可以刪除和修改
const hagan = { age: 22 }
Object.preventExtensions(hagan)
hagan.name = 'hagan'
console.log(hagan.name) // undefined
Object.seal()
使對(duì)象不能添加屬性,也不可以刪除,但可以修改
Object.freeze()
使對(duì)象不能添加屬性,也不可以刪除和修改