日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

boolean类型_JS核心理论之《数据类型、类型转换、深浅拷贝与参数传递》

發(fā)布時(shí)間:2024/4/11 javascript 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 boolean类型_JS核心理论之《数据类型、类型转换、深浅拷贝与参数传递》 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

數(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 arrRef = ["Hi!"]; var arrRef2 = arrRef; console.log(arrRef === arrRef2); // truevar arr1 = ["Hi!"]; var arr2 = ["Hi!"]; console.log(arr1 === arr2); // 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, 算法流程如下:

  • x或y中有一個(gè)為NaN, 則返回false;
  • x或y都為null或undefined中的一種,則返回true(null == undefined),否則返回false(null == 0);
  • x或y類型不一致,且為String,Number,Boolean中的一種,則x、y轉(zhuǎn)為Number再比較;
  • x或y中有一個(gè)為Object,則將其轉(zhuǎn)為原始類型,再進(jìn)行比較;
  • 優(yōu)先比類型,再比null與undefined,再比string和number,再比boolean與any,再比object與string、number、symbol; 以上如果轉(zhuǎn)為原始類型比較,則進(jìn)行類型轉(zhuǎn)換,直到類型相同再比較值的大小。這就是==的隱式轉(zhuǎn)換對(duì)比

    示例:

    [] == ![] //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í)行連接字符串的操作。

    加法(+)的算法如下:

  • +號(hào)左右分別進(jìn)行取值,進(jìn)行ToPrimitive()操作,轉(zhuǎn)為原始值;
  • 分別獲取左右轉(zhuǎn)換之后的值,如果存在String,則對(duì)其進(jìn)行ToString處理后進(jìn)行拼接操作;
  • 其他的都進(jìn)行ToNumber處理;
  • 在轉(zhuǎn)換時(shí)ToPrimitive,除去Date為string外,都按照ToPrimitive 類型為Number進(jìn)行處理;
  • 示例:

    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è)步驟:

  • 調(diào)用 valueOf()。如果結(jié)果是原始值(不是一個(gè)對(duì)象),則將其轉(zhuǎn)換為一個(gè)數(shù)字。
  • 否則,調(diào)用 toString() 方法。如果結(jié)果是原始值,則將其轉(zhuǎn)換為一個(gè)數(shù)字。
  • 否則,拋出一個(gè)類型錯(cuò)誤。
  • 示例:

    > 3 * { valueOf: function () { return 5 } } 15

    類型判斷

  • 判斷數(shù)組
    • 使用Array.isArray()判斷數(shù)組
    • 使用[] instanceof Array判斷是否在Array的原型鏈上,即可判斷是否為數(shù)組
    • [].constructor === Array通過(guò)其構(gòu)造函數(shù)判斷是否為數(shù)組
    • 也可使用Object.prototype.toString.call([])判斷值是否為[object Array]來(lái)判斷數(shù)組
  • 判斷對(duì)象
    • Object.prototype.toString.call({})結(jié)果為[object Object]則為對(duì)象
    • {} instanceof Object判斷是否在Object的原型鏈上,即可判斷是否為對(duì)象
    • {}.constructor === Object通過(guò)其構(gòu)造函數(shù)判斷是否為對(duì)象
  • 判斷函數(shù)
    • 使用typeof function判斷func是否為函數(shù)
    • 使用func instanceof Function判斷func是否為函數(shù)
    • 通過(guò)func.constructor === Function判斷是否為函數(shù)
    • 也可使用Object.prototype.toString.call(func)判斷值是否為[object Function]來(lái)判斷func
  • 判斷null
    • 最簡(jiǎn)單的是通過(guò)null===null來(lái)判斷是否為null
    • Object.prototype.proto===a判斷a是否為原始對(duì)象原型的原型,即null
    • typeof (a) == 'object' &amp;&amp; !a 通過(guò)typeof判斷null為對(duì)象,且對(duì)象類型只有null轉(zhuǎn)換為Boolean為false
  • 判斷NaN
    • isNaN(any)直接調(diào)用此方法判斷是否為非數(shù)值

    深淺拷貝

    淺拷貝: 拷貝的是對(duì)象的指針,修改內(nèi)容互相影響
    深拷貝:整個(gè)對(duì)象拷貝到另一塊內(nèi)存空間中,修改內(nèi)容不互相影響

    如下例子:對(duì)對(duì)象直接復(fù)制后,導(dǎo)致原對(duì)象值發(fā)生改變 let a = {age: 1 } let b = a a.age = 2 console.log(b.age) // 2解決辦法一: let a = {age: 1 } let b = Object.assign({}, a) a.age = 2 console.log(b.age) // 1解決辦法二: let a = {age: 1 } let b = { ...a } a.age = 2 console.log(b.age) // 1解決辦法三: let a = ['ant', 'bison', 'camel', 'duck', 'elephant'] let b = a.slice(1, 5) console.log(b) // ["bison", "camel", "duck", "elephant"]

    淺拷貝只解決了第一層的問(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))方法也是有局限性的:

  • 會(huì)忽略u(píng)ndefined和symbol
  • 不能序列化函數(shù)
  • 不能解決循環(huán)引用的對(duì)象
  • 解決辦法二:自己實(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
    function deepClone(obj, wm = new WeakMap()) {function isObject(o) {return (typeof o === 'object' || typeof o === 'function') && o !== null}if (!isObject(obj)) {throw new Error('非對(duì)象')}if (wm.has(obj)) return wm.get(obj); // 新增代碼,查哈希表let isArray = Array.isArray(obj)let newObj = isArray ? [] : {}wm.set(obj, newObj); // 新增代碼,哈希表設(shè)值Object.getOwnPropertySymbols(obj).forEach(symKey => {newObj[symKey] = isObject(obj[symKey]) ? deepClone(obj[symKey], wm) : obj[symKey]})//使用for in替換Reflect.ownKeysfor( let key in obj){newObj[key] = isObject(obj[key]) ? deepClone(obj[key],wm) : obj[key]}return newObj }

    測(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類型鍵屬性。

    function deepClone(obj, wm = new WeakMap()) {function isObject(o) {return (typeof o === 'object' || typeof o === 'function') && o !== null}if (!isObject(obj)) {throw new Error('非對(duì)象')}if (wm.has(obj)) return wm.get(obj) // 新增代碼,查哈希表let isArray = Array.isArray(obj)let newObj = isArray ? [] : {}wm.set(obj, newObj) // 新增代碼,哈希表設(shè)值while (obj) {Reflect.ownKeys(obj).forEach(key => {if(obj.propertyIsEnumerable(key)){newObj[key] = isObject(obj[key]) ? deepClone(obj[key],wm) : obj[key]}})obj = Object.getPrototypeOf(Object(obj))}return newObj }

    至此,該函數(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)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。