日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

JS的7種數(shù)據(jù)類型以及它們的底層數(shù)據(jù)結(jié)構(gòu)

 西北望msm66g9f 2021-01-13

年紀(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)題

  1. 解決代理對(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
  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
  1. 將以上代碼封裝起來(lái),可封裝成以下方法

注意:此方法無(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ì)象不能添加屬性,也不可以刪除和修改

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多