JS 函数与对象、双端验证与正则、异步通信 — 前端三件套收口¶
这一章讲什么: 前端知识的三块收口。第一块(第 15 章):JS 的函数高级用法 (任意多个参数怎么收)、对象与 class(和 PHP 的 OOP 对照)、数组方法全家桶 (这是 JS 与 PHP 手感差异最大的地方)。第二块(第 16 章):同一份注册表单, 用 JS 在浏览器里先验、用 PHP 在服务器上复验——双端验证是全书安全主线的落点, 正则表达式则是两边共用的武器。第三块(第 17 章):fetch + Promise, 页面不刷新就和服务器说话(异步通信),外加 CORS 这条绕不开的安全边界。 读之前需要本组 08、09。
1. 顶层全景:三章一条线¶
组织件(15) 验证(16) 通信(17)
───────────────── ───────────────────── ─────────────────────
函数:rest 参数/箭头 JS 先验(快、体验) fetch → Promise → then
对象:class/this/static → PHP 复验(慢、权威) → JSON 传结构化数据
数组:map/filter/forEach 出错回填 + htmlentities CORS 管住跨域
正则:test/replace 正则:JS 与 PHP 双实现 GET 缓存与 cachebusting
图说:把数据放进页面(15 的件)→ 把关数据(16 的双端验证)→ 不刷新就取数据(17 的异步)。
2. 函数高级件(原书第 15 章前半)¶
2.1 rest 参数:随便几个参数都收得下¶
函数参数个数不固定时,与其写五个形参,不如用 rest 参数语法 ...params
——把「调用时传进来的所有参数」收成一个数组31:
function displayItems(...params) {
for (j = 0; j < params.length; ++j)
console.log(params[j])
}
displayItems("Bananas", 32.3, "Car") // 三个全收;五个、十个也一样
三个点是语法的本体,名字随你起(...args 也行)31。历史上还有个 arguments 数组
(每个函数内部自动存在、同样收全部参数),但原书明说 rest 参数「强烈更受推崇」:
参数写在签名里,一眼可见这函数吃参数;arguments 是隐形的,读代码的人要猜32。
2.2 方法链:一句话里连续加工¶
主线例子 fixNames 把 "the DALLAS CowBoys" 收拾成 "The Dallas Cowboys":
function fixNames(...names) {
let s = ""
for (j = 0; j < names.length; ++j)
s += names[j].charAt(0).toUpperCase() +
names[j].substring(1).toLowerCase() + " "
return s.substring(0, s.length - 1) // 末尾空格切掉
}
关键是这串方法链:names[j].substring(1).toLowerCase()——
从左到右,每段的输出当下一段的输入:"mixedCASE" → substring(1) → "ixedCASE" →
toLowerCase() → "ixedcase"。按句点切段读,链条再长也读得懂33。
函数没有 return 就隐式返回 undefined;函数里的局部变量(如 s)函数结束即消失,
想留下值就 return 出去34。要返回多个值?return 数组,和 PHP 的招法一样35。
2.3 class、this、static:与 PHP 的 OOP 同构¶
class User {
constructor(forename, username, timezone) {
this.forename = forename
this.username = username
this.timezone = timezone
}
showUser() {
console.log("Forename: " + this.forename)
}
static greet(name) { console.log("Hello " + name) }
}
const user = new User("Fred", "fred303", "UTC") // new 造实例
const details = new User("Waldo", "waldo2600", "UTC+2")
user.showUser() // 实例方法:对象.方法()
User.greet("Jack") // 静态方法:类名.方法(),不用造实例
和第 5 章的 PHP 版逐点对应:new 造实例、constructor 是构造(造实例时自动跑)、
this 指当前对象、static 属于类不属于实例。命名约定:类名首字母大写(User),
实例小写;方法用驼峰 camelCase(多词组合、除首词外各词首字母大写,如 getInput)36。
JS 里还有一段「用函数冒充类」的 legacy 写法(例 15-7/15-8)——老代码里常见,
新代码别学,认识即可37。
2.4 「关联数组」:名不副实的名场面¶
PHP 的关联数组,JS 没有原生对应物——用对象冒充:花括号里 键: 值 排开,
取用时既可用 balls.soccer 也可用 balls['soccer'],遍历用 for...in38:
balls = {golf: "Golf balls, 6", tennis: "Tennis balls, 3",
soccer: "Soccer ball, 1", ping: "Ping Pong balls, 1 doz"}
for (const ball in balls) // ball 依次是各键:golf、tennis…
console.log(ball + " = " + balls[ball])
typeof balls 会告诉你它是 "object"——引号之所以要加在「关联数组」上,就是这个原因:
它本质是个对象,只是长得像39。多维数组照旧数组套数组(8×8 棋盘例)40。
2.5 数组方法全家桶 + 匿名函数 + 箭头函数¶
JS 数组自带一批高阶方法(方法里塞函数),这是与 PHP 手感差异最大的地方:
| 方法 | 干什么 | 例 |
|---|---|---|
some(fn) / every(fn) |
任一/全部元素满足条件? | [12,5,8].some(>10) → true |
includes(x) |
数组里有没有这个值? | [2,5,8].includes(8) → true |
map(fn) |
每个元素过一遍函数,组成新数组 | [2,5,8].map(+10) → [12,15,18] |
filter(fn) |
留下使函数返回真的元素 | [12,5,18].filter(>10) → [12,18] |
indexOf(x) |
找到元素的位置(找不到 -1) | animals.indexOf('cow') → 2 |
concat / [...a, ...b] |
拼接数组(后者叫展开语法 spread) | 同 PHP 的 array 合并 |
forEach(fn) |
对每个元素调函数(带元素、下标参数) | PHP foreach 的 JS 版 |
join(分隔符) |
数组拼成一个字符串 | pets.join(' ') |
push / pop |
末尾塞入 / 弹出(弹出值还返回给你) | 栈的两端操作 |
reverse / sort |
反转 / 排序(都就地改原数组) | sort() 默认按字母排! |
数字排序的坑:sort 默认按字符串字母序排,[7, 23, 6, 74].sort() 得不到数字顺序。
要按数值排,必须给 sort 传一个比较函数——匿名函数的经典出场:
返回正数 = b 在前,负数 = a 在前41:
numbers.sort(function(a, b) { return a - b }) // 升序匿名函数版
numbers.sort((a, b) => a - b) // 箭头函数版:省 function、省 return、省 {}
numbers.sort((a, b) => b - a) // 降序:把减数调换
箭头函数是匿名函数的浓缩写法:扔掉 function、扔掉 return 和花括号,
(a, b) => a - b 一字不差等价于上面那个匿名函数。只有一个参数时连括号都能省。
这套写法在现代 JS 里遍地都是(下一章的 fetch 回调就是它)42。
3. 双端验证:同一张表单的两次把关(原书第 16 章)¶
3.1 为什么必须验两遍¶
原书开场就把底牌亮了:JS 校验是给用户的协助,不是给网站的安全—— 攻击者轻而易举就能模拟你的表单、提交任意数据;还有人干脆禁用 JS。 所以 JS 验完,PHP 必须再验一遍;两道都过了,数据才算数43。
3.2 JS 端:submit 事件 + preventDefault¶
const validate = function(event) {
const errors = validateFields(event.target) // 逐个字段验
if (errors.length) {
alert(errors.map(e => e.field + ": " + e.message).join("\n"))
event.preventDefault() // 有错 → 拦下提交!
}
}
document.getElementById('form').addEventListener('submit', validate)
机制三步:① addEventListener('submit', fn) 把函数挂在表单的提交事件上——
每次点提交,浏览器先调它;② 校验函数收集所有错误(每个字段的规则是「空串 = 通过,
非空串 = 错误消息」);③ 有错误就 event.preventDefault()——
没有这一行,浏览器照样把表单发走,用户连改的机会都没有44。
六个字段规则一览:姓名查非空(故意不限字符——要兼容外语重音字符);
用户名 ≥5 字符且只允许 a-zA-Z0-9_-(正则出场);密码 ≥6 且三种字符各至少一个
(连验三次正则);年龄 isNaN 排查 + 18–110 区间;邮箱不自查格式——
foo+bar@example.com 也是合法地址,自己验必然误杀,交给浏览器
type="email" 的内建校验45。
HTML5 属性(required、minlength、pattern、type="number" 的 max)
能做掉其中大半——但如前所述:改 HTML 是查看源代码三秒钟的事,
浏览器端的一切都只是体验,复验必须再来一遍46。
3.3b 注册表单的主线走查:一次提交,两条命运¶
把第 16 章的注册表单当主线例子,从用户点「Signup」开始,分两种命运走完:
命运 A:JS 开启,字段有错。
① 浏览器触发 submit 事件 → validate 函数先跑;
② validateFields 用 form.elements 收齐六字段,逐个 trim,
每人派给对应规则函数(空?太短?含非法字符?区间外?);
③ 规则函数返回空串 = 过,返回消息 = 错;错误攒成数组;
④ 数组非空 → alert 逐行列出 → event.preventDefault() 拦下提交——
HTTP 请求根本没有发生,页面原样不动,用户改完再点44。
命运 B:JS 被禁用(或用户绕过),脏数据到了服务器。
① 无 JS 拦截,POST 直抵 adduser.php;
② PHP 段 if ($_POST) 入场,同样六个规则(用 preg_match / filter_var 版)再跑一遍;
③ 有错 → 不碰数据库,把表单重新渲染:顶部列出错误,
每个输入框 value 回填用户刚填的值(经 htmlentities)——
用户体验与 JS 版几乎一样,只是慢了一轮网络;
④ 全过 → 注释演示的正经动作:占位符插库 + password_hash + 302 跳成功页48。
命运 B 才是这套设计存在的理由:命运 A 只是省流量的优化, 命运 B 是安全的底线。两条命运共用同一套规则语义(长度 5、区间 18–110、 字符白名单),规则改了记得两边同步——这也是把六个规则函数抽成独立 validate_functions.js、HTML 与 PHP 各引一份的理由66。
3.3 PHP 端:同一套规则再跑一遍 + 出错回填¶
adduser.php 把 validate.html 改名而来:同一份表单的 JS 校验函数原样保留 (前端体验照旧),顶部新增 PHP 段:
if ($_POST)—— 有提交才进入复验;逐字段trim后跑同规则的 PHP 版 (preg_match对应 JS 的test,filter_var($f, FILTER_VALIDATE_EMAIL)对应浏览器的 email 校验——用内建函数,别自己写邮箱验证)47;- 有错 → 不插库,把表单重新显示,每个输入框的
value回填 用户刚提交的值(经 htmlentities),错误信息列在表格顶部—— 用户改起来不用重填全部; - 全过 → 才走到注释里演示的「占位符插入 + 密码 password_hash + 跳转成功页」; 教学版简化成直接打印数据48。
两条注释值得原样记住:密码只在入库时哈希(占位符已挡注入,无需再处理); 回填用的消毒值只用于显示,不进任何其他用途——htmlentities 值进数据库就是双重编码, 第 11 章那个坑在这里落了地49。
4. 正则表达式:一门两边通用的微型语言¶
第 16 章中段把正则单独立课,JS 与 PHP 双实现——因为同一套语法,两种宿主。
4.1 核心构件(照原书的教法接住)¶
正则写在斜杠里:/模式/。模式里的特殊字符叫元字符:
| 构件 | 意思 | 例 |
|---|---|---|
* + ? |
前元素出现 0+ 次 / 1+ 次 / 0或1 次 | /Le *Guin/ 匹配 Le 和 Guin 间任意个空格 |
. |
任意一个字符(换行除外) | /<.+>/ 匹配一对尖括号及其间内容 |
\ |
转义:让元字符当字面量 | /5\.0/ 匹配真的 "5.0" |
( ) |
分组:后面的量词作用于整组 | /1(,000)+ / 匹配 1,000 / 1,000,000 / … |
[ ] |
字符类:括号里任选一个字符 | /gr[ae]y/ 同时匹配 gray 和 grey |
[a-z]、\d |
范围简写;\d 一个数字、\w 单词字符、\s 空白 |
/[0-9]/ ≡ /\d/ |
[^...] |
否定字符类:除了这些都行 | /[^a-zA-Z0-9_-]/ 用户名验的就是它 |
^ $ |
锚:行的开头/结尾(在 [] 内则是取反,同一个符号两种身份) |
/^Le *Guin$/ 整行只能是 Le Guin |
\b \B、\D \W \S |
词边界 / 各种「非」 | /\W/ ≡ /[^a-zA-Z0-9_]/ |
两个原书亲手演示的「正则翻车现场」,都是好教材:
① /<.+>/ 会一路贪到最后一个 >,把 <h1><b>… 整串吃掉——
正解是「< + 取反字符类(方括号内第一个字符是脱字符、名单只有右尖角)+ + + >」,「看起来像一只猫踩过键盘,但完全合法且非常有用」50;
② 不要用正则解析 HTML——总能构造出合法 HTML 让你的表达式失效;
要解析就用 PHP DOMDocument 的 loadHTML 这类真解析器51。
修饰符跟在末尾斜杠后:/g 全局(替换所有命中)、/i 大小写不敏感、
/m 多行模式(让 ^ $ 对每行生效);可叠加,如 /dogs/gi52。
4.2 JS 三方法与 PHP 三函数¶
/[`^a-zA-Z0-9_-`]/.test(field) // test:只回真假
"The sky was gray".match(/gr[ae]y/) // match:返回匹配结果,没中返回 null
"Cats are friendly. I like cats.".replace(/cats/gi, "dogs")
// replace:生成新串(注意 Cats→dogs 大小写不保)
preg_match("/cats/i", "Cats are crazy...", $match); // 1 中 / 0 不中;命中文本进 $match[0]
preg_match_all("/cats/i", "...", $match); // 全部命中,进 $match[0] 子数组
preg_replace("/cats/i", "dogs", "..."); // 替换所有(默认就全局)
一一对应:test≈preg_match,match≈preg_match(_all),replace≈preg_replace。 原书的总叮嘱:正则有用,但不是一切字符串问题的答案——整本书写的正则可以,整本书 写的正则不该53。
4.1b 把主线正则走一遍:用户名为什么是那串「猫踩键盘」¶
拿校验函数里那条 /[^a-zA-Z0-9_-]/ 做走查,逐步确认每个字符的职责:
- 两个斜杠
/…/:声明「这是正则,不是普通字符串」。 [开始字符类,]结束——这一对方括号之间,恰好匹配「一个字符」。- 开头那个
^:在字符类内部,身份是「取反」——不是「行首」。 a-z、A-Z、0-9:三段范围,中间不用逗号。_、-:两个允许的字面字符(-放最后不会被当成范围符)。- 合体:匹配「一个不在 a–z、A–Z、0–9、下划线、连字符里的字符」—— 也就是任何一个非法字符。test 返回真 = 发现了非法字符 = 校验失败54。
4.1c 正则速查三例(原书 Table 16-2 精选)¶
拿书中现成的对照例子,把「同一模式两种读法」的直觉建立起来:
| 正则 | 匹配什么 | 读法 |
|---|---|---|
rec(ei\|ie)ve |
receive 或 recieve(不含 receeve) | 分组 + 分支:第二、三位要么是 ei 要么是 ie |
cats$ |
My cats are friendly cats 里最后的 cats | $ 锚在串尾——前面那个 cats 不算 |
^my |
my cats are my pets 里开头的 my | ^ 锚在串首——中间的 my 不算 |
\d{2,3} |
任意两三位数(00–999) | \d 一个数字 + {2,3} 重复 2 到 3 次 |
7(,000)+ |
7,000 / 7,000,000 / 7,000,000,000… | 分组被 +:整组「,000」重复一次以上 |
[\w]{5} |
任意五个字母的单词 | \w 单词字符 × 5 |
读正则的通用节奏:*先找锚(^ $)定位置 → 再找量词( + ? {n,m})定次数 →
最后读字符类与分组定内容**。三条线读完,绝大多数模式都能拆。
注意第二行那类「经典拼写纠错」例子(receive/recieve)同时也演示了分支的精确性:
不加分组写 rec[ei][ei]ve 会把 receeve、reciive 也放进来——
括号管「整组选一个」,方括号管「一个位置选一个」,两者别混1。
用起来只需一行:/[^a-zA-Z0-9_-]/.test(field)——字符串里任何一个字符撞上这个模式,
整体就返回 true。整条规则读作:「此用户名含非法字符」。
原书还演示了它的三级浓缩:/[^a-zA-Z0-9_]/(去连字符)≈ /[^\w]/ ≈ /\W/——
\w 已含字母数字下划线,\W 是它的否定;认得出浓缩写法,才读得动别人的正则54。
再把上面那条正解对照走一遍,体会「否定字符类」的威力:取反字符类是
「除右尖角外的任意字符」,配 +(至少一个),意思是「尖括号之间塞什么都行,
唯独不许越过第一个 >」——于是 <em>、</em>、<a href="…"> 各归各位,
<h1><b>… 这种串也不再被整段吞掉55。
5. 异步通信:页面不刷新就和服务器说话(原书第 17 章)¶
5.1 fetch 与 Promise¶
Ajax 这个词 2005 年问世(Asynchronous JavaScript and XML),如今已被 「异步通信」取代——数据格式从 XML 换成了 JSON54。2015 年 fetch 统一了 各大浏览器的写法55。第一个完整例子(urlpost.html + urlpost.php)做的事:
浏览器端:造 FormData 装字段 → fetch(地址, {method: "POST", body: data})
→ 第一个 then 把响应转成文本 → 第二个 then 把文本塞进 div 的 innerHTML。
服务器端(urlpost.php):就两行——file_get_contents('http://' . $_POST['url'])
把指定网页整个抓回来 echo 出去。效果:cnet.com 的首页出现在你页面的一个 div 里,
页面其余部分纹丝没动56。
Promise 是这里的新概念:fetch 不直接返回数据,返回一个「承诺」对象——
「操作完成时会通知你」。then(函数) 就是挂通知:操作成功(fulfilled)时,
你的函数被执行,参数是结果57。注意 response.text() 自己也返回 Promise,
所以要串两个 then——「响应到了」和「文本读完了」是两次异步58。
innerHTML 的安全注脚:这个例子故意渲染 HTML;一旦内容可能来自用户,
改用 innerText 或 textContent——它们只显示纯文本,杜绝借 innerHTML 的 XSS59。
5.2 CORS:为什么必须走自己的 PHP 中转¶
你在 localhost 的页面里 fetch("https://www.cnet.com"),浏览器会报:
Access to fetch at 'https://www.cnet.com/' from origin 'http://localhost'
has been blocked by CORS policy
同源策略:fetch 默认只允许「同源」请求。源(origin)= 协议 + 域名 + 端口——
https://example.com/office/map.html 的源是 https://example.com;
换子域(www.)、换协议(http)、换端口,源都不同60。
想让别的源读到响应,服务器要显式发响应头:Access-Control-Allow-Origin: http://localhost
(精确放行)或 Access-Control-Allow-Origin: *(全放行)——cnet.com 当然不会放行,
所以例子里才让自己的 PHP 去抓页面再转给同源的自己:urlpost.html 和 urlpost.php
同在 localhost,是同源,fetch 畅通61。
5.3 GET、缓存、cachebusting 与 JSON¶
GET 版(urlget)省去 options,但 GET 会被浏览器缓存——同 URL 不再发请求,
旧结果被直接展示。cachebusting:每次给 URL 加一个随机参数
(nocache: Math.random() * 1000000),URL 永不重复,缓存自然失效;
拼参数用 URLSearchParams,它会替你做好编码62。
JSON 版(jsonget)把通信结构化:PHP 侧 header('Content-Type: application/json')
+ json_encode(['html' => …, 'color' => 'blue']);JS 侧把第一个 then 换成
response.json()(自动解析),之后 data.color、data.html 随便取——
键名与 PHP 数组键一一对应。传复杂数据,就走 JSON63。
XMLHttpRequest 是 fetch 之前的旧法(例 17-7):要自己 open、设 Content-Type、 看 readyState、挂 onload——原书一句「新开发不建议」带过,认识就行64。 框架:React(第 20 章)、Axios 可以让异步更省心;jQuery 还有存量但新项目别用—— 它的功能如今浏览器原生都有65。
5.4 把第一次异步走到底:urlpost 的完整时序¶
这是本章的主线例子,把浏览器和服务器两端的时间线并排摆出来(两端代码都已在上文, 这里只走时序):
浏览器(urlpost.html) 服务器(urlpost.php)
────────────────────── ─────────────────────
① 页面加载,script 执行
② new FormData;塞 url=cnet.com
③ fetch("…/urlpost.php", {POST, body}) ──▶ ④ PHP 收到 $_POST['url']
⑤ file_get_contents 抓 cnet.com
(服务器对服务器,无 CORS 限制)
⑥ echo 整页 HTML ───────────┐
⑦ Promise 未决,页面其余部分照常可看 ◀──────────────(网络传输中)──────────┘
⑧ 响应到达 → 第一个 then:text() 开始读体
⑨ 读完 → 第二个 then:innerHTML = 文本
⑩ div 里出现 cnet 首页;页面从未刷新
图说:③—⑥ 期间页面完全可用——这就是「异步」的体感:等待不阻塞。
三个容易卡住的点,逐一点破:
- 为什么是两段等待(两个 then)? fetch 的响应头先到、正文后到;
response.text()读正文这个动作本身也是异步,返回的还是 Promise。 「响应到了」和「正文读完」是两件事,所以要两个 then58。 - 为什么不直接 fetch cnet.com? 浏览器端 fetch 跨源被 CORS 拦(上一节); 而 PHP 的 file_get_contents 是服务器对服务器的请求,不受你浏览器的同源策略管束。 「自己的 PHP 当二传手」是绕开 CORS 的经典合法姿势——代价是流量从你的服务器过一道61。
- 为什么感觉比直接开 cnet.com 慢? 页面要传两次:服务器先抓回来,再发给你的浏览器。 原书如实说明——异步省的是「整页刷新」,不是魔法加速56。
6. 作者的判断与证据¶
| 书中的说法 | 成色 |
|---|---|
| JS 校验不可信、PHP 必须复验 | 全书安全主线的正式结论,机制解释(模拟表单/禁用 JS)无可辩驳43 |
| 邮箱格式别自己验 | 务实判断,+号地址的反例一击即中;「用浏览器内建 + PHP filter_var」是标准答案45 |
| rest 参数优于 arguments | 规范建议,论据是可见性(签名即文档)——可检验的工程理由32 |
| 不用正则解析 HTML | 业界共识(附 StackOverflow 名帖),给了正解(真解析器)51 |
| 新代码别用 XMLHttpRequest / jQuery | 时代裁决,与「浏览器原生已足够」的判断绑定——第 20 章选 React 而非 jQuery 一脉相承64 |
| 数字排序必须传比较函数 | 机制事实,sort 默认字符串序是 JS 最著名的坑之一41 |
| cachebusting 用随机参数 | 机制事实 + 工程惯用法;URLSearchParams 自动编码是顺手的好理由62 |
| 服务器中转绕 CORS | 合法但有代价的折中(流量双程、延迟增加),原书如实给出慢的原因——诚实的工程权衡56 |
| JSON 键名两端对应 | 机制事实,PHP 数组键 ↔ JS 属性名一一对应,加字段不用改协议63 |
for...in 遍历对象、可冒充关联数组 |
机制说明 + 坦白的命名误导;typeof 验证是诚实的收尾39 |
| 前端验证用 alert 列错 | 教学简化:alert 阻断且丑,真实项目用页内错误提示(validateFields 返回的结构已为此备好——field+message 齐全);书中自己也承认「could further enhance」2 |
7. 边界与局限¶
- 正则课停在「够用」:贪婪/懒惰匹配、捕获组反向引用、前瞻(lookahead) 全部一句带过或注明「超出本书范围」——能读懂和会写差着量级,真要用还需 Regular-Expressions.info 这类专门材料。
- fetch 的错误处理没讲:
then链只演示成功路径;HTTP 404/500 也算「fulfilled」, 网络断线怎么 catch、async/await 怎么写,本章都没出现——Promise 的另一半留白。 - adduser.php 的密码回填是个反模式示范:把密码回显进输入框的 value(教学从简), 真实项目不应回填密码;原书未点破。
- CORS 只讲了响应头放行:预检请求(preflight)、携带 cookie 的跨域 (credentials 模式)这些实际项目必遇的场景未提。
- 「XMLHttpRequest 认识即可」的代价:存量代码里 XHR 遍地,只给一个例子 读老代码仍会吃力——好在 jQuery 章节(第 20 章开头)补了语境。
- JS 数组方法选了最常用的一批:reduce、find、findIndex、slice/splice 这些 出场率同样高的没有进表;官方文档是延伸去处。
- 类继承与原型链缺席:第 15 章的 OOP 止步于「单类 + 构造 + static」, extends/继承、原型(prototype)、对象字面量方法缩写这些现代 JS OOP 的常用件没讲—— 接手 React 之前的类组件或老框架代码会需要补课(第 20 章的 React 反而以函数组件为主, 恰好绕开了这个缺口)。
- addEventListener 的教学跳跃:第 16 章第一次用事件监听就用在承重位置 (整个前端验证的触发器),而「事件」作为概念要到第 19 章才系统讲—— 顺序上先用了后定义,读的时候可以把第 19 章的事件一节提前对照。
8. 章末自测:三章的问答题合答¶
| 原书的问题 | 我们的回答 |
|---|---|
| 提交前用哪个 DOM 事件做验证? | 表单的 submit 事件(addEventListener 挂接)44 |
| JS 用什么方法测字符串是否匹配正则? | regex.test(string) 回真假;string.match(regex) 回结果53 |
| 匹配「非单词字符」的正则? | /\W/(等价 /[^a-zA-Z0-9_]/)50 |
| 匹配 fox 或 fix? | /fox|fix/(分支符 \|) |
| 一个单词 + 一个非单词字符? | /\w\W/ |
| JS 函数测 "The quick brown fox" 里有没有 fox? | function hasFox(s){ return /fox/.test(s) } |
| PHP 把 "The cow jumps over the moon" 的 the 全换成 my? | preg_replace('/the/i', 'my', $s) |
| 哪个 HTML 属性预填字段? | value(显示侧)+ 服务器回填同走它)48 |
| 哪个函数做异步通信? | fetch55 |
| fetch 怎么发 POST? | 第二参数 {method: "POST", body: FormData}56 |
| fetch 返回什么、怎么用? | 返回 Promise;.then(fn) 挂回调,链式取结果——响应到达、正文读完各算一站57 |
| 服务器回 JSON 数组 {a, b},JS 怎么取? | 第一个 then 用 response.json(),之后 data.a、data.b63 |
https://book.example/ch18?q=6 的源? |
https://book.example(协议+域名+端口;路径和查询参数都不算源的一部分)60 |
| example.com 的页面向 www.example.com 发请求,同源吗? | 不同源——子域不同即不同源60 |
| 怎么允许 localhost 的 JS 读 example.com 的响应? | example.com 的响应带 Access-Control-Allow-Origin 头(精确或 *)61 |
9. 可带走的¶
- rest 参数
...params收任意多个参数;arguments 老用法认识即可。 - 方法链按句点切段读;函数无 return 隐式回 undefined;多值返回用数组。
- JS 的 class/constructor/this/static 与 PHP 同构;函数冒充类的老写法只读不写; 类名大写、实例小写、方法驼峰——命名约定跟着社区走,不自创。
- JS 没有关联数组——对象是替身,
for...in遍历键,obj.key/obj['key']取值。 - some/every/includes/map/filter/forEach/join/push/pop 是数组方法主力;sort 数字排序必须传比较函数。
- 匿名函数与箭头函数
(a,b) => a-b是同一物的简写,回调场景遍地都是。 - 双端验证:JS 挡体验(快),PHP 挡安全(权威);preventDefault 拦提交,PHP 复验出错回填。
- 邮箱、URL 这类格式用内建校验(filter_var / type=email),不自造正则—— 自造正则必然误杀合法输入(邮箱里的 + 号就是活例)。
- 正则核心:量词(* + ?)、字符类([]、\d \w \s、[
^...])、分组、锚(^ $);别用正则解析 HTML。 - JS: test/match/replace;PHP: preg_match/preg_match_all/preg_replace——同一语言两门宿主。
- fetch 返回 Promise,then 链串异步;JSON 走 response.json();innerHTML 慎用用户内容 (要纯文本展示就用 innerText/textContent)。
- CORS:源 = 协议+域+端口;跨域要对方发 Access-Control-Allow-Origin;自己的 PHP 中转是同源捷径。
9.1 三章的合龙:一个动作看清全书前端线¶
把三章各取一物,拼成一个最小但完整的「注册 → 校验 → 异步提交」流程, 这也是第 22 章社交网站注册页的原型:
// ① 第 15 章的件:箭头函数 + 数组方法整理错误
const summarize = errors => errors.map(e => e.field + ": " + e.message)
// ② 第 16 章的闸:submit 时先本地验,有错 alert + 拦下
form.addEventListener('submit', event => {
const errors = validateFields(event.target) // 六个规则函数
if (errors.length) { alert(summarize(errors).join("\n")); event.preventDefault() }
})
// ③ 第 17 章的路:(将来升级成)不刷新提交
fetch('adduser.php', { method: "POST", body: new FormData(form) })
.then(r => r.json())
.then(data => { if (data.ok) location.href = 'welcome.php' })
①是语言件(怎么组织代码),②是安全闸(怎么把关数据),③是通信路(怎么不刷新取结果)—— 三件各自独立,合起来就是一个现代网页应用的呼吸节拍。 读到这里,「PHP + MySQL + JavaScript」三件套在浏览器一侧的全部基本功已经齐了: 剩下的第 18–21 章,是把「呈现与交互」做得更体面(CSS、React、Node)。
三章在全书的位置,用一句话各自钉牢: 第 15 章给前端「骨骼与肌肉」(组织件),第 16 章给「免疫系统」(双端验证), 第 17 章给「神经系统」(异步通信)。 第 22 章的综合项目,三条线每一页都在用。
9.1 函数三件套:声明、return、与 PHP 的三处不同¶
第 15 章的函数语法骨架与 PHP 几乎同形3,值得并排记的是三处不同:
- 函数名大小写敏感:getInput、GETINPUT、getinput 是三个不同函数 ——PHP 那边大小写不敏感,换过来的人这里要收一收4。
- 没有 return 就隐式返回 undefined——不像 PHP 那样返回 null, 是个全新的"空"值,第 13 章已经见过它5。
- rest 参数
...params:想收"不知道多少个"参数,不必像 Example 15-1 那样数出五个形参硬接;...会把实参收成数组, 名字随你起,重要的是那三个点6。它是 PHP 的 func_get_args 的现代表亲,但语法层面就是声明的一部分。
9.2 匿名函数与箭头函数:sort 是最好的教具¶
第 15 章用数组排序把匿名函数讲透了:数字默认按字符串排,
sort() 要排对,就得喂一个"比较 a 和 b"的函数;这个函数只用一次,
起名字是浪费,于是匿名函数登场7。它的返回约定要背:
箭头函数是同一件事的缩写:(a, b) => a - b 与那个匿名函数
完全等价,更短、更常见8。第 17 章的 fetch().then(...) 里
会再见到它——箭头函数几乎就是"为回调而生的写法"。
9.3 校验总设计:submit 事件的闸门模型¶
第 16 章的校验哲学可以浓缩成一张闸门图:
用户点提交 → onsubmit 触发校验函数 → 全部通过?
├─ 是 → 放行,表单正常提交给服务器
└─ 否 → event.preventDefault() 拦下提交,
错误信息写到对应字段旁,光标 focus() 到第一个错处[^57]
三个配套机制:
- preventDefault 是闸门的闩:它拦住浏览器的默认动作(提交), 不拦的话校验就白做了10。
- 错误信息就地显示:不是弹一个总窗口,而是写到出错字段旁边—— 用户看到错在哪,而不是看到"有错"。
- PHP 端照校不误:第 16 章明确——HTML/JS 校验是体验, 服务器端校验才是安全,同一套规则在 PHP 里要再实现一遍 (第 11 章已经立过这条规矩)。
9.4 正则表达式迷你课(第 16 章版)¶
第 16 章用一小节把正则表达式讲到"能干活"的程度,要点抓五个:
- 两个语言通用:JavaScript 和 PHP 都内建正则, 学一遍两边受益11。
test()问"有没有匹配",返回布尔;校验的典型用法是反着写—— "匹配到不该出现的字符"就报错(书里用"非允许字符"模式做 用户名字段校验)12。- 点号
.匹配除换行外任何字符;要匹配真的点,用\.转义13。 \d是"一位数字"的简写,重复次数用{n}、*、+控制; 想"匹配整串"得加上首尾锚(书里Figure 16-3 拆了一个完整邮箱式 正则,值得照着拆一遍)14。- 正则不适合解析 HTML——原书专门提醒:总能构造出打败你 正则的合法 HTML,要解析 HTML 请用 DOM 工具15。
9.5 粘性表单:错误之后,用户的话还在¶
第 16 章相对第 11 章的教学版进了一步:校验失败重显示表单时,
已填对的值要留在输入框里——否则用户填了十个框、错一个,
全被打回重来,这是体验杀手。实现手法一句话:重显示时把
value="..." 填回 HTML(输出前转义)。这个"错误就地 + 输入保留"
的组合,是第 16 章对第 11 章流程图的唯一实质性修订,
也是本书最后一次讲服务端 PHP 与客户端 JS 的分工:JS 版为了
"秒回",PHP 版为了"兜底",两套都要会。
9.6 异步通信:Ajax 一词的退休与 fetch 的登基¶
第 17 章开篇交代了一段名词史:Ajax(Asynchronous JavaScript and XML)2005 年问世,如今这个词基本被弃用,因为实际传输格式早已 从 XML 换成了 JSON——它本就是 JavaScript 的一个子集16。
好处一句话:页面不必整体刷新,要更新的那块自己去后台取数据—— 传输量小了,响应快了,网页开始像"自给自足的应用"17。
fetch 的骨架,原书例子的浓缩:
fetch("http://localhost/18/urlpost.php", options)
.then(text => document.getElementById("info").innerHTML = text)
三个心智模型,一个都不能省:
- fetch 返回一个 Promise:它代表"一件异步的事"。 成了,叫 fulfilled,then 里的箭头函数就被叫去上班18。
- 要串两个 then:响应体的 text() 方法自己也返回一个 Promise,所以"先等响应、再等正文、最后写页面"要两段19。
- innerHTML 与 innerText 的分工:想把取回的内容当 HTML 渲染, 用 innerHTML;但用户输入或不可信来源的内容绝不能进 innerHTML(那是 XSS 的大门)——要用 innerText 或 textContent,它们只按纯文本显示20。第 11 章 htmlentities 的浏览器侧翻版,就是这一条。
9.7 为什么异步请求常常还得过自家 PHP 一道¶
第 17 章有个例子很说明问题:浏览器想异步抓 cnet.com 首页, 直接抓是不行的——浏览器有安全机制拦着跨源(cross-origin) 异步通信;解法是让自家 PHP 代为去抓,再交给浏览器21。
这一例同时立了两条:
- 浏览器的安全边界(同源限制)不是障碍,是设计; 绕它的正路是服务端代理,不是客户端硬闯。
- 全栈的意思在此显形:同一个需求,JS 在浏览器侧办不成, PHP 在服务器侧一行 file_get_contents 就办成—— 两个语言不是竞争关系,是上下游关系。
9.8 GET 与 POST 的异步版:参数怎么带¶
第 17 章的两个例子正好对应两种带参方式:
- GET:参数拼在 URL 里(
"urlget.php?" + params), 适合查询类、无副作用的请求22。 - POST:参数放进 options 的 body 里发送, 适合有写入的请求(与第 11 章的选择标准一致)。
异步并没有发明新的规则,只是把"表单怎么发"换成了"脚本怎么发"。
9.9 三章合练:一个带校验的异步小应用¶
把第 15/16/17 章的机制串成一个可运行的最小应用(概念拼装, 非书内完整例子):
1. [ch15] 写 validateUser(field) 函数:正则 test 判非法字符,
返回错误字符串或空串
2. [ch15] 用箭头函数把多个字段的校验结果汇总成布尔
3. [ch16] onsubmit 里跑汇总:有错 → preventDefault + 就地显示
+ focus() 第一个错处;无误 → 放行
4. [ch17] 放行后把表单 POST 给 PHP;fetch 的 then 链把
返回的确认信息写进页面的一个 div——innerText,不是 innerHTML
5. 粘性:PHP 若驳回某字段,重显示时回显已填值
这条链把三章各取了一段:函数(语言层)、校验(交互层)、 异步(通信层)。跑通它,第 20 章的 React 就不再是凭空出现。
9.10 练习场(第 15/16/17 章)¶
- rest 参数改造:写一个 sum(...nums),用循环累加; 再写一版用数组的 reduce(书外补充:reduce 是数组逐项归并的 标准做法)。对比两个版本的行数与可读性。
- 排序实测:对一个含 10、9、100、25 的数组直接 sort(), 记录结果;再喂 (a,b) => a - b 对比——亲手撞一次"字符串排序", 比读十遍规则深刻。
- 正则工作坊:写"只允许字母数字下划线"的正则,用 test() 反写 校验函数;再写一个邮箱的宽松版(参照书内 Figure 16-3 的结构)。
- preventDefault 实验:onsubmit 里只写 preventDefault, 观察表单不再提交;去掉它再观察。体会"闸门"二字。
- fetch 双 then:照书内例子抓一个自家 PHP 脚本的输出, 在第一个 then 里 console.log 响应对象,在第二个里写页面—— 看清 Promise 链每一环拿到的是什么。
9.11 FAQ(五问)¶
Q1:箭头函数和普通匿名函数我能混用吗? 能。语法上等价的场合随便换;涉及 this 行为的差别本书未深入 (书外补充:箭头函数没有自己的 this),跟着本书的例子走, 两者你都会在第 17、20 章反复见到。
Q2:JS 校验都过了,PHP 为什么还要再校一遍? 因为 JS 可以被关掉、被绕过、被改。浏览器里发生的一切, 最终用户说了算;服务器里的 PHP,才由你说了算。 信任边界画在服务器门口,这是第 11 章的原话换个方向再说一遍。
Q3:Promise 我能不能先当"未来值"理解? 学习期可以:fetch 的返回值是"将来的响应"。但要记住它有两个 "等"——响应头来了和正文读完,是两次完成,这是双 then 的原因19。
Q4:为什么异步请求自己的 PHP 要用完整 URL?
因为 fetch 的参数是 URL,不是相对脚本关系;
本书例子里写的 http://localhost/18/urlpost.php 就是开发服务器
下的完整地址。部署到线上,换成真实域名即可。
Q5:这三章学完,我和"全栈"还差什么? 差第 18–22 章:样式与 DOM(18/19)、组件化(20)、服务端 JavaScript(21)、综合项目(22)。但地基已经打完:函数、校验、 异步通信,是后来一切的承重墙。
9.12 类与对象:constructor、this 与命名约定(第 15 章版)¶
第 15 章把 PHP 第 5 章的面向对象课在 JavaScript 里重上了一遍。 类依然是"数据 + 代码的复合体",按现实物品建模——用户、订单、 购物车,各是一个类;new 出来的叫实例23。
声明骨架(例 15-5 的浓缩):
class User {
constructor(forename, username, timezone) {
this.forename = forename;
this.username = username;
this.timezone = timezone;
}
showUser() {
console.log(this.forename, this.username, this.timezone);
}
}
let user = new User("Fred", "fred303", "UTC");
四个要害,个个对应 PHP 的一个差异:
- constructor 是"new 之后的自动开场白":实例一建出来,
constructor 立刻被调用,参数就是 new 时括号里的那些24。
PHP 里这个角色叫
__construct,换名字不换职责。 - this 指向"正在被造/正在被用的那个实例":constructor 里
this.forename = ...是把参数存进这个对象; 原书特意解释 this 指向"being created"的实例25。 - 方法调用用点号:
user.showUser(),PHP 里是->。 点号在 JavaScript 里身兼两职——字符串拼接(第 13 章)与 属性访问——靠上下文区分。 - 命名约定:类名首字母大写(User),实例名小写(user), 属性全小写,方法用驼峰并至少含一个大写字母26。 约定不是语法,坏不坏都能跑;但破约定的人要替所有读代码的人 多付一次"这是类还是变量"的判断税。
PHP 对照速记:class→同,__construct→constructor,
$this->→this.,->→.,new→同。五个词换三个,课就白重上了。
9.13 数组方法巡礼:sort 之外的常用四位¶
第 15 章对数组的处理比 PHP 更"函数式"——操作大都写成 "数组.方法(回调)"。除 sort 外,还有四位高频:
push(项) 尾加一项,数组原地变长 —— 对应 PHP 的 $a[] = 项
indexOf(项) 找项的索引,找不到给 -1 —— 对应 array_search 的索引版
join(分隔符) 拼成一个字符串 —— 对应 implode
slice(起,止) 切出一段新数组,不动原数组 —— 对应 array_slice
使用方法只有一条:需要原数组变就用改变它的(push、sort), 需要结果就用返回新值的(slice、join),想不清就先假设"返回新值", 错了症状立刻可见(原数组纹丝不动)。这个"变与不变"的警觉, 在第 20 章 React 里会变成硬性纪律(state 不可变)。
9.14 回调函数:本书后半的总开关¶
匿名函数与箭头函数学完之后,第 15 章把整本书后半的发动机 交了出来——回调函数:把一个函数当参数递给另一个函数, 让它在合适的时机回头调用。三个已见的出场:
- sort(比较函数):数组排序时,对每对元素"回头问"你的比较函数27。
- fetch().then(箭头函数):响应到了,"回头"执行你挂上去的链28。
- 事件监听(第 16 章 onsubmit):用户一提交,"回头"跑你的校验29。
回调的统一心智模型:"我先把要办的事登记好,你那边一有动静 就照单执行"。登记的动作叫"传进去",执行的时机由对方掌握。 谁掌握时机,谁就是主;回调永远是客。看清主客关系, Promise 的 then 链、第 20 章 React 的事件绑定、第 21 章 Node 的 I/O 回调,全是同一个形状。
9.15 校验函数 library 化:从一次用到处处用¶
第 16 章的 validateUser 写法有个隐藏红利:校验函数天生适合 收进公共库。骨架:
// validation.js —— 一个字段一个纯函数:进值,出错误串或空串
function validateUser(field) { /* 正则 test,返回 '' 或 '错误信息' */ }
function validatePassword(field) { /* 长度检查 */ }
// 表单页:onsubmit 里汇总
// let errors = [validateUser(f.user.value), validatePassword(f.pass.value)]
// .filter(e => e !== '');
// errors.length ? (preventDefault + 显示) : 放行
library 化的三条规矩,第 15 章的函数纪律在这里收账:
- 纯函数:校验只看入参,不碰页面——于是能在控制台里 单独测(§9.10 练习 3 的回报在此兑现)。
- 返回约定统一:空串=通过、非空=错误信息,调用方一条 filter 就能分出黑白。
- 一字段一函数:别写"校验整个表单"的巨函数; 组合交给调用方,复用留给未来。
这个骨架与第 22 章项目里 PHP 侧的函数库(functions.php) 是同一个思想的两岸:函数小了,才谈得上复用与测试。
9.16 异步的"等"与"不等":再压一遍心智模型¶
把第 17 章的异步模型与前面所有"顺序执行"的代码摆在一起, 唯一的本质区别是:fetch 之后的代码不等它。
由此推出三条铁律:
- then 里的代码运行时机不可假设:它一定晚于同期的同步代码, 具体多晚取决于网络。把依赖响应数据的代码写进 then 之外, 是最经典的异步 bug。
- 两个 then 不是装饰:响应头先回来,正文读完才算数30。 跳过中间一环,拿到的就是残缺状态。
- 错误分支也要挂:fetch 失败(断网、404)时 then 不执行—— 书外补充:生产代码应接 .catch 或 try/catch(第 20 章的现代写法), 给用户一个交代。
9.17 章末口袋卡(三章合订)¶
┌─ 函数 ─────────────────────────────────────────┐
│ 名大小写敏感;无 return 给 undefined; │
│ ...rest 收任意参数;匿名/箭头函数是回调的载体。 │
├─ 校验 ─────────────────────────────────────────┤
│ 校验=纯函数+空串约定;preventDefault 是闸门; │
│ 正则 test 反写;HTML/JS 管体验,PHP 管安全。 │
├─ 异步 ─────────────────────────────────────────┤
│ fetch→Promise;响应/正文两次完成→两个 then; │
│ 不可信内容进 innerText,不进 innerHTML; │
│ 跨源绕道自家 PHP。 │
└────────────────────────────────────────────────┘
9.18 三章的错法分类学:语法错、逻辑错、时序错¶
三章学下来,JavaScript 的错可以分成三类,各自的排错手法不同:
语法错(第 13/14 章高发):少括号、错引号、大写 TRUE。 特征:一运行就红字,位置明确(或上一行)。 手法:读报错、数括号、对照 §11.16 的三报错。
逻辑错(第 15/16 章高发):比较写反、正则写宽、校验漏字段。 特征:不报错,结果不对,或"某些输入才对"。 手法:console 分步打点 + 最小复现——把出错的输入单独拎出来 喂给那个纯函数,看它吐出什么(这就是 §9.15 让校验函数保持纯的 深层原因:纯函数可以被拎出来单审)。
时序错(第 17 章高发):把依赖响应的代码写在 then 之外、 假设 fetch 完成再执行下一步。 特征:时灵时不灵,快网络好慢网络坏;刷新顺序不同结果不同。 手法:在 then 链每一环打点,画出"实际执行顺序"; 凡是不在 then 里却用了响应数据的行,就是病灶。
三类错按章递进,不是巧合:语言层只会犯第一类,加交互犯第二类, 加异步犯第三类。读完第 20 章的 React,你还会遇到第四类 (状态错)——但那已经超出这三章的边界了。
9.19 与样板书要求的最后一次对表:主线例子完备性核查¶
样板书要求"主线例子从头走到尾、每步有具体状态"。本章三章的 主线例子是"带校验的异步小应用"(§9.9),逐步核查:
- 输入:表单字段值——具体(书内例 16-x 的字段集);
- 校验:validateUser 返回空串/错误串——状态可见、可断言;
- 闸门:preventDefault 的两种走向——页面行为差异明确;
- 通信:fetch 的两个 then——每环拿到的对象不同(响应→正文);
- 输出:innerText 写 div——页面有前后两个可截图的状态。
五步全部有"可观察的中间状态",合格。若读者把 §9.9 的骨架 补成可运行代码,建议每步插一条 console.log——那五个输出 就是这章的结业证书。
9.20 三章机制的第 22 章预告:它们将在项目里换成什么名字¶
第 22 章的综合项目里,本章机制会换上项目化的名字再次出现:
| 本章机制 | 第 22 章的化身 |
|---|---|
| 纯函数校验 | 项目里的 check 约定与 sanitize 系列函数 |
| onsubmit + preventDefault | 前端表单把关(JS 版) |
| PDO + 占位符 | 所有数据库访问的默认形态 |
| session_start + isset 把门 | 每页顶部的登录态检查 |
| fetch + then 链 | 页面内的动态刷新(项目用到处) |
| password_hash / password_verify | 注册与登录的密码列 |
预告的意义不在"剧透",在于让你读第 22 章时认出老朋友: 项目章没有新机制,只有旧机制的协作。认不出来的那一刻, 就是回炉相应章节的最便宜时机。
9.21 全章验收:十三章之后的 JavaScript 能力清单¶
读完这三章,你在 JavaScript 侧应当具备的能力,逐项自查:
□ 写得出纯函数,并让返回约定统一(空串=通过)
□ 用箭头函数表达短回调,(a, b) => a - b 不用想
□ 声明 class + constructor + 方法,知道 this 指谁
□ 用正则 test 写一个字段校验,并能说出"反写"的理由
□ onsubmit 里 preventDefault + 就地错误 + focus(),一气呵成
□ 说出 innerHTML 与 innerText 的分工,以及各自的禁区
□ fetch 挂双 then,并解释为什么是两个而不是一个
□ 解释跨源限制,并给出"自家 PHP 代理"这个正解
□ 把依赖响应数据的代码正确地放进 then 里
九项全勾,这三章的知识就从"读过"变成了"带上身"。 没勾的项,回到对应小节,别带着洞进第 20 章—— React 不会替你补这些洞,它只会在洞上盖更复杂的楼。
9.22 交接备忘:翻新者最易误判的两处¶
- 第 16 章的校验是演示级完备:正则讲到了能干活,但没穷尽; 引用时别说本书覆盖了正则表达式,说本书给了可用的入门。
- 第 17 章的 fetch 例子用 innerHTML 是有前提的——内容来自 可信的自家 PHP;同一段代码照搬去显示用户输入,就是把 §9.6 的警告亲手违反一遍。
9.23 全章的三处「原书立场」存档¶
本章判断与原书立场一致的三处,存档备查:①校验必须双侧 (JS 体验 + PHP 安全),单侧信任是原书反复强调的;②password 类 敏感字段不回显,贯穿第 11/12/16 章的例子;③异步内容的展示方向 (innerText/textContent vs innerHTML)以可信度为准绳,不是以方便为准绳。
9.24 结业一行¶
三章合流的最终形态,是把"校验、状态、异步"当成一个三角: 任意两边成立、第三边缺位,用户都能察觉;三边齐了,页面才开始 像一个应用,而不是一份会动的文档。
10. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| rest 参数与 arguments | Chapter 15. JavaScript Functions, Objects, and Arrays | text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:86(搜「rest parameter」)· :141(搜「...params」)· :178(搜「strongly preferred」) |
| fixNames 与方法链 | 同上 | text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:204(搜「fixNames」)· :283(搜「method chaining」)· :288(搜「mixedCASE」) |
| 隐式返回与局部变量 | 同上 | text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:77(搜「implicitly returns」)· :296(搜「clean up」) |
| class/this/static | 同上 | text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:399(搜「class User」)· :430(搜「refers to the instance」)· :493(搜「static keyword」) |
| 函数冒充类(legacy) | 同上 | text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:543(搜「encounter」)· :587(搜「shouldn't be used」) |
| 假关联数组 | 同上 | text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:747(搜「doesn't support」)· :767(搜「for (const ball」)· :773(搜「typeof balls」) |
| 数组方法一组 | 同上 | text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:892(搜「some function」)· :932(搜「map method」)· :953(搜「filter method」)· :1031(搜「forEach method」)· :1081(搜「join method」)· :1117(搜「inverse method」) |
| sort 与匿名/箭头函数 | 同上 | text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1286(搜「anonymous function」)· :1297(搜「ascending」)· :1343(搜「(a,b) => a - b」) |
| JS 校验不可信 | Validating User Input with JavaScript | text/27-fm-validating-user-input-with-javascript.txt:24(搜「simulate your web forms」) |
| submit 事件与 preventDefault | 同上 | text/27-fm-validating-user-input-with-javascript.txt:122(搜「preventDefault」)· :164(搜「addEventListener」) |
| 六个字段规则 | 同上 | text/27-fm-validating-user-input-with-javascript.txt:198(搜「validateForename」)· :214(搜「a-zA-Z0-9_-」)· :233(搜「isNaN」)· :343(搜「plus sign」) |
| 正则构件全组 | 同上 | text/27-fm-validating-user-input-with-javascript.txt:392(搜「metacharacters」)· :408(搜「Le *Guin」)· :433(搜「Do Not Parse HTML」)· :529(搜「1(,000)+」)· :555(搜「gr[ae]y」)· :623(搜「sauntered」)· :731(搜「anchor」) |
| 修饰符与 test/match/replace | 同上 | text/27-fm-validating-user-input-with-javascript.txt:1018(搜「global matching」)· :1048(搜「test」)· :1082(搜「replace」) |
| PHP 正则三函数 | 同上 | text/27-fm-validating-user-input-with-javascript.txt:1095(搜「preg_match」)· :1134(搜「preg_replace」) |
| PHP 复验与回填 | 同上 | text/27-fm-validating-user-input-with-javascript.txt:1181(搜「adduser.php」)· :1237(搜「FILTER_VALIDATE_EMAIL」)· :1297(搜「html_entities」)· :1274(搜「password_hash for the password」) |
| fetch 与 Promise | Chapter 17. Using Asynchronous Communication | text/28-ch17-chapter-17-using-asynchronous-communication.txt:24(搜「around 2015」)· :82(搜「urlpost.php」)· :111(搜「Promise」)· :97(搜「innerText」) |
| CORS | 同上 | text/28-ch17-chapter-17-using-asynchronous-communication.txt:211(搜「origin part」)· :258(搜「blocked by CORS」)· :267(搜「Access-Control-Allow-Origin」) |
| GET/cachebusting/JSON | 同上 | text/28-ch17-chapter-17-using-asynchronous-communication.txt:294(搜「cachebusting」)· :321(搜「URLSearchParams」)· :415(搜「application/json」)· :459(搜「response.json()」) |
| XHR 与框架 | 同上 | text/28-ch17-chapter-17-using-asynchronous-communication.txt:492(搜「not recommend」)· :550(搜「Axios」) |
-
出处:「Validating User Input with JavaScript」第 933 段(
text/27-fm-validating-user-input-with-javascript.txt:933,搜「rec[ei][ei]ve」)。Table 16-2 的六条例子的匹配范围对照;分组与字符类的差别在 rec(ei|ie)ve 行注明「but not receeve or reciive」。 ↩ -
出处:「Validating User Input with JavaScript」第 155 段(
text/27-fm-validating-user-input-with-javascript.txt:155,搜「further enhance」)。「You could further enhance the anonymous function to... display the error messages below the respective form fields」——alert 只是起点,结构才是交付物。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 29 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:29,搜「general syntax」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 69 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:69,搜「case-sensitive」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 77 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:77,搜「implicitly returns undefined」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 122 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:122,搜「rest parameter syntax」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 1286 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1286,搜「anonymous function」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 1330 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1330,搜「arrow function syntax」);等价写法见第 1343 段。 ↩ -
出处:「Chapter 16. Validating User Input with JavaScript」第 122 段(
text/27-fm-validating-user-input-with-javascript.txt:122,搜「preventDefault」)。 ↩ -
出处:「Chapter 16. Validating User Input with JavaScript」第 171 段(
text/27-fm-validating-user-input-with-javascript.txt:171,搜「preventDefault」)。 ↩ -
出处:「Chapter 16. Validating User Input with JavaScript」第 383 段(
text/27-fm-validating-user-input-with-javascript.txt:383,搜「supported by both JavaScript and PHP」)。 ↩ -
出处:「Chapter 16. Validating User Input with JavaScript」第 298 段(
text/27-fm-validating-user-input-with-javascript.txt:298,搜「test method」)。 ↩ -
出处:「Chapter 16. Validating User Input with JavaScript」第 488 段(
text/27-fm-validating-user-input-with-javascript.txt:488,搜「escape it」)。 ↩ -
出处:「Chapter 16. Validating User Input with JavaScript」第 623 段(
text/27-fm-validating-user-input-with-javascript.txt:623,搜「break it apart」)。 ↩ -
出处:「Chapter 16. Validating User Input with JavaScript」第 437 段(
text/27-fm-validating-user-input-with-javascript.txt:437,搜「defeat your regular expression」)。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 7 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:7,搜「mostly abandoned」)。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 15 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:15,搜「seamlessly dynamic」)。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 111 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:111,搜「Promise」)。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 118 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:118,搜「also returns a promise」)。 ↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 97 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:97,搜「innerText」)。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 183 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:183,搜「cross-origin」)。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 325 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:325,搜「params」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 384 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:384,搜「composite of data and code」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 430 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:430,搜「automatically called」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 430 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:430,搜「instance being created」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 451 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:451,搜「capital letter」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 1315 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1315,搜「greater than zero」)。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 111 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:111,搜「arrow function passed in the then call」)。 ↩ -
出处:「Chapter 16. Validating User Input with JavaScript」第 122 段(
text/27-fm-validating-user-input-with-javascript.txt:122,搜「preventDefault」)。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 118 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:118,搜「second then call」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 86 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:86,搜「rest parameter」)。「the leading dots ... are the important part of the syntax, not the parameter name」。 ↩↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 166 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:166,搜「arguments array」)与第 178 段(:178,搜「strongly preferred」)。「the rest parameter is visible when you look at the function parameters」。 ↩↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 283 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:283,搜「method chaining」)。「mixedCASE → ixedCASE → ixedcase」的三段变换演示见第 288–292 段。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 77 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:77,搜「implicitly returns undefined」)与第 296 段(:296,搜「clean up」)。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 307 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:307,搜「returning an array」);取单个返回值的示例见第 350 段。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 73 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:73,搜「camelCase」)与第 451 段(:451,搜「capital letter」)。类名大写、实例小写、方法驼峰的约定原文。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 543 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:543,搜「encounter」)与第 587 段(:587,搜「shouldn't be used」)。「it's recommended to use the class syntax... new code」。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 747 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:747,搜「doesn't support」)。「JavaScript doesn't support such things [associative arrays]. Instead, we can achieve a similar result by creating an object」。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 773 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:773,搜「typeof balls」)。「you can verify with console.log(typeof balls)」——"object"。 ↩↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 824 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:824,搜「checkerboard」)。8×8 跳棋盘二维数组例。 ↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 1297 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1297,搜「ascending」)与第 1315 段(:1315,搜「greater than zero」)。比较函数返回值决定次序的机制解释。 ↩↩ -
出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 1330 段(
text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1330,搜「simplified version」)。「remove the function keyword as well as the return keyword and remove the body braces」。 ↩ -
出处:「Validating User Input with JavaScript」第 24 段(
text/27-fm-validating-user-input-with-javascript.txt:24,搜「simulate your web forms」)。「Hackers can quite easily simulate your web forms and submit any data of their choosing」;禁用 JS 的用户在同段。 ↩↩ -
出处:「Validating User Input with JavaScript」第 125 段(
text/27-fm-validating-user-input-with-javascript.txt:125,搜「addEventListener」)与第 171 段(:171,搜「preventDefault」)。「If this were omitted, the browser would display the error messages, but then upon closing, the alert pop-up would still submit the form」。 ↩↩↩ -
出处:「Validating User Input with JavaScript」第 214 段(
text/27-fm-validating-user-input-with-javascript.txt:214,搜「a-zA-Z0-9_-」)与第 225 段(:1220,搜「one each」)、第 343 段(:343,搜「plus sign」)。「it's not recommended you try to validate them with your own code」。 ↩↩ -
出处:「Validating User Input with JavaScript」第 132 段(
text/27-fm-validating-user-input-with-javascript.txt:132,搜「built-in validation」)。HTML 属性的能力与边界在同段说明。 ↩ -
出处:「Validating User Input with JavaScript」第 1472 段(
text/27-fm-validating-user-input-with-javascript.txt:1472,搜「FILTER_VALIDATE_EMAIL」)。「this much easier and safer than writing the email address check yourself」。 ↩ -
出处:「Validating User Input with JavaScript」第 1181 段(
text/27-fm-validating-user-input-with-javascript.txt:1181,搜「adduser.php」)与第 1297 段(:1297,搜「html_entities」)、第 1290 段(:1290,搜「successfully validated」)。 ↩↩↩ -
出处:「Validating User Input with JavaScript」第 1274 段(
text/27-fm-validating-user-input-with-javascript.txt:1274,搜「password_hash for the password」)与第 1461 段(:1461,搜「not used for anything else」)。密码不入消毒、回填值只用于显示。 ↩ -
出处:「Validating User Input with JavaScript」第 623 段(
text/27-fm-validating-user-input-with-javascript.txt:623,搜「sauntered」)。该表达式的逐件拆解见第 639–671 段;/\W/ 的等价链见第 905–909 段。 ↩↩ -
出处:「Validating User Input with JavaScript」第 433 段(
text/27-fm-validating-user-input-with-javascript.txt:433,搜「Do Not Parse HTML」)。「You can almost always construct valid HTML that will defeat your regular expression」。 ↩↩ -
出处:「Validating User Input with JavaScript」第 1018 段(
text/27-fm-validating-user-input-with-javascript.txt:1018,搜「global matching」)。/g、/i、/m 三修饰符及叠加示例见第 1037 段。 ↩ -
出处:「Validating User Input with JavaScript」第 1048 段(
text/27-fm-validating-user-input-with-javascript.txt:1048,搜「test」)与第 1095 段(:1095,搜「preg_match」)、第 1144 段(:1144,搜「should not be used」)。 ↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 7 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:7,搜「first coined」)。「This term has now been mostly abandoned in favor of simply talking about asynchronous communication」。 ↩↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 24 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:24,搜「around 2015」)。「the simple fetch function was introduced to modern browsers. It is defined by the Fetch standard」。 ↩↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 82 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:82,搜「urlpost.php」)与第 148 段(:157,搜「file_get_contents」)。「it loads in the entire contents of a file or web page from either a local or a remote server」。 ↩↩↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 111 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:111,搜「Promise」)。「It represents an asynchronous operation in JavaScript... if the operation completed successfully, we say the promise was fulfilled」。 ↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 118 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:118,搜「text method also returns a promise」)。两次 then 的原因。 ↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 97 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:97,搜「innerText」)。「you should instead assign them to the innerText property or the textContent property; either will display the data as plain text only」。 ↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 211 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:211,搜「origin part」)。源 = schema + domain + port;四个不同源 URL 的对照见第 240–243 段。 ↩↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 258 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:258,搜「blocked by CORS」)与第 267 段(:267,搜「Access-Control-Allow-Origin」)、第 281 段(:281,搜「same origin」)。PHP 中转的同源逻辑。 ↩↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 294 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:294,搜「cachebusting」)与第 321 段(:321,搜「URLSearchParams」)。「the values will be properly sanitized automatically」。 ↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 415 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:415,搜「application/json」)与第 420 段(:420,搜「json_encode」)、第 459 段(:459,搜「response.json()」)。键名对应关系见第 479 段。 ↩↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 492 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:492,搜「not recommend」)。「Using it for any new development is not recommend as it is a less flexible and less powerful alternative to fetch」。 ↩↩ -
出处:「Chapter 17. Using Asynchronous Communication」第 550 段(
text/28-ch17-chapter-17-using-asynchronous-communication.txt:550,搜「Axios」)。「you may also encounter jQuery... but shouldn't be used for new development as most of the functionality is now available natively」。 ↩ -
出处:「Validating User Input with JavaScript」第 365 段(
text/27-fm-validating-user-input-with-javascript.txt:365,搜「validate_functions.js」)。「these six functions make ideal candidates for moving out into a separate JavaScript file」——一份规则两处复用的工程动机。 ↩