JS篇之数据类型那些事儿

简介: JS组成JS数据类型(7+1)类型转换(装箱/拆箱)


人的不幸来源于他不肯安分守己地待在自己应该待的房里里 --《香水》

一语中的

  1. JS = ECMAScript + DOM + BOM
  2. DOM 并非只能通过 JS 访问
  3. JS是动态弱类型语言
  4. 每个变量只不过是一个用于保存任意值的命名占位符
  5. 实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有
  6. 基本类型是没有任何属性和方法
  7. 对象其实就是一组数据和功能的集合

文章概要

  • JS组成
  • JS数据类型(7+1)
  • 类型转换(装箱/拆箱)

JS组成

其实这是一个很容易忽略的问题。俗话说,最熟悉的陌生人说的就是这种情况。

浏览器环境下JS = ECMAScript + DOM + BOM

我们来简单介绍下。(后期会单独针对BOM/DOM进行分析)

ECMAScript

JS的核心部分,即 ECMA-262 定义的语言,并不局限于 Web 浏览器。

Web 浏览器只是 ECMAScript 实现可能存在的一种*宿主环境(host environment)。而宿主环境提供ECMAScript 的基准实现和与环境自身交互必需的扩展。(比如 DOM 使用 ECMAScript 核心类型和语法,提供特定于环境的额外功能)。

像我们比较常见的Web 浏览器Node.js和即将被淘汰的 Adobe Flash都是ECMA的宿主环境。

ECMAScript 只是对实现ECMA-262规范的一门语言的称呼, JS 实现了ECMAScript,Adobe ActionScript 也实现ECMAScript。

文档对象模型(DOM)

DOM是一个应用编程接口(API),通过创建表示文档的树,以一种独立于平台和语言的方式访问和修改一个页面的内容和结构。

在HTML文档中,Web开发者可以使用JS来CRUD DOM 结构,其主要的目的是动态改变HTML文档的结构。

DOM 将整个页面抽象为一组分层节点

DOM 并非只能通过 JS 访问, 像可伸缩矢量图(SVG)、数学标记语言(MathML)和同步多媒体集成语言(SMIL)都增加了该语言独有的 DOM 方法和接口。

浏览器对象模型(BOM)

用于支持访问和操作浏览器的窗口。

针对浏览器窗口和子窗口(frame)提供了

  • 弹出新浏览器窗口的能力
  • 移动、缩放和关闭浏览器窗口的能力
  • navigator 对象,提供关于浏览器的详尽信息
  • location 对象,提供浏览器加载页面的详尽信息
  • screen 对象,提供关于用户屏幕分辨率的详尽信息
  • performance 对象,提供浏览器内存占用、导航行为和时间统计的详尽信息
  • 对 cookie 的支持
  • 其他自定义对象,如 XMLHttpRequest 和 IE 的 ActiveXObject

JS数据类型

每种编程语言都具有内建的数据类型,而根据使用数据的方式从两个不同的维度将语言进行分类。

  1. 动态/静态
  • 动态类型:运行过程中需要检查数据类型
  • 静态类型:使用之前就需要确认其变量数据类型
  1. 强/弱
  • 强类型:不支持隐式类型转换
  • 弱类型:支持隐式类型转换

隐式类型转换 :在赋值过程中,编译器会把 int 型的变量转换为 bool 型的变量

通过上述的介绍和平时大家的使用JS的数据类型发现。

JS是动态弱类型语言。

由于JS的语言特性,我们可以进而得出另外一个结论:每个变量只不过是一个用于保存任意值的命名占位符。

而谈到JS数据类型,就绕不开针对数据的分类。你没猜错,还是一样的配方,大家熟悉的味道。

ECMAScript 有8 种数据类型

  1. Undefined
  2. Null
  3. Boolean
  4. String
  5. Number
  6. Symbol (ES6新增)
  7. BigInt (ES2020新增)
  8. Object (基本引用类型、)

根据数据存储位置的不同,我们将JS数据类型分为两大类:

  1. 基本数据类型(primary) 存放在栈内存中,类型1-7
  2. 复杂数据类型/引用类型 存放在堆内存中, 类型8

针对老生常谈的问题,我们来搞点不一样的。

JS 判断数据类型方式(4种)

该问题在一些面试题中,出现的频率还挺高。(敲黑板,考试要考!)

1. typeof

typeof 操作符可以确定值的原始类型,也就是说,该操作只能区分基本数据类型,而对于复杂数据类型就鞭长莫及了。

let un, nu =null, bo = true, st = '789', 
    num = 789,sy = Symbol('789'),bi = 789n;
typeof un; // "undefined"
typeof nu; // "object" 这是一个特例,或者说null就是一个特例
typeof bo; //"boolean"
typeof st; // "string"
typeof num;// "number"
typeof sy; // "symbol"
typeof bi; // "bigint"
复制代码

特例分析: null值表示一个空对象指针。所以针对typeof null 返回了一个"object"。

2. Object.prototype.toString.call(xx)

若参数(xx)不为 null 或 undefined,则将参数转为对象,再作判断。转为对象后,取得该对象的 [Symbol.toStringTag] 属性值(可能会遍历原型链)作为 tag,然后返回 "[object " + tag + "]" 形式的字符串。

针对基本数据类型,通过装箱过程转为对象类型。

Object.prototype.toString.call(null) //[ojbect Null]
Object.prototype.toString.call(undefined) //[object Undefined]
Object.prototype.toString.call(true) // [object Boolean]
Object.prototype.toString.call(()=>{}) // [object Function]
复制代码

通过Object.prototype.toString可以将数据类型很容易的分开。但是,每次进行判断的时候,多了一堆额外的信息。所以,我们可以对该方法进行改进。

function getDataType(type){
  return Object.prototype.toString.call(type)
  .split(' ')[1]
  .slice(0,-1)
  .toLocaleLowerCase();
}
getDataType(null) //null
getDataType(undefined) // undefined
getDataType(true) // boolean
getDataType(()=>{}) // function
复制代码

3. instanceof

在一些资料中讲到,instanceof 是用来判断 a 是否为 B 的实例,表达式为:a instanceof B,如果 a 是 B 的实例,则返回 true,否则返回 false

其实这句话是不严谨的。准确的描述应该是:a instanceof B 用于判断实例a原型链中出现过相应的构造函数B,则 instanceof 返回 true 。 instanceof 判断的是 a和B是否有血缘关系,而不是仅仅根据是否是父子关系。

let ar = [];
ar instanceof Array // true
ar instanceof Object // true  如果按照实例的关系的话,这应该返回false
复制代码

多说一句,在ES6中instanceof 操作符会使用 Symbol.hasInstance 函数来确定关系。

这个属性定义在 Function 的原型上,因此默认在所有函数和类上都可以调用。

function Car() {} 
let c = new Car(); 
console.log(Car[Symbol.hasInstance](c)); // true 
复制代码

我们可以通过重新定义该方法,来改变instanceof的值。

class Parent{} 
class Child extends Parent { 
  static [Symbol.hasInstance]() { 
   return false; 
  }  
} 
let c = new Child(); 
console.log(Parent[Symbol.hasInstance](c)); // true 
console.log(c instanceof Parent); // true 
// 
console.log(Child[Symbol.hasInstance](c)); // false 
console.log(c instanceof Child); // false
复制代码

(这里再埋一个伏笔,后期会有针对原型的文章)

4. constructor

只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构造函数。

每次调用构造函数创建一个新实例,实例的内部[[Prototype]]指针就会被赋值为构造函数的原型对象。

实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有

所以,可以通过实例和构造函数原型的关系,来判断是否实例类型。

''.constructor === String; // true;
true.constructor === Boolean; // true;
new Number(1).constructor === Number // number 类型存在包装对象
复制代码

null/undefined是一个假值,没有对应包装对象(无法进行装箱操作),也不是任何构造函数的实例。所以,不存在原型,即,无法使用constructor判断类型。


类型转换(装箱/拆箱)

基本类型是没有任何属性和方法

此时,有人就会有一个疑问,当定义了let str = '789';,此时可以通过str进行属性和方法调用。这不是和上面的那个相悖嘛。

其实,针对基本类型的属性和方法的调用,都是在基本类型的包装对象上进行操作。

装箱转换

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

let str = '789';
str.length; //3 属性调用
str.slice(1); // "89"  方法调用
=======等价于
let strObj = new String(789);
strObj.length; //3
strObj.slice(1); //"89"
复制代码

拆箱转换

在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即,拆箱转换)。

对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。

拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError

let o = {
      valueOf : () => {console.log("valueOf"); return {}},
      toString : () => {console.log("toString"); return {}}
     }
  o * 2
 // valueOf
 // toString
 // TypeError
复制代码

对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

let o = {
        valueOf : () => {console.log("valueOf"); return {}},
        toString : () => {console.log("toString"); return {}}
    }
 o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}
 console.log(o + "")
 // toPrimitive
 // hello
复制代码


相关文章
|
3月前
|
存储 JavaScript 对象存储
js检测数据类型有那些方法
js检测数据类型有那些方法
142 59
|
6天前
|
存储 JavaScript 前端开发
JavaScript中的数据类型以及存储上的差别
通过本文的介绍,希望您能够深入理解JavaScript中的数据类型及其存储差别,并在实际编程中灵活运用这些知识,以提高代码的性能和稳定性。
27 3
|
1月前
|
存储 JavaScript 前端开发
js中的数据类型
JavaScript 中的数据类型包括五种基本类型(String、Number、Undefined、Boolean、Null)和三种引用类型(Object、Array、Function,以及ES6新增的Symbol)。基本类型直接存储值,引用类型存储的是指向实际数据的内存地址。了解它们的区别对于掌握 JavaScript 的变量赋值和函数传参至关重要。
21 1
|
2月前
|
存储 JavaScript 前端开发
JavaScript 数据类型详解:基本类型与引用类型的区别及其检测方法
JavaScript 数据类型分为基本数据类型和引用数据类型。基本数据类型(如 string、number 等)具有不可变性,按值访问,存储在栈内存中。引用数据类型(如 Object、Array 等)存储在堆内存中,按引用访问,值是可变的。本文深入探讨了这两种数据类型的特性、存储方式、以及检测数据类型的两种常用方法——typeof 和 instanceof,帮助开发者更好地理解 JavaScript 内存模型和类型检测机制。
109 0
JavaScript 数据类型详解:基本类型与引用类型的区别及其检测方法
|
2月前
|
JavaScript 前端开发 开发者
【干货拿走】JavaScript中最全的数据类型判断方法!!!!
【干货拿走】JavaScript中最全的数据类型判断方法!!!!
27 1
|
2月前
|
存储 JavaScript 前端开发
JavaScript数据类型全解:编写通用函数,精准判断各种数据类型
JavaScript数据类型全解:编写通用函数,精准判断各种数据类型
45 0
|
3月前
|
JavaScript 前端开发
JavaScript基础知识-基本数据类型和引用数据类型
关于JavaScript基础知识的文章,主要介绍了基本数据类型和引用数据类型。
44 2
JavaScript基础知识-基本数据类型和引用数据类型
|
3月前
|
存储 前端开发 JavaScript
前端基础(三)_JavaScript数据类型(基本数据类型、复杂数据类型)
本文详细介绍了JavaScript中的数据类型,包括基本数据类型(Number、String、Boolean、Undefined、Null)和复杂数据类型(Object),并解释了如何使用`typeof`操作符来识别变量的数据类型。同时,还讨论了对象、函数和数组等复杂数据类型的使用方式。
66 2
|
4月前
|
JavaScript 前端开发
JavaScript基础&实战(1)js的基本语法、标识符、数据类型
这篇文章是JavaScript基础与实战教程的第一部分,涵盖了JavaScript的基本语法、标识符、数据类型以及如何进行强制类型转换,通过代码示例介绍了JS的输出语句、编写位置和数据类型转换方法。
JavaScript基础&实战(1)js的基本语法、标识符、数据类型
|
4月前
|
开发者 图形学 开发工具
Unity编辑器神级扩展攻略:从批量操作到定制Inspector界面,手把手教你编写高效开发工具,解锁编辑器隐藏潜能
【8月更文挑战第31天】Unity是一款强大的游戏开发引擎,支持多平台发布与高度可定制的编辑器环境。通过自定义编辑器工具,开发者能显著提升工作效率。本文介绍如何使用C#脚本扩展Unity编辑器功能,包括批量调整游戏对象位置、创建自定义Inspector界面及项目统计窗口等实用工具,并提供具体示例代码。理解并应用这些技巧,可大幅优化开发流程,提高生产力。
435 1
下一篇
DataWorks