javascript数据类型与引用类型的区别以及原始值详解

news2024/7/6 18:28:32

基本数据类型介绍

在JavaScript中,数据类型可以分为基本数据类型与引用数据类型.其中基本数据类型包括
Undefined,Null,Boolean,Number,String5种数据类型,在ES6中新增了两种基本的数据类型,Symbol,bigint
引用类型有Object,Function,Array,Date,RegExp等
在这里插入图片描述

这两种类型区别简略

区别基本数据类型引用数据类型
存储位置栈(stack)堆(heap)
占据空间小,大小固定大,大小不固定,只在栈中引用堆中地址

引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会首先检索其在栈中地址,取得地址后从堆中获得实体

在这里插入图片描述

值的可变性:

基本数据类型: 值不可变,javascript中的原始值(undefined、null、布尔值、数字和字符串)是不可更改的
引用数据类型:引用类型是可以直接改变其值的

//基本数据类型
var str = "abc";
console.log(str[1]="f");    // f
console.log(str);           // abc

//引用数据类型
 var a = [1,2,3];
a[1] = a;
console.log(a[1]); // a

输出如下
在这里插入图片描述

关于原始值

原始类型 (Primitive types)
原始类型也被称为“基本类型”。

目前在 JavaScript 中有以下几种原始类型:

  • string(字符串)
  • number(数字)
  • boolean(布尔)
  • null(空)
  • undefined(未定义)
  • bigint(大整数,ES6)
  • symbol(标志?ES6)

如下:

typeof 'chenpipi';  // "string"
typeof 12345;       // "number"
typeof true;        // "boolean"
typeof null;        // "object"
typeof undefined;   // "undefined"
typeof 12345n;      // "bigint"
typeof Symbol();    // "symbol"

特别特别注意
typeof null 虽然返回 “object”,但是这不代表 null 就是对象,这其实是 JavaScript 的一个 Bug,且从 JavaScript 诞生以来便如此。

在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object。
The history of “typeof null” 2ality – JavaScript and more

原始值 (Primitive values)

原始值也就是原始类型的值(数据)。

A primitive value is data that is not an object and has no methods.

原始值是一种没有任何方法的非对象数据。
也就是说,string、number 和 boolean 等原始类型的值本身是没有任何属性和方法的。

在这里就有人要问了,明明string和number都是有方法和属性的啊

这里有一个非常有意思的点,但是在讨论这个问题之前,先让我们认识下包装对象。

包装对象 (Wrapper objects)

除了 null 和 undefined 外的原始类型都有其相应的包装对象(ps:这句话很重要)

String(字符串)
Number(数字)
Boolean(布尔)
BigInt(大整数,ES6)
Symbol(标志?ES6)

对象 (Object)

对象是引用类型。

首先,包装对象本身是一个对象,也是函数。

String instanceof Object;   // true
String instanceof Function; // true

构造函数 (Constructor)
实例 (Instance)
其中 String、Number 和 Boolean 均支持使用 new 运算符来创建对应的包装对象实例。

例如 String 的声明:

interface StringConstructor {
 new(value?: any): String;
  (value?: any): string;
 readonly prototype: String;
}
declare var String: StringConstructor;

使用 new 运算符得到的数据是对象(Object):

// 字符串
	console.log(typeof 'pp')  // "string"
	console.log(typeof new String('pp'))  // "object"
	console.log(new String() instanceof Object) // true
	// 数字
	console.log(typeof 123)  // "number"
	console.log(typeof new Number(123))  // "object"
	console.log(new Number() instanceof Object)  // true
	// 布尔
	console.log(typeof true)  // "boolean"
	console.log(typeof new Boolean(true)) // "object"
	console.log(new Boolean() instanceof Object) // true

在这里插入图片描述

我们可以调用包装对象实例的 valueOf() 函数来获取其原始值:

// 字符串
let s = new String('pp');
s.valueOf();                // "pp"
typeof s.valueOf();         // "string"
// 数字
let n = new Number(123);
n.valueOf();                // 123
typeof n.valueOf();         // "number"
// 布尔
let b = new Boolean(true);
b.valueOf();                // true
typeof b.valueOf();         // "boolean"

在这里插入图片描述

“异类” (Attention)

而 BigInt 和 Symbol 都属于“不完整的类”,不支持 new 运算符。

例如 BigInt 的声明:

interface BigIntConstructor {
  (value?: any): bigint;
 readonly prototype: BigInt;
}
declare var BigInt: BigIntConstructor;

可以看到 BigInt 的声明中没有 new 运算符相关函数。

普通函数 (Function)

包装对象也可以作为普通函数来使用。

其中 String()、Number() 和 Boolean() 函数都可以用来对任意类型的数据进行显式类型转换。
另外 Object() 函数也可用于显式类型转换,但本文不再展开。

String
示例代码:

typeof String();    // "string"
String();           // ""
String('pp');       // "pp"
String(123);        // "123"
String(true);       // "true"
String(false);      // "false"
String(null);       // "null"
String(undefined);  // "undefined"
String([]);         // ""
String({});         // "[object Object]"

tips:1
当我们使用 String() 函数来转换对象时,JavaScript 会先访问对象上的 toString() 函数,如果没有实现,则会顺着原型链向上查找。

举个例子:执行 String({ toString() { return ‘pp’; } }) 返回的结果是 “pp”;,并非 “[object Object]”;。

所以 String() 函数并不能够用来判断一个值是否为对象(会翻车)。
tips:2
常用的判断对象的方式为 Object.prototype.toString({}) === ‘[object Object]’;。

举个例子:执行 Object.prototype.toString({ toString() { return ‘pp’; } }) 返回的是 “[object Object]”;。

Number

示例代码:

typeof Number();    // "number"
Number();           // 0
Number('');         // 0
Number('pp');       // NaN
Number(123);        // 123
Number(true);       // 1
Number(false);      // 0
Number(null);       // 0
Number(undefined);  // NaN
Number([]);         // 0
Number({});         // NaN

tips:
对于 Number() 函数来说,可能最实用的转换就是将 true 和 false 转换为 1 和 0 吧。

Boolean

示例代码:

typeof Boolean();   // "boolean"
Boolean();          // false
Boolean('');        // false
Boolean('pp');      // true
Boolean(0);         // false
Boolean(1);         // true
Boolean(null);      // false
Boolean(undefined); // false
Boolean([]);        // true
Boolean({});        // true

tips:
某些情况下,我们会在数据中使用 0 和 1 来表示真假状态,此时就可以使用 Boolean() 进行状态的判断。

BigInt

BigInt() 函数用于将整数转换为大整数。

该函数接受一个整数作为参数,传入参数若为浮点数或任何非数字类型数据都会报错。

示例代码:

BigInt(123);        // 123n
BigInt(123n);       // 123n
typeof 123n;        // "bigint"
typeof BigInt(123); // "bigint"

BigInt & Number
需要注意的是,BigInt 和 Number 是不严格相等(宽松相等)的。

示例代码:

123n === 123; // false
123n == 123;  // true

Symbol

Symbol() 函数用于创建一个 symbol 类型的值。

该函数接受一个字符串作为描述符(参数),如果传入其他类型的值则会被转换为字符串(除了 undefined)。

注意,每一个 symbol 值都是独一无二的,即使它们的描述符都是一样的。

且 symbol 类型的数据只能通过 Symbol() 函数来创建。

示例代码:

// 后面的返回值是 Devtools 模拟出来的,并非实际值
Symbol('pp');                   // Symbol(pp)
Symbol(123);                    // Symbol(123)
Symbol(null);                   // Symbol(null)
Symbol({});                     // Symbol([object Object])

// 类型
typeof Symbol('pp');            // "symbol"
Symbol('pp') === Symbol('pp');  // false

// 描述符
Symbol('pp').description;       // "pp"
Symbol(123).description;        // "123"
Symbol({}).description;         // "[object Object]"
Symbol().description;           // undefined
Symbol(undefined).description;  // undefined

原始值不是对象 (Primitive not Object)

有意思的来了~

没有属性和方法 (No properties, no functions)
本文前面有提到:「原始值是一种没有任何方法的非对象数据。」

我们都知道对象(Object)上可以有属性和方法。

但是字符串不是对象,所以你不能给字符串增加属性。

做个小实验:

let a = 'chenpipi';
console.log(a.length);  // 8
// 尝试增加新的属性
a.name = '吴彦祖';
console.log(a.name);    // undefined
// 尝试修改已有的属性
typeof a.slice;         // "function"
a.slice = null;
typeof a.slice;         // "function"

但仍有人反驳写代码的时候明明可以调用到字符串、数字和布尔值上的方法!

比如下面这段代码,能够正常执行并得到符合预期的结果:

// 字符串
let s = 'chenpipi';
s.toUpperCase();      // "CHENPIPI"
'ChenPiPi'.slice(4);  // "PiPi"
// 数字
let n = 123;
n.toString();         // "123"
(123.45).toFixed(2);  // "123.5"
// 布尔值
let b = true;
b.toString();         // "true"
false.toString();     // "false"

tips:
有没有发现,数字的字面量后面不能直接调用函数?例如执行 123.toString() 会报 SyntaxError(语法错误)。
这是因为数字(浮点数)本身会用到小数点 .,而调用函数也需要用小数点,这时就出现了歧义(字符串和布尔值就没有这种烦恼)。
对于这种情况,我们可以使用括号 () 将数字包裹起来,如 (123).toString();或者使用两个连续的小数点 … 来调用函数,如 123…toString()。

奇了怪了

既然字符串不是对象,那么为什么字符串会有属性和方法呢?

转念一想,数字就是数字,数字身上怎么会有方法呢?

这确实不符合逻辑,但是这又与实际相矛盾。

咋回事呢???

替身使者 (I can’t translate this)

答案揭晓~

暗中操作

以字符串(string)为例,当我们在代码中读取字符串的属性或者方法时, JavaScript 会静默地执行下面的操作:

  1. 将字符串通过 new String() 的方式来创建一个临时的包装对象实例;
  2. 通过创建的对象来执行我们的代码逻辑(读取属性或执行函数);
  3. 临时对象不再使用,可以被销毁。
    如下面的栗子:
let a = 'chenpipi';
console.log(a);   // "chenpipi"
// ------------------------------
let b1 = a.length;
console.log(b1);  // 8
// 上面的代码相当于:
let b2 = (new String(a)).length;
console.log(b2);  // 8
// ------------------------------
let c1 = a.toUpperCase();
console.log(c1);  // "CHENPIPI"
// 上面的代码相当于:
let c2 = (new String(a)).toUpperCase();
console.log(c2);  // "CHENPIPI"

数字(number)和布尔值(boolean)同理,但数字通过 new Number() 来创建临时对象,而布尔值则通过 new Boolean() 来创建。

除了上面的例子,最有力的证明,就是他们的构造函数:

'chenpipi'.constructor === String;  // true
(12345).constructor === Number;     // true
true.constructor === Boolean;       // true

这一切都是 JavaScript 在暗中完成的,且过程中产生的临时对象都是一次性的(用完就丢)。

原来如此

芜湖,这么一来就说得通了!

这也就能解释为什么我们能够访问字符串上的属性和方法,却不能增加或修改属性。

那是因为我们实际操作的目标其实是 JavaScript 创建的临时对象,而并非字符串本身!

所以我们的增加或修改操作实际上是生效了的,只不过是在临时对象上生效了!

就像这样:

// 代码中:
let a = 'chenpipi';
a.name = '吴彦祖';
console.log(a.name);  // undefined

// 相当于:
let a = 'chenpipi';
(new String(a)).name = '吴彦祖';
console.log(a.name);  // undefined

// 相当于:
let a = 'chenpipi';
let temp = new String(a);
temp.name = '吴彦祖';
console.log(a.name);  // undefined

总结 (Summary)

以上,就是本篇文章的全部内容了。

最后我们来总结一下:

  1. 多数原始类型都有相应的包装对象;
  2. 有些包装对象可以被 new,有些不行;
  3. 包装对象一般被用来进行显式的类型转换;
  4. 对象上有属性和方法;
  5. 原始值上没有属性和方法;
  6. 原始值上也不能有属性和方法;
  7. 但我们可以像操作对象一样来操作原始值;
  8. 这是因为 JavaScript 在执行代码的时候偷偷搞小动作;
  9. JavaScript 会用临时的包装对象来替原始值执行操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/861164.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript高级:原型和原型链

在 JavaScript 中,原型与原型链是一种强大的继承机制,它使对象之间能够共享属性和方法,从而实现高效的代码复用。虽然这听起来可能有些复杂,但是我们可以用通俗易懂的方式来理解这个概念。本文将为你详解原型和原型链的概念与作用…

win11虚拟机安装

win11虚拟机安装 下载虚拟机客户端安装客户端创建虚拟机下载 ISO切换root账号GNOME桌面 下载虚拟机客户端 版本是16.2.3 链接:https://pan.baidu.com/s/13c6XVWFbeQKbCnrlfxD8cA 提取码:qxdc 安装客户端 安装向导 点击下一步 接收条款,点…

C#小轮子:自动连续Ping网络地址

文章目录 前言Ping代码异步问题 前言 工作中,我们经常用到Ping这个指令,有时候我们需要Ping整个网段来查看这个网段上面有什么设备,哪些Ip地址是通的,这个时候就需要Ping指令 Ping 代码 我这个是批量Ping的代码,而…

Stable Diffusion WebUI安装和使用教程(Windows)

目录 下载Stable Diffusion WebUI运行安装程序,双击webui.bat界面启动插件安装(github)模型下载(有些需要魔法)安装过程遇到的大坑总结参考的博客 整个过程坑巨多,我花了一个晚上的时间才全部搞定,本教程针对有编程基础…

Ctfshow web入门 SSTI 模板注入篇 web361-web372 详细题解 全

CTFshow SSTI web361 笔记分享 一、代码块 变量块 {{}} 用于将表达式打印到模板输出 注释块 {##} 注释 控制块 {%%} 可以声明变量,也可以执行语句 {% for i in .__class__.__mro__[1].__subclasses__() %}{% if i.__name___wrap_close %}{% print i.__init__.…

面试热题(反转链表)

给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 链表的题&#xff0c;大部分都可以用指针或者递归可以做&#xff0c;指针如果做不出来的话&#xff0c;…

adb 通过wifi连接手机

adb 通过wifi连接手机 1. 电脑通过USB线连接手机2. 手机开启USB调试模式&#xff0c;开启手机开发者模式3.手机开启USB调试模式 更多设置-》开发者选项-》USB调试4.点击Wi-Fi 高级设置&#xff0c;可以查看到手机Wi-Fi的IP地址&#xff0c;此IP地址adb命令后面的ip地址&#xf…

竞赛项目 疫情数据分析与3D可视化 - python 大数据

文章目录 0 前言1 课题背景2 实现效果3 设计原理4 部分代码5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 大数据全国疫情数据分析与3D可视化 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff0…

【办公软件】WPS2019打开时报错:安装Office自定义项,安装期间出错

文章目录 一、报错信息二、解决方法&#xff1a; 三、原因 一、报错信息 二、解决方法&#xff1a; 1.按一下左下角【开始】按钮&#xff0c;点击【运行】&#xff0c;在对话框输入【regedit】打开注册表 2.按CtrlF查找【YdWordAddIn.vsto】 3.删除【Manifest】下的数据 (如…

flutter引入高德地图,release 版本闪退问题解决

前提是在确定了闪退是由于地图引起的之后。报错内容如下 1、检查高德开放平台&#xff0c;查看参数是否正确配置&#xff0c;SHA1 和包名是否正确。配置 SHA1 可参考获取 Android 的 SHA1 值_LoveShadowing的博客-CSDN博客 2、检查是否引入 sdk&#xff0c;是否配置混淆&#…

痞子衡嵌入式:AppCodeHub - 一站网罗恩智浦MCU应用程序

近日&#xff0c;恩智浦官方隆重上线了应用程序代码中心(Application Code Hub&#xff0c;简称 ACH)&#xff0c;这是恩智浦 MCUXpresso 软件生态的一个重要组成部分。痞子衡之所以要如此激动地告诉大家这个好消息&#xff0c;是因为 ACH 并不是又一个恩智浦官方 github proje…

k8s集群网络插件搭建——————解决集群notready(k8s1.20版本,docker24)

前面已经提到&#xff0c;在初始化 k8s-master 时并没有网络相关配置&#xff0c;所以无法跟 node 节点通信&#xff0c;因此状态都是“NotReady”。但是通过 kubeadm join 加入的 node 节点已经在k8s-master 上可以看到。 那么&#xff0c;这个时候我们该怎么办呢&#xff1f;…

微信小程序备案流程

微信小程序备案流程 &#x1f4d4; 千寻简笔记介绍 千寻简笔记已开源&#xff0c;Gitee与GitHub搜索chihiro-notes&#xff0c;包含笔记源文件.md&#xff0c;以及PDF版本方便阅读&#xff0c;且是用了精美主题&#xff0c;阅读体验更佳&#xff0c;如果文章对你有帮助请帮我…

基于Promise.resolve实现Koa请求队列中间件

本文作者为360奇舞团前端工程师 前言 最近在做一个 AIGC 项目&#xff0c;后端基于 Koa2 实现。其中有一个需求就是调用兄弟业务线服务端 AIGC 能力生成图片。但由于目前兄弟业务线的 AIGC 项目也是处于测试阶段&#xff0c;能够提供的服务器资源有限&#xff0c;当并发请求资源…

uniapp 扩展组件 uni-forms 的表单验证之 validateFunction 只响应一次

uniapp 扩展组件 uni-forms 的表单验证之 validateFunction 只响应一次 问题代码官方说明参考资料 问题代码 直接从官方示例中复制过来改的。为了演示 <template><view><uni-forms ref"form" :modelValue"formData" :rules"rules&qu…

vue-pc端实现按钮防抖处理-自定义指令

前言 我们经常在移动端会处理按钮和输入框的防抖和节流处理&#xff0c;在pc端很少进行这样的操作 但是在pc端也是可以进行按钮的防抖操作&#xff0c;这样也是比较合理&#xff0c;可以不用但不可以不会 我们只要配合vue项目自定义指令加上全局注册&#xff0c;就可以实现按…

maven的入门使用

maven的入门使用 1.Maven&#xff08;Maven Apache&#xff09;是一个流行的项目构建和管理工具&#xff0c;2.项目结构和POM文件&#xff1a;3.POM文件&#xff08;Project Object Model&#xff09;4.依赖管理&#xff1a; 在POM文件中5.生命周期和构建过程1.前言2.插件系统3…

【MAC】 M2 brew安装 docker 运行失败 解决

MAC 安装 brew install --cask docker 之后一直显示docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?. 网上看了一些文章 发现 这个不适用于M2 所以要从官网上下载 docker 安装成功

Java算法_ 房子强盗(LeetCode_Hot100)

题目描述&#xff1a;你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表…

在 Windows 上安装 OpenCV – C++ / Python

在这篇博文中&#xff0c;我们将在 Windows 上安装适用于 C 和 Python 的 OpenCV。 C 安装是在自定义安装 exe 文件的帮助下完成的。而Python的安装是通过Anaconda完成的。 在 Windows 上安装 OpenCV – C / Python&#xff08;opencv官方Wndows上安装openCV- C/ Pthon 的链接…