Margrop
Articles241
Tags633
Categories6

Categories

1password 24GB VRAM 2K 3.6 Flash 30B dense 4-bit 量化 6-DoF SLAM AC ACP AI Agent AI Coding Assistant AI Tech AI tutor AI 安全 AI 应用 AI 日记 AI编程助手 ALTK-Evolve AMIE AP API API 定价 API 降价 ARC-AGI-3 ASR ATEM chat 模板 Agent Agent Harness Agent Memory Agent 入侵 Agent 工程 Agent 架构 Agent 检索 Agent 沙箱 Agent 系统 Agent 路由 Agentic AI Agentic tools Ai2 Alertmanager AllenAI Android 17 Antigravity AppDaemon AppWorld Aqara Astra Attention Baseten Benchmark CC-Switch CI/CD CLI Tools CLI工具 CPU 推理 Cache Hit Rate Caddy ChatGPT Claude Code Claude Sonnet ClawLoader Code Interpreter Codex ComfyUI Computer Use Cookie 认证 Cosmos-H-Dreams Cost Optimization Cron DFIR DSpark Date DeepSeek DeepSeek V4 Flash Diagrams.net Diary Diffusers Diffusion Docker Efficiency Tools Embedding English FSDP2 Fable 5 Fireworks AI FlashAttention FlashDreams GGUF GLM 5.2 GLM-5.2 GPT-4.1 GPT-5.6 GPT-Live GPT-Red GPU 加速 GPU 性能分析 Gateway Gemini Gemini 3.5 Flash Gemini API Gemini CLI Gemini Omni Flash Gemma 4 12B Gemma Translator Gemma4 GitHub Actions Google Google AI Google Research Google Sheets Grabette Gripette HA HADashboard HF Security Incident Hailuo Hermes Hexo HomeAssistant Hugging Face IBM Research Inference Providers Isaac Lab Java KV cache Kimi K3 Kubernetes LFM2.5 LFM2.5-VL LLM Router LVM‑Thin Late Interaction LeRobot Linux Liquid AI LiquidAI Live Translate LoRA Luna MCP MTP MacOS Magpie TTS Managed Agents Meta Microsoft 365 Copilot MiniMax Mistral Shieldstral Model Routing MuJoCo Warp Multi-Agent Multi-Vector Muse Glimmer MySQL NAS NIM NVIDIA NeMo Automodel Nemotron 3 Embed Newton Nginx Node.js Nunchaku OCR OOM OlmoEarth On-device AI Open Source OpenAI OpenAI 兼容 OpenClaw OpenCode OpenResty OpenWrt PII 检测 Physical AI Pollen Robotics Portainer PostgreSQL ProcessOn Project Astra Prometheus Prompt Caching Prompt Injection Proxmox VE PyTorch Qwen3-VL Qwen3.6 RAG RPC RTEB Real-Time Inference Red Teaming Responses API SNAP SOCKS5 SPED SVDQuant Scientific Computing Self-Forcing Distillation Sentence Transformers Session Sheets canvas Shell Sol Storage Buckets Strands Agents Subagent Surgical Robotics TTS Terra Think button TimeMachine TutorMoments UML Uptime Kuma V4-Pro VPS VoiceEQ WARP WebRTC WebSocket Windows World Foundation Model agent agentic aligenie aliyun annotation aop autofs backup bash bitwarden boot brew browser budget control centos cert certbot charles chat chrome classloader client clone closures cloudflare command commit commoditization container crontab cyber capability demo dependency deploy developer devtools dll dns docker domain download drafter draw drawio dsm dump dylib environment hooks exception fail2ban feign firewall-cmd flow free tier frontier hosted model frp frpc frps fuckgfw full-duplex function gfw git github gperftools gridea grub guardrail lockout gvt-g hacs havcs heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https huggingface_hub iKuai iMessage image img img2kvm immortalwrt import index inference cost install intel io ios ip iptables iso java javascript jni jnilib jpa js json jsonb jupter jupyterlab jvm k8s kernel key kvm lastpass launchctl learning letsencrypt linux llama.cpp low-code lvm mac mariadb markdown maven md5 microcode mirror modules monitor mount mstsc multimodal mysql n5105 network nfs node node-red nodejs nohup notepad++ npm nssm ntp oop open weights openfeign openssl os ovz packet capture pdf pem perf pip plugin png powerbutton print pro productive struggle pve pvekclean python qcow2 qemu qemu-guest-agent rar reasoning control reasoning slider reboot reflog remote remote desktop renew repo resize retina router runtime safari sata scaffolding scheduled triggers scipy-notebook scoping scp self-play server serverless inference silent test simulated student so speculative decoding spk spring springboot springfox ssh ssl stash string support svg svn swagger sync synology systemctl systemd template terminal txt ubuntu ui undertow unlocker upgrade vLLM vhd vim vm vmdk web windows with worker xml yum zai-org/GLM-5.2 zip 上下文压缩 上下文工程 交换机 人才争夺 代理 企业 AI 优化 低延迟 供应链 健康检查 光猫 免费层 内存 内存优化 内网渗透 分布式推理 分布式训练 医疗 AI 升级 卫星影像 反向代理 反诉 向量检索 启动 告警 告警优化 地球观测 地理空间推理 复盘评测 夏令时 多 token 预测 多智能体 多模态 多模态 Agent 多语言 大厂人才战 大模型评测 天猫精灵 安全 安全事件 安装 定时任务 实时语音 客户端 SDK 容器 导入 小米 屏幕理解 工具审计 工具调用 工具调用拦截 工程团队 工程实践 工程笔记 常用软件 应用市场 延迟优化 开权重 开源权重 开源模型 异常 异步任务 异步委派 微信 心跳 性能优化 成本优化 成本控制 扩散模型 技术 抓包 按 provider 优先级 排查 推理加速 推理速度 推理预算 描述文件 提示词敏感性 故障排查 效率工具 教育数据开源 教育评测 数据工作流 数据流 数据集偏差 文本编码器 旁路由 日志分析 日记 时区 显卡虚拟化 智能家居 智能音箱 服务管理 本地 agent 机器人仿真 机器人学习 机器人数据采集 架构 模块 模型推理 模型评测 模型路由 残存访问 流式推理 流程 流程图 浏览器 漫游 火绒 电信 画图 监控 监控系统 监管 磁盘 稀疏注意力 立体声 端侧 AI 端侧推理 端口 端口冲突 端口扫描 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 自动恢复 自动攻击 自部署 苹果 虚拟机 视觉语言模型 视频生成 视频问诊 认证 证书 评测 评测基准 评测方法学 诉讼 语音 AI 语音 Agent 语音识别 超时 路由 路由器 软件管家 软路由 运维 运维监控 连接保活 连接问题 通信机制 通知 邮件漏发 部署 配置 量化 钉钉 镜像 镜像源 长上下文 长连接 门窗传感器 问题排查 防火墙 阿里云 阿里源 集客 飞书

Hitokoto

Archive

Java / C开发人员应该知道的三个JavaScript怪癖

Java / C开发人员应该知道的三个JavaScript怪癖

JavaScript可能是一种欺骗性的语言,并且可能会造成极大的痛苦,因为它不是100%一致的。众所周知,它确实存在一些不好的,令人迷惑的或冗余的功能:臭名昭著的with语句隐式全局变量不稳定的比较可能是最著名的。

JavaScript是历史上最成功的火焰生成器之一!除了它所存在的缺陷(在新的ECMAScript规范中部分解决了这些缺陷)之外,大多数程序员都讨厌JavaScript的原因有两个:

  • 他们错误地认为DOM等同于JavaScript语言,该语言具有相当糟糕的API。

  • 它们来自C和Java等语言的JavaScript。他们被JavaScript的语法迷住了,以为它的工作方式与那些命令式语言的工作方式相同。这种误解将导致混乱,沮丧和错误。

因此,通常JavaScript的声誉比其应有的差。

在我的职业生涯中,我注意到了几种模式:大多数具有Java或C / C ++背景的开发人员所使用的语言功能在JavaScript中都假定相同,而又完全不同。

本文收集了最麻烦的文章,将Java方式与JavaScript方式进行比较以显示差异,并重点介绍JavaScript的最佳实践。

作用域

大多数开发人员因为被迫而开始使用JavaScript,因此几乎所有的开发人员都在花一点时间学习该语言之前就开始编写代码。每个这样的开发人员都至少一次受到过JavaScript范围的欺骗。

因为JavaScript的语法非常类似于(故意)C系列语言,并且用花括号分隔了functioniffor主体,所以人们可以合理地期望词汇块级作用域。不幸的是,这种情况并非如此。

首先,在JavaScript中,变量作用域是由函数而不是方括号来确定的。换句话说,iffor主体不会创建新的作用域,并且实际上会提升在其主体中声明的变量,即,在声明该变量的最内层函数的开始处创建该变量,否则在全局范围内创建该变量。

其次,该with语句的存在迫使JavaScript作用域是动态的,直到运行时才能确定。听到with不赞成使用该语句,您可能不会感到惊讶:剥离的JavaScriptwith实际上是一种词法范围的语言,即可以通过查看代码来完全确定范围。

正式地,在JavaScript中,有四种来定义一个变量作用域:

  • 语言预定义:默认情况下,所有作用域都包含名称thisarguments
  • 形式参数:为一个函数声明的任何(形式)参数的范围都限于该函数的主体。
  • 函数声明
  • 变量声明

进一步的复杂性是由于隐式全局作用域引起的,该隐式全局作用域分配给(隐式)声明的没有var关键字的变量。这种疯狂与在this没有显式绑定的情况下调用函数时要引用的全局范围的隐式分配成对(在下一节中将对此进行详细介绍)。

在深入研究细节之前,让我们清楚地说明可以用来避免混淆的良好模式:

使用严格模式('use strict';),并将所有变量和函数声明移至每个函数的顶部;避免在forif块内声明变量,以及在这些块内声明函数(出于不同的原因,这超出了本文的范围)。

变量提升(Hoisting)

变量提升是一种简化形式,用于解释声明的实际行为。提升变量在包含它们的函数的开头声明,并初始化为undefined。然后,在原始声明所在的实际行中进行赋值。

看下面的例子:

1
2
3
4
5
6
7
8
9
10
function myFunction() {
console.log(i);
var i = 0;
console.log(i);
if (true) {
var i = 5;
console.log(i);
}
console.log(i);
}

您希望将什么值打印到控制台?您会对以下输出感到惊讶吗?

1
2
3
4
undefined
0
5
5

在该if块内部,该var语句未声明该变量的本地副本i,而是覆盖了之前声明的副本。请注意,第一条console.log语句显示变量的实际值i,该变量的初始值设置为undefined。您可以通过将该"use strict";指令用作函数的第一行来对其进行测试。在严格模式下,必须先声明变量,然后才能使用它,但是您可以检查JavaScript引擎不会抱怨该声明。在一个侧面说明,要知道,你会得到不抱怨的重新声明一var:如果你想赶上这样的错误,你应该更好地处理与棉绒这样的代码JSHint或JSLint的。

现在,让我们再看一个示例,以突出显示变量声明的另一种易于出错的用法:

1
2
3
4
5
6
7
8
9
10
11
var notNull = 1;
function test() {
if (!notNull) {
console.log("Null-ish, so far", notNull);
for(var notNull = 10; notNull <= 0; notNull++){
//..
}
console.log("Now it's not null", notNull);
}
console.log(notNull);
}

尽管您可能会有不同的期望,但if主体仍会执行,因为notNulltest()函数内部声明了名为的变量的本地副本,并且该主体已被提升。类型强制在这里也起作用。

函数声明与函数表达式

提升不仅适用于变量,函数表达式(对于所有意图和用途都是变量),并且函数声明也同样适用。与本主题相比,需要更加谨慎地对待该主题,但是简而言之,函数声明的行为主要类似于函数表达式,只是将它们的声明移到其作用域的开头。

考虑以下示例,该示例显示了函数声明的行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo() {
// A function declaration
function bar() {
return 3;
}
return bar();

// This function declaration will be hoisted and overwrite the previous one
function bar() {
return 8;
}
}

现在,将其与显示函数表达式行为的示例进行比较:

1
2
3
4
5
6
7
8
9
10
11
12
function foo() {
// A function expression
var bar = function() {
return 3;
};
return bar();

// The variable bar already exists, and this code will never be reached
var bar = function() {
return 8;
};
}

有关这些概念的进一步了解,请参见参考资料部分。

With

以下示例显示了只能在运行时确定作用域的情况:

1
2
3
4
5
6
function foo(y) {
var x = 123;
with(y) {
return x;
}
}

如果y有一个名为的字段x,则函数foo()将返回y.x,否则它将返回123。这种编码做法可能会导致运行时错误,因此强烈建议您避免使用该with语句。

展望未来:ECMAScript 6

ECMAScript 6规范将添加添加块级作用域的第五种方法:let语句。考虑下面的代码:

1
2
3
4
5
6
7
8
9
10
function myFunction() {
console.log(i);
var i = 0;
console.log(i);
if (false) {
let i = 5;
console.log(i);
}
console.log(i);
}

ECMAScript中6,声明ilet的体内if将创建一个新的变量本地的if块。作为一种非标准的选择,可以let如下声明块:

1
2
3
4
5
6
var i = 6;
let (i = 0, j = 2) {
/* Other code here */
}
// prints 6
console.log(i);

在上面的代码中,变量ij将仅存在于块内部。在撰写本文时,对支持let有限,即使对于Chrome也是如此。

作用域(Scopes)总结

下表总结了不同语言的作用域:

特征 Java Python JavaScript Warnings
作用域 词法(块) 词法(函数,类或模块) 是的 它的工作方式与Java或C截然不同
块作用域 是的 let关键字(ES6) 再次警告:这不是Java!
吊装 决不! 是的 对于变量和函数表达式,仅悬挂声明。对于函数声明,该定义也将被提升

函数(Funtions)

JavaScript的另一个非常容易被误解的功能是函数,尤其是因为在命令式编程语言中Java,没有这样的概念。

实际上,JavaScript是一种功能编程语言。嗯,不是像Haskell那样的纯粹的函数式编程语言-毕竟,它仍然具有命令式的风格,并且像Scala一样,鼓励而不是简单地允许可变性。尽管如此,JavaScript仍可以用作纯函数式编程语言,而函数调用没有任何副作用。

一等公民(First-Class Citizens)

JavaScript中的函数可以像其他任何类型一样对待,例如String和Number:它们可以存储在变量中,作为参数传递给函数,由函数返回,并存储在数组中。函数还可以具有属性,并且可以动态更改,这是因为…

对象(Objects)

对于大多数JavaScript新手来说,一个非常令人惊讶的事实是函数实际上是对象。在JavaScript中,每个函数实际上都是一个Function对象。该Function构造函数创建一个新的Function对象:

1
var func = new Function(['a', 'b', 'c'], '');

(几乎)等于:

1
function func(a, b, c) { }

我说它们几乎是等效的,因为使用Function构造函数的效率较低,会生成匿名函数,并且不会为其创建上下文创建闭包。Function对象总是在全局范围内创建。

Function函数的类型建立在Object上。通过检查您声明的任何函数,可以很容易看出这一点:

1
2
3
4
5
function test() {}
// prints "object"
console.log(typeof test.prototype);
// prints function Function() { [native code] }
console.log(test.constructor);

这意味着函数可能并且确实具有属性。其中一些已分配给创建时的功能,例如namelength。这些属性分别返回函数定义中的参数名称和数量。

考虑以下示例:

1
2
3
4
5
function func(a, b, c) { }
// prints "func"
console.log(func.name);
// prints 3
console.log(func.length);

但是您甚至可以自己为任何函数设置新属性:

1
2
3
4
5
6
function test() {
console.log(test.custom);
}
test.custom = 123;
// prints 123
test();

函数总结

下表描述了Java,Python和JavaScript中的函数:

特征 Java Python JavaScript Warnings
内置功能 Java 8 Lambdas 是的 是的
回调/命令模式 对象(或Java 8的lambda) 是的 是的 函数(回调)具有可以由“客户端”修改的属性
动态创建 eval-功能对象 eval具有安全性考虑,Function对象可能无法正常工作
属性 可以有属性 不能限制对函数属性的访问

闭包

如果不得不选择我最喜欢的JavaScript功能,那么毫无疑问我会选择闭包。JavaScript是第一种引入闭包的主流编程语言。如您所知,Java和Python的闭包功能弱化了很长时间,您只能从封装作用域中读取(某些)值。

例如,在Java中,匿名内部类提供了类似闭包的功能,但有一些限制。例如,只能在其范围内使用最终局部变量-更好的说,可以读取它们的值。

JavaScript允许完全访问外部范围的变量和函数。可以读取,编写它们,并且如果需要的话,甚至可以将它们隐藏在本地定义中:您可以在“作用域”部分中查看所有这些情况的示例。

更有趣的是,在闭包中创建的函数会记住创建该函数的环境。通过结合使用闭包和函数嵌套,可以使外部函数返回内部函数而不执行它们。此外,您可以使外部函数的局部变量在声明内部函数的执行结束后很长一段时间内在内部函数的关闭中幸存。这是一个非常强大的功能,但也有缺点,因为它是JavaScript应用程序中内存泄漏的常见原因。

一些示例将阐明这些概念:

1
2
3
4
5
6
7
8
9
10
11
12
function makeCounter () {
var i = 0;

return function displayCounter () {
console.log(++i);
};
}
var counter = makeCounter();
// prints 1
counter();
// prints 2
counter();

makeCounter()上面的函数创建并返回另一个跟踪其创建环境的函数。尽管在分配makeCounter()变量时执行结束counter,但是局部变量i保留在displayCounter的闭包中,因此可以在其主体内部进行访问。

如果我们makeCounter再次运行,它将创建一个新的闭包,并带有一个不同的条目i:

1
2
3
4
5
6
7
var counterBis = makeCounter();
// prints 1
counterBis();
// prints 3
counter();
// prints 2
counterBis();

为了使它更有趣,我们可以更新该makeCounter()函数,使其带有一个参数:

1
2
3
4
5
6
7
8
9
10
function makeCounter(i) {
return function displayCounter () {
console.log(++i);
};
}
var counter = makeCounter(10);
// prints 11
counter();
// prints 12
counter();

外部函数参数也保留在闭包中,因此我们这次无需声明局部变量。每次致电makeCounter()都会记住我们设置的初始值,然后继续使用。

对于许多基本的JavaScript模式而言,闭包是最重要的:命名空间,模块,私有var,备忘录是最著名的。

作为示例,让我们看看如何模拟对象的私有变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function Person(name) {
return {
setName: function(newName) {
if (typeof newName === 'string' && newName.length > 0) {
name = newName;
} else {
throw new TypeError("Not a valid name");
}
},
getName: function () {
return name;
}
};
}

var p = Person("Marcello");

// prints "Marcello"
a.getName();

// Uncaught TypeError: Not a valid name
a.setName();

// Uncaught TypeError: Not a valid name
a.setName(2);
a.setName("2");

// prints "2"
a.getName();

通过这种模式,利用闭包,我们可以使用我们自己的setter和getter为属性名称创建包装器。ES5使得此过程变得容易得多,因为您可以使用具有属性的getter和setter的对象来创建对象,并以最精细的方式控制对属性本身的访问。

闭包总结

下表描述了Java,Python和JavaScript中的闭包:

特征 Java Python JavaScript Warnings
关闭 匿名内部类中的弱类(只读) 嵌套def中的弱功能(只读) 是的 内存泄漏
记忆模式 必须使用共享对象 可能使用列表或字典 是的 更好地使用惰性评估
命名空间/模块模式 并不需要 并不需要 是的
私有属性模式 并不需要 不可能 是的 可能会造成混乱

结论

在本文中,我介绍了JavaScript的三个功能,这些功能经常被来自不同语言(尤其是Java和C)的开发人员所误解。特别是,我们讨论了范围,托管,函数和闭包等概念。如果您想深入研究这些主题,请阅读以下文章列表:

  • JavaScript范围界定
  • 函数声明与函数表达式
  • Let声明和let

文章来源
https://www.sitepoint.com/three-javascript-quirks-java-c-developers-should-know/

本文阅读量 --
Author:Margrop
Link:https://blog.margrop.com/post/three-javascript-quirks-java-c-developers-should-know/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可