当前位置: 首页 > article >正文

前端知识图谱 - JavaScript基础(变量和类型)

变量和类型

1. JavaScript 规定了几种语言类型?

JavaScript 语言的每一个值都属于某一种数据类型,它规定了 7 种语言类型。语言类型广泛用于变量、函数参数、表达式、函数返回值等场合,根据最新的语言标准,这 7 种语言类型如下:

  • Undefined
    Undefined 类型表示未定义,任何一个变量未定义之前都是 Undefined类型,值为 undefined。但需要我们注意的是,undefined 不是一个关键字,它可以作为一个变量被定义,所以当我们为一个变量直接赋值 undefined 的时候,有可能和预期效果出现偏差。
const undefined: number = 123;
const udf: number = undefined;	// echo 123

严谨一点的话,我们可以使用 void 运算,来把任意表达式转换为 undefined。例如:

const udf: undefined = void 0; // echo undefined

但在实际编程中,我们一般不会给 Undefined 类型的值赋值,这样可以保证它是一个未赋值的自然状态,从而让它的值为 undefined。

  • Null
    Null 类型表示定义了但为空,它和 undefined 还是有一定区别的。

  • String
    String 类型用来表示文本数据,只读。

  • Boolean
    Boolean 类型有两个值,用关键字 true 和 false 来表示逻辑意义上的真和假。

  • Number
    Number 类型表示通常意义上的数字,大致对应数学中的有理数。
    JavaScript 中的 Number 类型基本符合 IEEE 754-2008 规定的双精度浮点数规则,但它为特定的几个语言场景规定了几种特殊情况:

    • Infinity:无穷大。当一个数字除以 0 时,得到的结果就是 Infinity。
    • NaN:非数字。当一个数字和一个非数字(例如字符串)作加法之外的运算时,得到的结果就是 NaN。

    根据双精度浮点数的定义,Number 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff,所以 Number 无法精确表示此范围外的整数。
    根据双精度浮点数的定义,非整数的 Number 无法用 == 或 === 来比较。它也造成了一个 精度丢失 的问题:

    0.1 + 0.2 == 0.3	// echo false
    

    这里我们需要换一种比较方法:使用 JS 提供的最小精度值。

    Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON	// echo true
    
    最大数字:Number.MAX_VALUE = 1.7976931348623157e+308
    最大安全数字:Number.MAX_SAFE_INTEGER = 2 ^53 - 1 = 9007199254740991
    
  • Symbol
    自ECMAScript 2015起,Symbol 成为了一种新的原生类型,就像 Number 和 String 一样,表示独一无二的值。

  • Object
    Object 类型是 JS 中最复杂的类型,也是 JS 的核心机制之一,是一种无序的键值对集合。

2. 如何避免 JavaScript 浮点数运算精度丢失?

我们先来看一个经典问题:0.1 + 0.2 != 0.3

0.1 + 0.2 == 0.3	// echo false
0.1 + 0.2			// 0.30000000000000004
(0.1 + 0.2) * 10e16	// echo 30000000000000004

可以看到,相加的结果和预期差了一个微小值,我们后期计算的话就会出现误差。
为什么会这样呢?我们先来看看运算过程:小数转换二进制用的是 乘2取整法,就是不断乘2,小数为0为止,顺序排列。
通过计算,可得:

0.1 = 0.0001100(1100) = 2^-4 * 1.1001100(1100) // 1100循环
0.2 = 0.001100(1100) = 2^-3 * 1.1001100(1100)

JS 中数字存储使用的是 ieee 754 64位双精度浮点数,64 位中第 1 位为符号位,0 正 1 负;之后 11 位为指数位,用来确定范围;其余 52 位都是尾数位,用来确定精度。
由于 0.1 和 0.2 都是无限循环的二进制,保留位数 52 位,不算最左边整数位。最终计算机中存储的数位:

0.1 = 2^-4 * 1.100 11001100 11001100 11001100 11001100 11001100 11001100 1
0.2 = 2^-3 * 1.100 11001100 11001100 11001100 11001100 11001100 11001100 1

0.00011001100110011001100110011001100110011001100110011001
+
0.0011001100110011001100110011001100110011001100110011001
=
0.01001100110011001100110011001100110011001100110011001011
// 保留 52 为,末位进位(逢1进1)
0.01 00110011 00110011 00110011 00110011 00110011 00110011 00110.30000000000000004

原生解决办法:

parseFloat((0.1 + 0.2).toFixed(10))

或者使用像 decimal.js 这样的库,它们提供了更加精确的浮点数处理能力。

3. JavaScript 对象的底层数据结构是什么?

JavaScript 对象的底层数据结构并没有在规范中明确定义,不同的 JavaScript 引擎可能会采用不同的实现方式。在当前的主流 JavaScript 引擎中,比如 V8 引擎(Chrome 和 Node.js 使用的引擎),JavaScript 对象的底层实现是基于哈希表的。

哈希表是一种能够快速查找元素的数据结构,它通过计算元素的哈希值(一个用于确定元素位置的数字)来存储元素。当你访问对象的属性时,引擎会计算属性名的哈希值,并用它来快速找到相应的属性值。

需要注意的是,虽然大多数现代 JavaScript 引擎使用哈希表作为对象的底层数据结构,但这并不是规范规定的实现方式,不同的实现可能会有所不同。

4. 理解值类型和引用类型

值类型和引用类型是编程语言数据类型的两种基本分类,它们在内存中的存储和传递方式不同。

值类型又称为原始类型或原始值(primitive value),这类值直接存储在栈(Stack)内存中,基础数据类型的值不可修改。当我们定义一个变量,为它赋值时,可以理解为,我们为这个变量绑定了一个内存空间,值就存储在这个内存空间中。
我们定义两个基本类型的变量,把一个值赋值给另一个值,会进行值的复制,这两个变量的值是相互独立的,修改一个变量的值不会影响另一个变量的值。

引用类型的变量存储在栈上,但它们指向的对象存储在堆(Heap)上。堆是内存中的动态区域,相当于自留空间,程序在运行期间会动态分配给堆栈。堆中存储的一般都是对象,然后在栈内存中存储一个变量指针,计算机通过这个变量指针,找到堆中的数据块进行操作,这种访问方式叫做按引用访问。
我们定义两个变量,其中一个为引用类型,当我们把引用类型的变量赋值给另一个变量,只是复制了对象的引用(地址),两个变量指向的是同一个值,修改一个变量会影响另一个变量的值。

5. JavaScript 中的变量在内存中的具体存储形式

同上

6. Symbol类型在实际开发中的应用,手动实现一个简单的 Symbol

了解 JavaScript Symbol 类型及其应用场景
手动实现一个 Symbol:https://segmentfault.com/a/1190000015262174

7. 至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型

let obj = { str: "", num: 0, bool: false, nul: null, und: undefined, sym: Symbol(), arr: [] }
  1. typeof:直接返回数据的类型字段。但无法区分 NullObjectArray,它们的返回值都是 object
console.log(typeof obj.str) // 'string'
console.log(typeof obj.num) // 'number'
console.log(typeof obj.bool) // 'boolean'
console.log(typeof obj.nul) // 'object'
console.log(typeof obj.und) // 'undefined'
console.log(typeof obj.sym) // 'symbol'
console.log(typeof obj.arr) // 'object'
console.log(typeof obj) // 'object'
  1. instanceof:只能判断引用类型数据,null、understand不支持判断
obj.str instanceof String // false
obj.num instanceof Number // false
obj.bool instanceof Boolean // false
obj.nul instanceof Null // Uncaught ReferenceError: Null is not defined
obj.und instanceof Undefined // ReferenceError: Undefined is not defined
obj.sym instanceof Symbol // false
obj.arr instanceof Array // true
obj instanceof Object // true
  1. Object.prototype.toString.call():完美的判断方法。
Object.prototype.toString.call(obj.str) // '[object String]'
Object.prototype.toString.call(obj.num) // '[object Number]'
Object.prototype.toString.call(obj.bool) // '[object Boolean]'
Object.prototype.toString.call(obj.nul) // '[object Null]'
Object.prototype.toString.call(obj.und) // '[object Undefined]'
Object.prototype.toString.call(obj.sym) // '[object Symbol]'
Object.prototype.toString.call(obj.arr) // '[object Array]'
Object.prototype.toString.call(obj) // '[object Object]'
  1. Array.isArray():数组类型内置的判断方法。
Array.isArray(obj.arr) // true
Array.isArray(obj) // false

参考:
js的7中语言类型详解 - https://www.cnblogs.com/lilistyle/p/13613756.html
解析 js 中 0.1 + 0.2 != 0.3 - https://www.php.cn/faq/386908.html


http://www.kler.cn/a/447270.html

相关文章:

  • CMD使用SSH登陆Ubuntu
  • STM32 高级 物联网通信之CAN通讯
  • 基于MATLAB的图像增强
  • jmeter 接口性能测试 学习笔记
  • 现代控制理论——自由度
  • linux-----进程及基本操作
  • git怎么将一个没使用过git的项目上传到某个仓库
  • [计算机网络]唐僧的”通关文牒“NAT地址转换
  • Java写URI网址唤醒APP小程序等NDEF信息
  • IP地址查询的背后②:IP地址(IPv4)的构成、类型以及子网划分
  • vscode的keil assistant 中搜索不到全局变量
  • RTOS之邮箱
  • JAVA学习-练习试用Java实现“使用Arrays.sort方法对整数数组进行排序”
  • SQL进阶技巧:如何计算商品需求与到货队列表进出计划?
  • 深度学习之超分辨率算法——SRGAN
  • 【ETCD】【源码阅读】深入分析 applierV3backend.Apply`方法源码
  • 设计模式之桥接模式:抽象与实现之间的分离艺术
  • C语言 排序
  • sqlite基础
  • 安卓native层抓trace
  • ASP.NET Core - 依赖注入 自动批量注入
  • gesp(三级)(8)洛谷:B3926:[GESP202312 三级] 单位转换
  • HarmonyOS(71) 自定义事件分发之TouchTestStrategy使用说明
  • 电脑提示ntdll.d缺失是什么原因?不处理的话会怎么样?ntdll.dll文件缺失快速解决方案来啦!
  • 数据云平台的可观测性
  • 【Leecode】Leecode刷题之路第87天之扰乱字符串