javascript
boolean类型_JS核心理论之《数据类型、类型转换、深浅拷贝与参数传递》
數(shù)據(jù)類型
基本類型:共7種,也被稱為值類型,是一種既非對(duì)象也無(wú)方法的數(shù)據(jù)。包括:string、number、bigint、boolean、null、undefined、symbol。
除了 null 和 undefined之外,所有基本類型都有其對(duì)應(yīng)的包裝對(duì)象:
String 為字符串基本類型。
Number 為數(shù)值基本類型。
BigInt 為大整數(shù)基本類型。
Boolean 為布爾基本類型。
Symbol 為字面量基本類型。
這個(gè)包裹對(duì)象的valueOf()方法返回基本類型值。
引用類型:對(duì)象(Object)、數(shù)組(Array)、函數(shù)(Function)、Date、RegExp、基本包裝類型(String、Number、Boolean、BigInt、Symbol)以及單體內(nèi)置對(duì)象(Global、Math)
二者區(qū)別:
- 基本類型
- 基本類型的值是不可改變的。
- 基本類型的值保存在棧中。
- 基本類型的比較是值的比較。
- 保存與復(fù)制的是值本身。
- 使用typeof檢測(cè)數(shù)據(jù)的類型。
- 引用類型
- 引用類型可以擁有屬性和方法,并且是可以動(dòng)態(tài)改變的.
- 引用類型的值是同時(shí)保存在棧內(nèi)存和堆內(nèi)存中的對(duì)象。
- 引用類型的比較是地址的比較。
- 保存與復(fù)制的是指向?qū)ο蟮囊粋€(gè)指針。
- 使用instanceof檢測(cè)數(shù)據(jù)類型。
對(duì)于引用類型的變量,==和===只會(huì)判斷引用的地址是否相同,而不會(huì)判斷對(duì)象具體里屬性以及值是否相同。因此,如果兩個(gè)變量指向相同的對(duì)象,則返回true.
如果是不同的對(duì)象,即使包含相同的屬性和值,也會(huì)返回false
示例:
示例:
var a,b; a = "zyj"; b = a;a.toUpperCase(); console.log(a); // zyj console.log(b); // zyja = "呵呵"; // 改變 a 的值,并不影響 b 的值 console.log(a); // 呵呵 console.log(b); // zyj示例:
var a = {name:"percy"}; var b; b = a; a.name = "zyj"; console.log(b.name); // zyjb.age = 22; console.log(a.age); // 22 var c = {name: "zyj",age: 22 };類型轉(zhuǎn)換
顯式調(diào)用Boolean(value)、Number(value)、String(value)完成的類型轉(zhuǎn)換,叫做顯示類型轉(zhuǎn)換。
比較操作或者加減乘除四則運(yùn)算操作時(shí),常常會(huì)觸發(fā) JavaScript 的隱式類型轉(zhuǎn)換。隱式類型轉(zhuǎn)換時(shí),絕大多數(shù)情況下都是優(yōu)先轉(zhuǎn)為number型。
js內(nèi)部用于實(shí)現(xiàn)類型轉(zhuǎn)換的4個(gè)函數(shù)是:
- ToPrimitive ( input [ , PreferredType ] ), 轉(zhuǎn)換為原始對(duì)象,即基本類型,依賴于valueOf和toString的實(shí)現(xiàn)(先valueOf,再toString)
- ToBoolean ( argument ),除了以下(undefined、null、-0/+0、NaN、'')五個(gè)值的轉(zhuǎn)換結(jié)果為false,其他的值全部為true
- ToNumber ( argument )
- ToString ( argument )
比較運(yùn)算
JavaScript 為我們提供了嚴(yán)格比較與類型轉(zhuǎn)換比較兩種模式,嚴(yán)格比較(===)只會(huì)在操作符兩側(cè)的操作對(duì)象類型一致,并且內(nèi)容一致時(shí)才會(huì)返回為 true,否則返回 false。
而更為廣泛使用的 == 操作符則會(huì)首先將操作對(duì)象轉(zhuǎn)化為相同類型,再進(jìn)行比較。對(duì)于 <= 等運(yùn)算,則會(huì)首先轉(zhuǎn)化為原始對(duì)象(Primitives),然后再進(jìn)行對(duì)比。
x == y, 算法流程如下:
示例:
[] == ![] //true 1. 基于運(yùn)算符的優(yōu)先級(jí),運(yùn)算![],[]轉(zhuǎn)為boolean后為真值,取反后,變?yōu)?#xff1a;[]==false 2. 任何類型與boolean比較,boolean轉(zhuǎn)為number,即 []==0 3. []為對(duì)象,轉(zhuǎn)為原始值,ToPrimitive先valueOf返回[],再toString返回'' 4. 最后string轉(zhuǎn)number,變?yōu)?0==0加法運(yùn)算
遇到算數(shù)運(yùn)算符(- 、* 、/ 和 %)的時(shí)候會(huì)在運(yùn)算之前將參與運(yùn)算的雙方轉(zhuǎn)換成數(shù)字。而加法(+)運(yùn)算有些特殊,只要其中一個(gè)操作數(shù)是字符串,那么它就執(zhí)行連接字符串的操作。
加法(+)的算法如下:
示例:
1+'2'+false1.左邊取原始值,依舊是Number 2.中間為String,則都進(jìn)行toString操作 3.左邊轉(zhuǎn)換按照toString的規(guī)則,返回'1',得到結(jié)果temp值'12' 4.右邊布爾值和temp同樣進(jìn)行1步驟 5.temp為string,則布爾值也轉(zhuǎn)為string'false' 6.拼接兩者 得到最后結(jié)果 '12false'對(duì)象轉(zhuǎn)換
只有在 JavaScript 表達(dá)式或語(yǔ)句中需要用到數(shù)字或字符串時(shí),對(duì)象才被隱式轉(zhuǎn)換。 當(dāng)需要將對(duì)象轉(zhuǎn)換成數(shù)字時(shí),需要以下三個(gè)步驟:
示例:
> 3 * { valueOf: function () { return 5 } } 15類型判斷
- 使用Array.isArray()判斷數(shù)組
- 使用[] instanceof Array判斷是否在Array的原型鏈上,即可判斷是否為數(shù)組
- [].constructor === Array通過(guò)其構(gòu)造函數(shù)判斷是否為數(shù)組
- 也可使用Object.prototype.toString.call([])判斷值是否為[object Array]來(lái)判斷數(shù)組
- Object.prototype.toString.call({})結(jié)果為[object Object]則為對(duì)象
- {} instanceof Object判斷是否在Object的原型鏈上,即可判斷是否為對(duì)象
- {}.constructor === Object通過(guò)其構(gòu)造函數(shù)判斷是否為對(duì)象
- 使用typeof function判斷func是否為函數(shù)
- 使用func instanceof Function判斷func是否為函數(shù)
- 通過(guò)func.constructor === Function判斷是否為函數(shù)
- 也可使用Object.prototype.toString.call(func)判斷值是否為[object Function]來(lái)判斷func
- 最簡(jiǎn)單的是通過(guò)null===null來(lái)判斷是否為null
- Object.prototype.proto===a判斷a是否為原始對(duì)象原型的原型,即null
- typeof (a) == 'object' && !a 通過(guò)typeof判斷null為對(duì)象,且對(duì)象類型只有null轉(zhuǎn)換為Boolean為false
- isNaN(any)直接調(diào)用此方法判斷是否為非數(shù)值
深淺拷貝
淺拷貝: 拷貝的是對(duì)象的指針,修改內(nèi)容互相影響
深拷貝:整個(gè)對(duì)象拷貝到另一塊內(nèi)存空間中,修改內(nèi)容不互相影響
淺拷貝只解決了第一層的問(wèn)題,但是如果遇到嵌套對(duì)象,就不行了,就得用深拷貝。
let a = {age: 1,jobs: {first: 'FE'} } let b = { ...a } a.jobs.first = 'native' console.log(b.jobs.first) // native解決辦法一:
let a = {age: 1,jobs: {first: 'FE'} } let b = JSON.parse(JSON.stringify(a)) a.jobs.first = 'native' console.log(b.jobs.first) // FE但是JSON.parse(JSON.stringify(object))方法也是有局限性的:
解決辦法二:自己實(shí)現(xiàn)深拷貝函數(shù)(考慮了對(duì)象、數(shù)組、Symbol類型以及多層嵌套)
function deepClone(obj) {function isObject(o) {return (typeof o === 'object' || typeof o === 'function') && o !== null}if (!isObject(obj)) {throw new Error('非對(duì)象')}let isArray = Array.isArray(obj)let newObj = isArray ? [] : {}Reflect.ownKeys(obj).forEach(key => {newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]})return newObj }const e = Symbol("e") const f = Symbol.for("f") let obj = {a: [1, 2, 3],b: {c: 2,d: 3} } obj[e] = 'localSymbol' obj[f] = 'globalSymbol'let newObj = deepClone(obj) newObj.b.c = 1console.log(newObj) // { a: [ 1, 2, 3 ], b: { c: 2, d: 3 }, [Symbol(e)]: 'localSymbol', [Symbol(f)]: 'globalSymbol' } console.log(newObj[e] === obj[e]) // true console.log(obj.b.c) // 2上述函數(shù)的問(wèn)題是沒(méi)有考慮循環(huán)引用以及來(lái)自原型鏈上的屬性的拷貝。
let obj = {a: [1, 2, 3],b: {c: 2,d: 3} } obj.e = objlet newObj = deepClone(obj) console.log(newObj.e) // 2>輸出 RangeError: Maximum call stack size exceededlet childObj = Object.create(obj) let newObj = deepClone(childObj)console.log('原對(duì)象:') for(let key in childObj){console.log(childObj[key]) } console.log('新對(duì)象:') for(let key in newObj){console.log(newObj[key]) }>輸出 原對(duì)象: [ 1, 2, 3 ] { c: 2, d: 3 } 新對(duì)象:解決循環(huán)引用問(wèn)題,我們可以額外開(kāi)辟一個(gè)存儲(chǔ)空間,來(lái)存儲(chǔ)當(dāng)前對(duì)象和拷貝對(duì)象的對(duì)應(yīng)關(guān)系。
當(dāng)需要拷貝當(dāng)前對(duì)象時(shí),先去存儲(chǔ)空間中找,有沒(méi)有拷貝過(guò)這個(gè)對(duì)象,如果有的話直接返回,如果沒(méi)有的話繼續(xù)拷貝,這樣就巧妙化解的循環(huán)引用的問(wèn)題。
這里使用Reflect.ownKeys() 獲取所有的鍵值,同時(shí)包括 Symbol。
- for…in 獲取當(dāng)前對(duì)象及其原型鏈上的所有可枚舉屬性
- Object.keys 獲取當(dāng)前對(duì)象上的所有可枚舉屬性
- Object.getOwnPropertyNames 獲取當(dāng)前對(duì)象上的所有可枚舉和不可枚舉屬性
- Object.getOwnPropertySymbols 獲取當(dāng)前對(duì)象上所有Symbol屬性
- Reflect.ownKeys 獲取當(dāng)前對(duì)象上所有可枚舉、不可枚舉屬性以及Symbol屬性
- Object.prototypeOf 獲取對(duì)象原型鏈上一級(jí)的對(duì)象
- Reflect.prototypeOf 獲取對(duì)象原型鏈上一級(jí)的對(duì)象
所有通過(guò)Object和Reflect方法獲取對(duì)象的屬性,都無(wú)法訪問(wèn)到對(duì)象原型鏈上的屬性。
- Object.keys 是獲取到對(duì)象屬性的所有方法中范圍最小的一種方法
- Reflect.ownKeys 是獲取到對(duì)象屬性的所有方法中范圍最大的一種方法
- 此外 Reflect.ownKeys = Object.getOwnPropertyNames + Object.getOwnPropertySymbols
測(cè)試一下:
const e = Symbol('e') const f = Symbol.for('f') const g = Symbol.for('g') let obj = {a: [1, 2, 3],b: {c: 2,d: 3,}, } obj[e] = 'localSymbol' obj[f] = 'globalSymbol'let childObj = Object.create(obj) childObj[g] = 'globalSymbol_'let newObj = deepClone(childObj)console.log('原對(duì)象:') for (let key in childObj) {console.log(childObj[key]) } while (childObj) { // 循環(huán)Object.getOwnPropertySymbols(childObj).forEach(symKey => {console.log(childObj[symKey])})childObj = Object.getPrototypeOf(Object(childObj)) }console.log('新對(duì)象:') for (let key in newObj) {console.log(newObj[key]) } while (newObj) { // 循環(huán)Object.getOwnPropertySymbols(newObj).forEach(symKey => {console.log(newObj[symKey])})newObj = Object.getPrototypeOf(Object(newObj)) }>輸出 原對(duì)象: [ 1, 2, 3 ] { c: 2, d: 3 } globalSymbol_ localSymbol globalSymbol 新對(duì)象: [ 1, 2, 3 ] { c: 2, d: 3 } globalSymbol_此時(shí),上述函數(shù)可以深拷貝當(dāng)前對(duì)象或數(shù)組的所有可枚舉屬性、Symbol類型鍵,以及該對(duì)象原型鏈上的所有可枚舉屬性。但仍然有一個(gè)問(wèn)題,就是不能拷貝原型鏈上的Symbol類型鍵。
需要使用Object.getPrototypeOf來(lái)循環(huán)獲取上一級(jí)對(duì)象的Symbol類型鍵屬性。
至此,該函數(shù)可以深拷貝當(dāng)前對(duì)象和它原型鏈上的所有可枚舉屬性及Symbol屬性,結(jié)果大家可以去驗(yàn)證。
總結(jié):
淺拷貝
- Object.assign()
- 擴(kuò)展運(yùn)算符 …
- Array.prototype.slice()/Array.prototype.concat()
深拷貝
- JSON.parse(JSON.stringify())
- lodash的深拷貝函數(shù)
參數(shù)傳遞
- 基本類型傳值調(diào)用(值拷貝)
- 引用類型傳共享調(diào)用(指針拷貝)
1.關(guān)鍵點(diǎn)是函數(shù)傳參時(shí),傳入的是實(shí)參的拷貝,而不是實(shí)參本身。所以,基本類型傳遞的是變量的值的拷貝,而引用類型傳遞對(duì)象的指針的拷貝,其指針也是變量的值。
所以傳共享調(diào)用也可以說(shuō)是傳值調(diào)用。
2.值拷貝后,對(duì)值修改,自然不會(huì)影響原值。指針拷貝后,與原指針指向的是同一個(gè)對(duì)象,如果函數(shù)內(nèi)修改對(duì)象的屬性,剛原對(duì)象屬性自然也變,但如果直接對(duì)指針拷貝賦予新值,即修改它的指向,則不會(huì)影響到原指針指向的原對(duì)象。
示例:
function changeStuff(a, b, c) {a = a * 10;b.item = "changed";c = {item: "changed"}; }var num = 10; var obj1 = {item: "unchanged"}; var obj2 = {item: "unchanged"};changeStuff(num, obj1, obj2);console.log(num); // 10 console.log(obj1.item); //changed console.log(obj2.item); //unchanged可以看到,變量 a 的值就是 num 值的拷貝,變量 b c 分別是 obj1 obj2 的指針的拷貝。
函數(shù)的參數(shù)其實(shí)就是函數(shù)作用域內(nèi)部的變量,函數(shù)執(zhí)行完之后就會(huì)銷毀。
變量 a 的值的改變,并不會(huì)影響變量 num。
而 b 因?yàn)楹?obj1 是指向同一個(gè)對(duì)象,所以使用 b.item = "changed"; 修改對(duì)象的值,會(huì)造成 obj1 的值也隨之改變。
由于是對(duì) c 重新賦值了,所以修改 c 的對(duì)象的值,并不會(huì)影響到 obj2。
總結(jié)
以上是生活随笔為你收集整理的boolean类型_JS核心理论之《数据类型、类型转换、深浅拷贝与参数传递》的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python文本提取_使用Python从
- 下一篇: html 轮播图_JS拖拽专题(二)——