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