值和類型 八種數據類型 undefined、null、boolean、number、string、symbol、bigint、object 原始值和引用值 原始值:按值訪問。值存儲在棧中。變數賦值傳遞時會創建該值的副本,兩個變數(複製與被覆制)完全獨立。 常見原始值類型:undefined、null ...
目錄
值和類型
八種數據類型
undefined
、null
、boolean
、number
、string
、symbol
、bigint
、object
原始值和引用值
原始值:按值訪問。值存儲在棧中。變數賦值傳遞時會創建該值的副本,兩個變數(複製與被覆制)完全獨立。
常見原始值類型:undefined
、null
、boolean
、number
、string
、symbol
、bigint
。
let num1 = 5
let num1 = num2
num2 = 4 // num1此時不發生改變
引用值:按引用地址訪問。對象存儲在堆中,其引用地址會存儲在棧中指向對象。變數賦值傳遞時會複製其引用地址,兩個變數(複製與被覆制)會指向同一個對象,對象數據的修改會同時反映到兩個變數上。
常見引用類型(object
):Object
、Function
、Array
、Date
、RegExp
等。
let obj1 = { a: 1 }
let obj2 = obj1
obj2.a = 4 // 此時發生改變 obj1.a = 1 => obj1.a = 4
訪問對象的方式
.
點訪問:不支持變數名稱和特殊字元。對象不存在的屬性,通過點訪問賦值後會創建該屬性。[]
中括弧訪問:支持變數名稱和特殊字元。對象不存在的屬性,通過中括弧訪問賦值後也會創建該屬性。
let a = {}
let key = 'sex'
a.name = '小明' // 對象.屬性名
a['age'] = 22 // 對象['屬性名']
a[key] = '男' // 對象[變數名]
console.log(a) // { name: '小明', age: 22 }
相等與全等運算符
==
相等運算符:檢查其兩個操作數是否相等,它會嘗試強制類型轉換並且比較不同類型的操作數。
===
全等運算符:檢查它的兩個操作數是否相等, 全等運算符總是認為不同類型的操作數是不同的。
註意:比較對象時,需要考慮其引用地址。也可用toString()
方法轉換後比較。
({name: '小紅'}).toString() === ({name: '小米'}).toString() => true
相等和不相等操作符轉換規則:
- 如果有一個操作數是布爾值,則在比較相等性之前先將其轉換為數值——
false
轉換為0
,而true
轉換為1
; - 如果一個操作數是字元串,另一個操作數是數值,在比較相等性之前先將字元串轉換為數值;
- 如果一個操作數是對象,另一個操作數不是,則調用對象的
valueOf()
方法(返回對象原始值),用得到的基本類型值按照前面的規則進行比較;
相等和不相等操作符比較規則:
null
和undefined
是相等的。- 要比較相等性之前,不能將
null
和undefined
轉換成其他任何值。 - 如果有一個操作數是
NaN
,則相等操作符返回false
,而不相等操作符返回true
。重要提示:即使兩個操作數都是NaN
,相等操作符也返回false
;因為按照規則,NaN
不等於NaN
。 - 如果兩個操作數都是對象,則比較它們是不是同一個對象。如果兩個操作數都指向同一個對象,則相等操作符返回
true
;否則,返回false
。
typeof 和 instanceof
typeof 操作符
可推斷類型(返回值:對應類型字元串)如下:
undefined
、boolean
、stirng
、number
、object
、function
、bigint
註意:null
值表示一個空對象指針,使用typeof
操作符檢測null
值時會返回"object"
instanceof 操作符
可推斷類型(返回值:布爾值)如下:
Object
、Array
、Date
、Function
、RegExp
、基本包裝類型(Boolean
、Number
和String
)、單體內置對象(Global
和Math
)
手寫 typeof 和 instanceof 方法
前置知識:原型鏈、toString
方法
對象toString
方法應用:
- 檢測對象類型
let obj = new Number('5')
Object.prototype.toString.call(obj) // '[object Number]'
- 實現進位轉換
let num = 5
num.toString(2) // '101'
手寫typeof
方法
function myTypeof(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
}
手寫instanceof
方法:
function myInstanceof(left, right) {
while (1) {
if (left === null) {
return false
}
if (left === right.prototype) {
return true
}
left = left.__proto__
}
}
說明:變數left
為實例對象,變數right
為構造函數。
深拷貝與淺拷貝
淺拷貝
基本概念:對象的淺拷貝是其屬性與拷貝源對象的屬性共用相同引用(指向相同的底層值)的副本。
實現方式:
Object.assign()
Object.create()
- 擴展運算符:
{ ...obj }
Array.concat()
Array.slice()
Array.from()
- lodash API :
_.clone()
let obj1 = { a: 2, b: { s: 4 } }
let obj2 = { ...obj1 }
obj2.a = 3 // obj1.a 不發生改變
obj2.b.s = 5 // obj1.b.s 此時發生改變,值更改為5
註意:以上方式僅對對象第一層作深拷貝,無法對多層級對象實現深拷貝
深拷貝
基本概念:對象的深拷貝是指其屬性與其拷貝的源對象的屬性不共用相同的引用(指向相同的底層值)的副本。
實現方式:
- 遞歸實現(簡版)
function cloneDeep(obj) {
let res = Array.isArray(obj[key]) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && obj[key] === 'object') {
res[key] = cloneDeep(obj[key])
} else {
res[key] = obj[key]
}
}
}
return res
}
-
JSON.parse(JSON.stringify(obj))
-
lodash API :
_.cloneDeep()
let obj1 = { a: 2, b: { s: 4 } }
let obj2 = JSON.parse(JSON.stringify(obj1))
obj2.a = 3 // obj1.a 不發生改變
obj2.b.s = 5 // obj1.b.s 不發生改變
註意:深拷貝會使源和副本互相獨立,即對多層級對象能夠實現深拷貝
參考