Margrop
Articles394
Tags1012
Categories7

Categories

/health 200 /v1/models 0.025s 0.17.0 0步 0步主动 0步元递归 0步本身 12类 18789 18天idle 18天静默 192.168.x.x 1password 2.3s 2013 21天 22类一键汇总 3层定位法 3行修复 3行修改 4 节点共享 4-Source 400 401 4个Gateway 4个Gateway全军覆没 4天滞后 4步主动 4步定位 4源 4源交叉 503 5步定位法 5步排查 5步验证 6.2.0 6.24 release 6.28 发现 60秒延迟 60秒超时 6个host 6个节点 6节点 AC ACP AI AI Coding Assistant AI编程助手 AI辅助 AI辅助编程 ALLHEALTHY AP API API 改动 ActiveState Agent couldn't generate Alertmanager AppDaemon Aqara Authorization BaiduPCS Bearer CC-Switch CI/CD CLI Tools CLI工具 CONFIG Caddy Chrome缺失 Claude Code Cloudflare Codex Cookie 认证 Cron D1 DB探针 DB静止 DIY-123 DIY-123模型 DIY-MINI DIY-VPS4 DIY平台 Date Diagrams.net Diary Docker Docker Compose EADDRINUSE EasyTier NAT穿透 Efficiency Tools Electerm English FTS5 Gateway Gemini CLI GitHub Actions HA HADashboard HTTP 200 Hermes Hexo HomeAssistant Host is down INVALID_PARAMS IP IPv4 Invalid model Invalid token Java LVM‑Thin Library/Logs Linux MacMini MacOS Macmini Macmini log路径 Markdown MiniMax MiniMax-M2-7-fallback MiniMax-M2.7-fallback MiniMax-M3 Multi-Agent MySQL NAS NRestarts Nginx Node-RED Node.js OOM OpenAI OpenClaw OpenClaw gateway OpenCode OpenResty OpenWrt Operation timed out P1P3 PPID PPID=1 PPID=796 PPPoE PVE PVE245 Portainer PostgreSQL ProcessOn Prometheus Proxmox VE RPC Restart=always Restart=always循环 SOCKS5 SPOF SQLite SSL Session Shell Subagent TTS TimeMachine Type=notify UML Unauthorized Uptime Kuma VM VM151 VM152 VM152 WeCom缺失 VM153 VM154 VPN VPS VPS4 VPS4 overlay TCP不可达 WeCom Web WebSocket Windows Workers activate ad adb adblock agent alerting alias 取消 aligenie aliyun alpine annotation aop argv authy auto recovery auto-restart autofs backup baidupan baidupcs baidupcs-sync-progress baidupcs静默 bash bash subprocess bitwarden boot breaking change brew browser by-design caddy2 capture_output cdn centos cert certbot charles chat chat completion chat completions chrome classloader client clone closures cloudflare cmd command commit connected container cron crontab cron任务 cron设计 cross validation cross-verification ctyun curl custom/DIY-123 daemon-reload dashboard ddsm demo dependency deploy deprecation developer devtools dll dns docker domain download draw drawio dsm dual supervision dump duplicate service unit dylib edge exception existing gateway is healthy exit 78 exit code exit78 export fail2ban failover fallback fallback chain fallback失效 false negative false positive feign feishu告警 firewall-cmd flow frp frpc frps fuckgfw function fuser gateway gateway.log gcc gfw git gitea github golang google_gemma-4 gperftools grep gridea grub gvt-g hacs havcs health check health-check-all heap hello hexo hibernate hidden bomb hidpi hoisting homeassistant host down hosts html htmlparser https iKuai idea idle-detection idle_hours image img img2kvm immortalwrt import inactive index install intel investigation io ios ip iptables iptv ipv6 iso java javascript jetbrains jieba jni jnilib journald journald日志漂移 jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kill orphan kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live log path log rotate loopback-proxy low-code lsof lsof -p lvm lxc m3u8 mac macOS macOS app macos manual mariadb markdown maven md5 meta-acceptance meta-pattern meta-probe microcode minimax mirror misjudgment model alias model id model live test model provider modem modules monitor mount mstsc multisource mysql n2n n5105 nas netstat network new-api newapi nfs node node-red nodejs nohup notepad++ npm nssm ntp one-api oop openai compatible openclaw openclaw/ openfeign openssl orphan process orphan进程 os otp ovz p14 packet capture pat pdf pem perf ping ping通但chat不通 pip plugin png port bind race port=18789 powerbutton print pro probe probe of probe probe-of-probe process check process detection provider token provider/model proxy ps ps -axo args ps -eo args ps+grep pve pvekclean python python subprocess qcow2 qemu qemu-guest-agent qmshutdown rar reboot reconnect循环 reflog release notes remote remote desktop renew repo resize retina root route router rule rules running runtime safari sata schema schema列名 scipy-notebook scoping scp self-blind self-leak self-reference server server is busy service不可信 shared config single point of failure single source single-instance slmgr so socket-proxyd socks source spk split边界 spring springboot springfox sqlite3 CLI ss ss -tlnp ssh ssh probe ssh probe-of-probe ssh timeout ssl stale stash stderr/stdout stderr被吞 stdout/stderr string subprocess supernode supervisor svg svn swagger sync synology system-level daemon system-level vs user-level system-level与user-level抢端口 systemctl systemctl --user systemctl --user disable systemctl daemon-reload systemctl disable systemctl is-active systemctl restart systemctl show systemd systemd --user systemd duplicate service systemd exit 78 systemd restart loop systemd service unit systemd unit systemd unit race systemd user instance systemd-socket systemd-user双重监管 systemd被覆盖 tap tap-windows tapwindows telecom template terminal tls tmux token token失效 totp transient 999 trigram tvbox txt typo ubuntu udisk ui undertow unicode61 unified logging uninstall unit stopped unlocker upgrade upstream upstream alias upstream provider timeout uptimeMs url user-level daemon v1 v1 API v1 chat completions v10探针 v11探针 v12探针 v13探针 v14 v15探针 v1探针 v2 API v2ray v6探针 v7探针 v8探针 vhd vim vlmcsd vm vmdk weakest signal web websocket wechat windows with work day 14 work day 15 work day 17 work day 2 worker wow xiaoya xml yum zip 一行修改 一键idle告警脚本 一键告警脚本 一键解决方案 上海 上海晴 上游LLM容量 不动 不干预 不是我的锅 中国电信 中文搜索 主动0步 主动0步本身 主动不修 主动不追问 主动不追问本身 主动不追问本身也是清单之外 主动不通知 主动不通知本身 主动修 主动修system-level本身也是清单之外 主动修本身也是清单之外 主动反思 主动周一 主动想起 主动意识到 主动意识到0步本身 主动意识到0步本身也是清单之外 主动排查 主动追问 主动通知 云电脑 交叉验证 交换机 人机协作 代理 伏笔 优化 伪故障 但chat 30s+ 但是我的事 体检 保护逻辑本身也是清单之外 修systemd-user本身 修复方案 修挖坑闭环 修正本身 修正递归 值班 假阳 假阳性 假阴 健康检查 健康检查探针 元递归 光猫 克制 全HEALTHY 全员HEALTHY 全绿 全量同步 公网IP 共享配置 内存 内存优化 内网 内网IP 内网渗透 写作 分词 切换 列名误判 升级 协作 单位混淆 博客 又是周五 双重监管 反向代理 反向探针 反常健康 反常稳定 反常稳定本身 反应 vs 知识 反着来 反讽 启动 告警 告警优化 周一 周一焦虑 周三 周二 周二晚上 周二青岛后周三 周五 周五晚上 周六 周六晚上 周四 周四晚上 周报 周日 周日山崎 周日山崎后周一 周日晚上 周末 周末不干预 周末也是修坑日 周末也是清单之外 周末修坑 周末挖坑 周末本身也是清单之外 周末突破 周末第二天 周末第五天 周末落地 周末落地本身 夏令时 多场景 多智能体 多源验证 多节点 多节点管理 大小写敏感 天猫精灵 天翼云 孤儿进程 安全 安装 定时任务 容器 容器网络 宿命雷 导入 小米 山崎 山崎之夜 工作感悟 工作日 工作日常 工作日第三天 工作日第五天 工作日第四天 已通知用户 常用软件 幂等 广告屏蔽 序列号 应用市场 异常 弃用 循环类 心态 心智成长 心理模型 心跳 心跳检查 性能优化 性能最快 感悟 打工 打工人 打工人的克制 打工人的反讽 打工人的无奈 打工人的自指 批量校验 技术 抓包 拼写错误 挖坑→修坑闭环 排查 排查思路 排查流程 探针 探针再升级 探针本身 探针版本 探针的探针 探针管理 探针自己 探针自检 探针踩坑 接受 接受之后 接受修 接受修正 接受层 接受挖坑 接受本身 接受递归 描述文件 放下 故障 故障排查 效率 效率工具 教训 数据 新api 旁路由 旁路进程 无服务器 日志路径 日记 时区 显卡虚拟化 智能家居 智能音箱 最弱信号 服务器 服务管理 架构 梯子 模块 模型别名映射 模型探测 模型端点可达性 模型端点能ping通 模型调用 横线点 死循环 毫秒 流程 流程图 流程管理 浏览器 清单之后 清单之外 清单之外也包括接受本身 清单的元递归 清单设计 清单边界 清单进化 源码备份 漫游 激活 激活循环 火绒 焦虑 玄学 生活 用户主动 用户关机 电信 画图 监控 监控系统 直播源 直觉 磁盘 端口 端口 LISTEN 端口冲突 端口占用 端口扫描 第10天 第10类 第11天 第11类 第12天 第12类 第13天 第13类 第14天 第14类 第15类 第16天 第16类 第17个青岛 第17类 第18天 第18类 第19天 第19类 第20天 第20类 第21个青岛 第21天 第21类 第22天 第22类 第23天 第23类 第24天 第25天 第25类 第26天 第26类 第27天 第27类 第28类 第29类 第30类 第31类 第32类 第33类 第34类 第35类 第4个山崎 第4次复发 第6天 第7天 第8天 第9天 第9类 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 自动恢复 自定义模型 自建应用 自我反思 自我发现 自我打脸 自我盲区 自指 自检撞自检 自检本身 自检脚本 节点角色 虚拟机 被动意识到 角色不匹配 角色误判 角色误配 角色错配 认证 设计偏差 证书 语雀 误判 误报 误报过滤 超时 路由 路由器 软件管家 软路由 运维 运维监控 进程 进程探测 连接保活 连接问题 连续5天 通信机制 通知 通知元递归 通知挖坑 通知本身 部署 部署链路 配置 配置盲 配置落后 重启不写日志 鉴权失效 钉钉 镜像 镜像源 长期稳定 长期静默 长连接 门窗传感器 问题排查 防火墙 阿里云 阿里源 隐藏3天 隐藏雷 集客 青岛 静默期 飞书 飞书告警

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:http://blog.margrop.com/post/three-javascript-quirks-java-c-developers-should-know/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可