惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
P
Privacy International News Feed
T
Tenable Blog
L
Lohrmann on Cybersecurity
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
S
Securelist
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cisco Talos Blog
Cisco Talos Blog
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
P
Privacy & Cybersecurity Law Blog
Vercel News
Vercel News
Cyberwarzone
Cyberwarzone
月光博客
月光博客
T
The Blog of Author Tim Ferriss
Scott Helme
Scott Helme
爱范儿
爱范儿
Stack Overflow Blog
Stack Overflow Blog
C
Cisco Blogs
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Proofpoint News Feed
A
Arctic Wolf
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
LangChain Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
T
Tor Project blog
Security Latest
Security Latest
Blog — PlanetScale
Blog — PlanetScale
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
博客园 - 三生石上(FineUI控件)
I
InfoQ
Spread Privacy
Spread Privacy
B
Blog RSS Feed
Microsoft Azure Blog
Microsoft Azure Blog
S
SegmentFault 最新的问题
云风的 BLOG
云风的 BLOG
Last Week in AI
Last Week in AI
MongoDB | Blog
MongoDB | Blog
C
CERT Recently Published Vulnerability Notes
A
About on SuperTechFans
博客园_首页
Engineering at Meta
Engineering at Meta
Project Zero
Project Zero
Latest news
Latest news

smallyu的博客

为什么买的加密货币一定要去中心化 RISC-V 虚拟机无法代替 EVM 虚拟机 Polkadot 的 Existential Deposit 机制错在哪里 利息,套利,交易策略,金融市场 LayerZero 从来不挑战比特币的地位 默认参数引起的以太坊节点运行错误 硬件钱包与资产安全 预测市场比币圈更赌场 为什么去中心化的跨链桥不可能实现 我对于 AI 时代的答案 不要投资任何隐私币 区块链技术世界的三大真理 以太坊 AA 钱包的致命问题是什么 基于 AI 语义执行的 MCP 区块链的设计 一个集成 Geth 和 CometBFT 的兼容层 我的加密货币定投策略(二) DeFi 基础: 理解 AMM 定价机制 Go 语言 GMP 调度器的原理是什么 Web3 项目分析计划 对 Psyche Network 项目的分析 continuation 教程: 理解 CPS 如何开发一个比特币符文(Runes)协议 比特币脚本开发教程 我从王垠的计算机科学视频班学到了什么 区块链技术面试题(2025年版) Rust 语言容易让新手困惑的一个“过度优化” Solana 智能合约开发教程 (1) 尝试开发一个最小 EVM 虚拟机 基于 ZK 的链上身份系统设计 一个 Web3 打赏系统的设计 鼓吹 Cursor 的人技术能力都差 关于 Code Review 的礼节 假如启动一个新的以太坊 PoS 网络 发行加密货币的最好方式 所有 BFT 共识的区块链都是中心化的 对 2025 年区块链行业的预测 Restaking 项目的经济难题 如何看懂任意区块链项目的技术架构 为什么不要做以太坊的二次开发 为什么不要做智能合约开发和 DeFi 开发 我的加密货币定投策略(一) PoS 类型的区块链如何处理分叉 Ethereum Casper 为什么需要 EIP-7251 区块链中的 PBFT 不需要第二次投票 开发者的思维方式 发币的核心要点 炒币投资的小 tips 为什么说 PoW 比 PoS 更加去中心化 牛市对普通人来说意味着什么 如何健康地远程工作 为什么比特币不用概率加密函数 程序员的 “服从权威” 心理 区块链技术面试题 如何区分公有链和联盟链 对 Layer 2 项目创业想法的回复 对区块链共识机制的理解 Pebbling Game 鹅卵石游戏 PDP 文件证明的局限性 不要小瞧 ChatGPT 为什么炒币不是一个好主意 一种在区块链上生成随机数的机制 为什么以太坊的私钥计算不可逆 关于以太坊的私钥碰撞 “猜均值的2/3” dApp 游戏设计 Proof of Storage/Space/Replication 的区别 Proofs of Retrievability 文件证明的含义 对 S-PDP 文件证明的示例和解释 我的加密货币交易机器人 对区块链行业的见闻 随机确认块的共识机制 VRF + BFT 共识引起交易失败的问题 为什么要重视编程思想 对 Web 3.0 的理解 GitBook 好用吗? 一种区块链节点存储扩容的方式 一种基于“自我中心主义”的共识机制 链表常见算法题及解析 基于 Multi-Linked List 的区块链设想 理解哈希函数与序列化 联盟链比公有链差在哪儿 在 Dijkstra 算法中保存路径 为什么数字货币使用区块链是政治问题 区块链:下一代数字身份认证体系的基石 网页技术能实现 3D 建模吗? 给区块链一个定义 从 Erlang 开始了解 Actor 模型 一种侧边导航栏的交互方式 Rust 的 ownership 是什么? Haskell 中的 Monad 是什么? 浅析 Libra 背后的区块链技术 对区块链的理性认识 Rust 基础语法概述 基于 Java 的爬虫框架 WebCollector Kotlin:简化版的 Scala 主流编程语言的异常处理机制 Go 语言基本语法 Scala 语法基础 用 Scala 改写 Java 浅度实践 Python 获取海贼王更新信息 HTML5 音乐可视化
JavaScript 有关联数组吗?
2019-05-18 · via smallyu的博客

2019-05-18

如果你接触过PHP,那你对关联数组一定不陌生。C或Java中数组下标都是从0开始的数值,而PHP除了数值,还可以用字符串作为数组的下标。用数值做下标的数组叫做索引数组,用字符串做下标的数组叫做关联数组,他们都是合法的数组。

<?php
$arr[0] = 1;        // 索引数组
$arr["a"] = "b";    // 关联数组

echo $arr[0];       // 1
echo $arr["a"];     // b

在JavaScript中,同样可以使用字符串来作为数组的下标:

let arr = []
arr[0] = 1
arr['a'] = 'b'

昨天,我和漂亮同事在使用JavaScript中用字符串做下标的数组时,遇到了令人困惑的问题。

缘起

在Express.js框架的路由处理中,用res.json()返回数组,下标为数值的数组可以正常返回,下标使用字符串的数组却始终返回空。这是一段最简代码,可以用来描述该过程:

app.get('/', (req, res) => {
  let arr = []
  arr['a'] = 'b'

  console.log(arr)  // [a: 'b']
  res.json(arr)     // []
})

预期返回的数组arr包含1个元素,console.log()直接在命令行打印的文本内容是[a: 'b'],和预期一致,然而如果通过页面请求路由,返回的内容是[],这是匪夷所思的,也就是说res.json()把数组的内容吞掉了。

探寻

为了寻找问题的真实原因,我在框架的中找到res.json()方法的定义:

res.json = function json(obj) {
  var val = obj;
  // ...
  var body = stringify(val, replacer, spaces, escape)
  // ...
  return this.send(body);
};

返回内容body经过了stringify()方法处理,stringify()方法调用的是JavaScript中JSON标准库的方法JSON.stringify():

function stringify (value, replacer, spaces, escape) {
  var json = replacer || spaces
    ? JSON.stringify(value, replacer, spaces)
    : JSON.stringify(value);
  // ...
}

那么就说明,JSON.stringify()方法的返回值,会忽略用字符串做下标的数组。为了证实这一现象,用简单的Demo测试一下:

let arr1 = [], arr2 = []
arr1[0] = 1
arr2['a'] = 'b'

JSON.stringify(arr1)    // "[1]"
JSON.stringify(arr2)    // "[]"

所以问题又来了,JavaScript标准库中的JSON.stringify()方法,为什么要忽略数组中下标为字符串的元素?是有意为之,官方不赞成使用字符串做下标,还是无奈之举,存在不可抗拒的原因无法实现?为了找到问题的根源,我试着从Chrome解析JavaScript的 V8引擎 中寻找JSON.stringify()的定义。

V8引擎是用C++写的,关于JSON.stringify()的定义应该是这一段代码:

// ES6 section 24.3.2 JSON.stringify.
BUILTIN(JsonStringify) {
  HandleScope scope(isolate);
  JsonStringifier stringifier(isolate);
  Handle<Object> object = args.atOrUndefined(isolate, 1);
  Handle<Object> replacer = args.atOrUndefined(isolate, 2);
  Handle<Object> indent = args.atOrUndefined(isolate, 3);
  RETURN_RESULT_OR_FAILURE(isolate,
                           stringifier.Stringify(object, replacer, indent));
}

可以推测出,object即JSON.stringify()处理并返回的内容,返回之前使用args.atOrUndefined()方法进行包装。这里atOrUndefined()被反复调用,传入两个参数,可以理解为,第一个参数isolate保存有完整的参数信息,第二个参数是数据的索引,结合起来便是atOrUndefined()方法要处理的完整数据。

然后看atOrUndefined()的定义,在下面的代码中,tOrUndefined()调用了at()方法,at()方法又调用了Arguments的at方法:

Handle<Object> atOrUndefined(Isolate* isolate, int index) {
  if (index >= length()) {
    return isolate->factory()->undefined_value();
  }
  return at<Object>(index);
}

Handle<S> at(int index) {
  DCHECK_LT(index, length());
  return Arguments::at<S>(index);
}

Arguments::at()方法中,指针value获取了待处理参数的内存地址,然后使用reinterpret_cast对value的值进行类型强转。

Handle<S> at(int index) {
  Object** value = &((*this)[index]);
  // This cast checks that the object we're accessing does indeed have the
  // expected type.
  S::cast(*value);
  return Handle<S>(reinterpret_cast<S**>(value));
}

到这里值就返回了,但是并没能解释为什么使用字符串做下标的数组内容会被忽略。只要是同一个数组,它的值就会保存在一段连续的地址空间中,即使reinterpret_cast处理的是指针变量,也应该无论多少都照常输出才是。

真相

最后,通过Google找到了一个关于数组使用字符串做下标的问题和答案(String index in js array ),我才明白为什么字符串做下标的数组如此特殊,因为JavaScript里压根就没有关联数组!

let arr1 = [], arr2 = []
arr1[0] = 1
arr2['a'] = 'b'

arr1.length     // 1
arr2.length     // 0

给一个数组使用字符串作为下标赋值后,数组的长度不会改变,赋的值并没有作为数组元素储存到数组里。使用字符串作为下标能够正常对数组取值赋值的原因是,JavaScript将字符串作为数组的属性进行了储存。

let arr = []
arr['a'] = 'b'

arr.hasOwnProperty('a')   // true

因此,JSON.stringify()处理的是数组的内容,reinterpret_cast也只是基于指针对数组内容进行类型转换,属性什么的,当然不会有输出!

后续

  1. 为什么console.log()可以将数组的属性也输出?对于要输出的内容,它是怎么定义的?

  2. 为什么JavaScript中typeof []的值是"object",也就是数组的类型是对象,但对象的属性会被处理,而数组不会?