重学前端 6 # JavaScript类型有哪些你不知道的细节?

简介: 重学前端 6 # JavaScript类型有哪些你不知道的细节?

JavaScript类型有哪些你不知道的细节?



winter提了几个问题测试:能回答对几个?


   1、为什么有的编程规范要求用 void 0 代替 undefined?

   2、字符串有最大长度吗?

   3、0.1 + 0.2 不是等于 0.3 么?为什么 JavaScript 里不是这样的?

   4、ES6 新加入的 Symbol 是个什么东西?

   5、为什么给对象添加的方法能用在基本类型上?



如果有点犹豫,不妨看看下面的介绍,或者找找资料温习一下。



类型


  1. Undefined
  2. Null
  3. Boolean
  4. String
  5. Number
  6. Symbol
  7. Object



1、Undefined、Null


Undefined:


Undefined 类型表示未定义,它的类型只有一个值,就是 undefined

任何变量在赋值前是 Undefined 类型、值为 undefined

JavaScript 的代码 undefined 是一个变量,而并非是一个关键字,这是 JavaScript 语言公认的设计失误之一

为了避免无意中被篡改,可以使用 void 0 来获取 undefined 值。



Null:

  • Null 类型也只有一个值,就是 null,它的语义表示空值
  • 与 undefined 不同,null 是 JavaScript 关键字
  • 在任何代码中,都可以用 null 关键字来获取null值



2、String


  • String 用于表示文本数据
  • String 有最大长度是 2^53 - 1
  • 字符串的最大长度,实际上是受字符串的编码长度影响的。


   Note: 现行的字符集国际标准,字符是以 Unicode 的方式表示的,每一个 Unicode 的码点表示一个字符,理论上,Unicode 的范围是无限的。UTF 是 Unicode 的编码方式,规定了码点在计算机中的表示方法,常见的有 UTF16 和 UTF8。Unicode 的码点通常用 U+??? 来表示,其中 ??? 是十六进制的码点值。0-65536(U+0000 - U+FFFF)的码点被称为基本字符区域(BMP)。


3、Number


JavaScript 中的 Number 类型有 18437736874454810627(即 264-253+3) 个值


NaN,占用了 9007199254740990,这原本是符合 IEEE 规则的数字


Infinity,无穷大


-Infinity,负无穷大


根据双精度浮点数的定义,Number 类型中有效的整数范围是-0x1fffffffffffff 至 0x1fffffffffffff,所以 Number 无法精确表示此范围外的整数


根据浮点数的定义,非整数的 Number 类型无法用 ==(=== 也不行)来比较


关于javaScript中 0.1 + 0.2 == 0.3 ? 这个问题的解释:

console.log( 0.1 + 0.2 == 0.3);
>> false


输出结果为false,说明两边不相等,这是浮点运算特点导致的,实际上,这里错误的不是结论,而是比较的方法,正确的比较方法是使用javaScript提供的最小精度值:


我们可以查找MDN文档的Number可以找到属性EPSILON


Number.EPSILON:两个可表示数字之间的最小间隔

console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);
>> true


这样的比较输出结果为true检查等式左右两边差的绝对值是否小于最小精度,才是正确的比较浮点数的方法



4、Symbol


关于Symbol的介绍,我准备用ES6文档-阮一峰来做一些介绍,具体的可以参考文档


4.1、ES6 引入Symbol的原因

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。从根本上防止属性名的冲突。


4.2、介绍

凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。


4.2.1、Symbol 值通过Symbol函数生成,先来一段代码:

let s = Symbol();
typeof s
>> "symbol"


上面代码中,变量s就是一个独一无二的值。sSymbol数据类型。

4.2.2、Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述

let s1 = Symbol('foo');
let s2 = Symbol('bar');
s1
>> Symbol(foo)
s2
>> Symbol(bar)
s1.toString()
>> "Symbol(foo)"
s2.toString()
>> "Symbol(bar)"


上面代码中,s1和s2是两个 Symbol 值。如果不加参数,它们在控制台的输出都是Symbol(),不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。


4.2.3、如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。

const obj = {
    a: '123123',
    toString() {
        return 'iuoisigud';
    }
};
const sym = Symbol(obj);
sym // Symbol(iuoisigud)


4.2.4、Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false
// 有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');
s1 === s2 // false


4.2.5、Symbol 值不能与其他类型的值进行运算,会报错。

let sym = Symbol('My symbol');
"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string


4.2.6、Symbol 值可以显式转为字符串,也可以转为布尔值,但是不能转为数值。

let sym = Symbol('My symbol');
String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
let sym = Symbol();
Boolean(sym) // true
!sym  // false
Number(sym) // TypeError
sym + 2 // TypeError

 


4.2.7、其他的一些属性可以去看ES6文档-阮一峰


4.3、注意

Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型



5、Object

Object 是 JavaScript 中最复杂的类型,也是 JavaScript 的核心机制之一。


为什么给对象添加的方法能用在基本类型上?


回答:“运算符提供了装箱操作,它会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。”


比如原型上添加方法,也可以应用于基本类型:

Symbol.prototype.hello = () => console.log("hello");
var a = Symbol("a");
console.log(typeof a); //symbol,a 并非对象
a.hello(); //hello,有效



6、类型转换


6.1、臭名昭著的“ == ”运算


  • 因为 JS 是弱类型语言,所以类型转换发生非常频繁
  • “ == ”试图实现跨类型的比较,它的规则复杂到几乎没人可以记住。


6.2、转换规则

fffd75cd8f6162cd0c8f43363dd089c0.jpg


6.3、StringToNumber

字符串到数字的类型转换,存在一个语法结构,类型转换支持十进制、二进制、八进制和十六进制


比如:

Number('0xFF')
>> 255


6.4、装箱转换

   装箱(boxing):值类型实例到对象的转换,它暗示在运行时实例将携带完整的类型信息,并在堆中分配。


每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类,所谓装箱转换,正是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。


   例子:利用一个函数的 call 方法来强迫产生Symbol装箱

var symbolObject = (function() {
    return this;
}).call(Symbol("a"));
console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true


   例子:使用内置的 Object 函数,我们可以在 JavaScript 代码中显式调用装箱能力。

var symbolObject = Object(Symbol("a"));
console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true

   每一类装箱对象皆有私有的 Class 属性,这些属性可以用 Object.prototype.toString 获取:

var symbolObject = Object(Symbol("a"));
console.log(Object.prototype.toString.call(symbolObject));
>> [object Symbol]


6.5、拆箱转换

拆箱(unboxing):是将引用类型转换为值类型


6.5.1、在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换

toPrimitive(input, preferedType)


input是输入的值,preferedType是期望转换的类型,它可以是字符串,也可以是数字。



inputType Result
Undefined input argument
Null input argument
Boolean input argument
Number input argument
String input argument
Object 忽略 第二个参数 hint PreferredType 直接调用内置方法 [[DefaultValue]]



6.5.2、如果转换的类型是number,会执行以下步骤:参考博客


   如果input是原始值,直接返回这个值;

   否则,如果input是对象,调用input.valueOf(),如果结果是原始值,返回结果;

   否则,调用input.toString()。如果结果是原始值,返回结果;

   否则,抛出错误。



6.5.3、如果转换的类型是String,2和3会交换执行,即先执行toString()方法。


例子1:先将两个操作数转换为string,然后进行拼接

[] + []
>> ""
[] -----> ''
[] -----> ''
[] + [] = ''


例子2:先将两个操作数转换为string,然后进行拼接

[] + {}
>> "[object Object]"
// 解释
[] -----> ''
{} -----> '[object Object]'
[] + {} = '[object Object]'


例子3:js解释器会将开头的 {} 看作一个代码块,而不是一个js对象

{} + []
>> 0
// 真正参与运算的是 + []
// {} + [] 等价于 + []



7、规范类型


List 和 Record: 用于描述函数传参过程。

Set:主要用于解释字符集等。

Completion Record:用于描述异常、跳出等语句执行过程。

Reference:用于描述对象属性访问、delete 等。

Property Descriptor:用于描述对象的属性。

Lexical Environment 和 Environment Record:用于描述变量和作用域。

Data Block:用于描述二进制数据。



8、补充阅读


typeof 的运算结果,与运行时类型的规定有很多不一致的地方(typeof 的设计是有缺陷的)

a2e8055728cc169c4f75311ae7308b9d.png








目录
相关文章
|
2月前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
2月前
|
JavaScript 前端开发 Java
springboot解决js前端跨域问题,javascript跨域问题解决
本文介绍了如何在Spring Boot项目中编写Filter过滤器以处理跨域问题,并通过一个示例展示了使用JavaScript进行跨域请求的方法。首先,在Spring Boot应用中添加一个实现了`Filter`接口的类,设置响应头允许所有来源的跨域请求。接着,通过一个简单的HTML页面和jQuery发送AJAX请求到指定URL,验证跨域请求是否成功。文中还提供了请求成功的响应数据样例及请求效果截图。
springboot解决js前端跨域问题,javascript跨域问题解决
|
2月前
|
缓存 JavaScript 前端开发
JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用
本文深入讲解了 JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用。
53 5
|
2月前
|
开发框架 JavaScript 前端开发
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势。通过明确的类型定义,TypeScript 能够在编码阶段发现潜在错误,提高代码质量;支持组件的清晰定义与复用,增强代码的可维护性;与 React、Vue 等框架结合,提供更佳的开发体验;适用于大型项目,优化代码结构和性能。随着 Web 技术的发展,TypeScript 的应用前景广阔,将继续引领 Web 开发的新趋势。
43 2
|
2月前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
80 1
|
2月前
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
56 4
|
2月前
|
资源调度 前端开发 JavaScript
vite3+vue3 实现前端部署加密混淆 javascript-obfuscator
【11月更文挑战第10天】本文介绍了在 Vite 3 + Vue 3 项目中使用 `javascript-obfuscator` 实现前端代码加密混淆的详细步骤,包括安装依赖、创建混淆脚本、修改 `package.json` 脚本命令、构建项目并执行混淆,以及在 HTML 文件中引用混淆后的文件。通过这些步骤,可以有效提高代码的安全性。
108 2
|
2月前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
175 1
|
2月前
|
前端开发 JavaScript 安全
vite3+vue3 实现前端部署加密混淆 javascript-obfuscator
【11月更文挑战第7天】本文介绍了在 Vite 3 + Vue 3 项目中使用 `javascript-obfuscator` 实现前端代码加密混淆的详细步骤。包括项目准备、安装 `javascript-obfuscator`、配置 Vite 构建以应用混淆,以及最终构建项目进行混淆。通过这些步骤,可以有效提升前端代码的安全性,防止被他人轻易分析和盗用。
323 0
|
3月前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
205 2