编程语言研究书库

JS 函数与对象、双端验证与正则、异步通信 — 前端三件套收口

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 ——把「调用时传进来的所有参数」收成一个数组1:

function displayItems(...params) {
  for (j = 0; j < params.length; ++j)
    console.log(params[j])
}
displayItems("Bananas", 32.3, "Car")   // 三个全收;五个、十个也一样

三个点是语法的本体,名字随你起(...args 也行)1。历史上还有个 arguments 数组 (每个函数内部自动存在、同样收全部参数),但原书明说 rest 参数「强烈更受推崇」: 参数写在签名里,一眼可见这函数吃参数;arguments 是隐形的,读代码的人要猜2。

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"。按句点切段读,链条再长也读得懂3。 函数没有 return 就隐式返回 undefined;函数里的局部变量(如 s)函数结束即消失, 想留下值就 return 出去4。要返回多个值?return 数组,和 PHP 的招法一样5。

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)6。 JS 里还有一段「用函数冒充类」的 legacy 写法(例 15-7/15-8)——老代码里常见, 新代码别学,认识即可7。

2.4 「关联数组」:名不副实的名场面

PHP 的关联数组,JS 没有原生对应物——用对象冒充:花括号里 键: 值 排开, 取用时既可用 balls.soccer 也可用 balls['soccer'],遍历用 for...in8:

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"——引号之所以要加在「关联数组」上,就是这个原因: 它本质是个对象,只是长得像9。多维数组照旧数组套数组(8×8 棋盘例)10。

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 在前11:

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 回调就是它)12。

3. 双端验证:同一张表单的两次把关(原书第 16 章)

3.1 为什么必须验两遍

原书开场就把底牌亮了:JS 校验是给用户的协助,不是给网站的安全—— 攻击者轻而易举就能模拟你的表单、提交任意数据;还有人干脆禁用 JS。 所以 JS 验完,PHP 必须再验一遍;两道都过了,数据才算数13。

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()—— 没有这一行,浏览器照样把表单发走,用户连改的机会都没有14。

六个字段规则一览:姓名查非空(故意不限字符——要兼容外语重音字符); 用户名 ≥5 字符且只允许 a-zA-Z0-9_-(正则出场);密码 ≥6 且三种字符各至少一个 (连验三次正则);年龄 isNaN 排查 + 18–110 区间;邮箱不自查格式—— foo+bar@example.com 也是合法地址,自己验必然误杀,交给浏览器 type="email" 的内建校验15。

HTML5 属性(required、minlength、pattern、type="number" 的 max) 能做掉其中大半——但如前所述:改 HTML 是查看源代码三秒钟的事, 浏览器端的一切都只是体验,复验必须再来一遍16。

3.3b 注册表单的主线走查:一次提交,两条命运

把第 16 章的注册表单当主线例子,从用户点「Signup」开始,分两种命运走完:

命运 A:JS 开启,字段有错。 ① 浏览器触发 submit 事件 → validate 函数先跑; ② validateFields 用 form.elements 收齐六字段,逐个 trim, 每人派给对应规则函数(空?太短?含非法字符?区间外?); ③ 规则函数返回空串 = 过,返回消息 = 错;错误攒成数组; ④ 数组非空 → alert 逐行列出 → event.preventDefault() 拦下提交—— HTTP 请求根本没有发生,页面原样不动,用户改完再点14。

命运 B:JS 被禁用(或用户绕过),脏数据到了服务器。 ① 无 JS 拦截,POST 直抵 adduser.php; ② PHP 段 if ($_POST) 入场,同样六个规则(用 preg_match / filter_var 版)再跑一遍; ③ 有错 → 不碰数据库,把表单重新渲染:顶部列出错误, 每个输入框 value 回填用户刚填的值(经 htmlentities)—— 用户体验与 JS 版几乎一样,只是慢了一轮网络; ④ 全过 → 注释演示的正经动作:占位符插库 + password_hash + 302 跳成功页17。

命运 B 才是这套设计存在的理由:命运 A 只是省流量的优化, 命运 B 是安全的底线。两条命运共用同一套规则语义(长度 5、区间 18–110、 字符白名单),规则改了记得两边同步——这也是把六个规则函数抽成独立 validate_functions.js、HTML 与 PHP 各引一份的理由18。

3.3 PHP 端:同一套规则再跑一遍 + 出错回填

adduser.php 把 validate.html 改名而来:同一份表单的 JS 校验函数原样保留 (前端体验照旧),顶部新增 PHP 段:

  1. if ($_POST) —— 有提交才进入复验;逐字段 trim 后跑同规则的 PHP 版 (preg_match 对应 JS 的 test,filter_var($f, FILTER_VALIDATE_EMAIL) 对应浏览器的 email 校验——用内建函数,别自己写邮箱验证)19;
  2. 有错 → 不插库,把表单重新显示,每个输入框的 value 回填 用户刚提交的值(经 htmlentities),错误信息列在表格顶部—— 用户改起来不用重填全部;
  3. 全过 → 才走到注释里演示的「占位符插入 + 密码 password_hash + 跳转成功页」; 教学版简化成直接打印数据17。

两条注释值得原样记住:密码只在入库时哈希(占位符已挡注入,无需再处理); 回填用的消毒值只用于显示,不进任何其他用途——htmlentities 值进数据库就是双重编码, 第 11 章那个坑在这里落了地20。

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>… 整串吃掉—— 正解是「< + 取反字符类(方括号内第一个字符是脱字符、名单只有右尖角)+ + + >」,「看起来像一只猫踩过键盘,但完全合法且非常有用」21; ② 不要用正则解析 HTML——总能构造出合法 HTML 让你的表达式失效; 要解析就用 PHP DOMDocument 的 loadHTML 这类真解析器22。

修饰符跟在末尾斜杠后:/g 全局(替换所有命中)、/i 大小写不敏感、 /m 多行模式(让 ^ $ 对每行生效);可叠加,如 /dogs/gi23。

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。 原书的总叮嘱:正则有用,但不是一切字符串问题的答案——整本书写的正则可以,整本书 写的正则不该24。

4.1b 把主线正则走一遍:用户名为什么是那串「猫踩键盘」

拿校验函数里那条 /[^a-zA-Z0-9_-]/ 做走查,逐步确认每个字符的职责:

  1. 两个斜杠 /…/:声明「这是正则,不是普通字符串」。
  2. [ 开始字符类,] 结束——这一对方括号之间,恰好匹配「一个字符」。
  3. 开头那个 ^:在字符类内部,身份是「取反」——不是「行首」。
  4. a-z、A-Z、0-9:三段范围,中间不用逗号。
  5. _、-:两个允许的字面字符(- 放最后不会被当成范围符)。
  6. 合体:匹配「一个不在 a–z、A–Z、0–9、下划线、连字符里的字符」—— 也就是任何一个非法字符。test 返回真 = 发现了非法字符 = 校验失败25。

4.1c 正则速查三例(原书 Table 16-2 精选)

拿书中现成的对照例子,把「同一模式两种读法」的直觉建立起来:

正则匹配什么读法
rec(ei|ie)vereceive 或 recieve(不含 receeve)分组 + 分支:第二、三位要么是 ei 要么是 ie
cats$My cats are friendly cats 里最后的 cats$ 锚在串尾——前面那个 cats 不算
^mymy 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 也放进来—— 括号管「整组选一个」,方括号管「一个位置选一个」,两者别混26。

用起来只需一行:/[^a-zA-Z0-9_-]/.test(field)——字符串里任何一个字符撞上这个模式, 整体就返回 true。整条规则读作:「此用户名含非法字符」。 原书还演示了它的三级浓缩:/[^a-zA-Z0-9_]/(去连字符)≈ /[^\w]/ ≈ /\W/—— \w 已含字母数字下划线,\W 是它的否定;认得出浓缩写法,才读得动别人的正则25。

再把上面那条正解对照走一遍,体会「否定字符类」的威力:取反字符类是 「除右尖角外的任意字符」,配 +(至少一个),意思是「尖括号之间塞什么都行, 唯独不许越过第一个 >」——于是 <em>、</em>、<a href="…"> 各归各位, <h1><b>… 这种串也不再被整段吞掉27。

5. 异步通信:页面不刷新就和服务器说话(原书第 17 章)

5.1 fetch 与 Promise

Ajax 这个词 2005 年问世(Asynchronous JavaScript and XML),如今已被 「异步通信」取代——数据格式从 XML 换成了 JSON25。2015 年 fetch 统一了 各大浏览器的写法27。第一个完整例子(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 里, 页面其余部分纹丝没动28。

Promise 是这里的新概念:fetch 不直接返回数据,返回一个「承诺」对象—— 「操作完成时会通知你」。then(函数) 就是挂通知:操作成功(fulfilled)时, 你的函数被执行,参数是结果29。注意 response.text() 自己也返回 Promise, 所以要串两个 then——「响应到了」和「文本读完了」是两次异步30。

innerHTML 的安全注脚:这个例子故意渲染 HTML;一旦内容可能来自用户, 改用 innerText 或 textContent——它们只显示纯文本,杜绝借 innerHTML 的 XSS31。

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)、换端口,源都不同[^30]。 想让别的源读到响应,服务器要显式发响应头:Access-Control-Allow-Origin: http://localhost (精确放行)或 Access-Control-Allow-Origin: *(全放行)——cnet.com 当然不会放行, 所以例子里才让自己的 PHP 去抓页面再转给同源的自己:urlpost.html 和 urlpost.php 同在 localhost,是同源,fetch 畅通32。

5.3 GET、缓存、cachebusting 与 JSON

GET 版(urlget)省去 options,但 GET 会被浏览器缓存——同 URL 不再发请求, 旧结果被直接展示。cachebusting:每次给 URL 加一个随机参数 (nocache: Math.random() * 1000000),URL 永不重复,缓存自然失效; 拼参数用 URLSearchParams,它会替你做好编码33。

JSON 版(jsonget)把通信结构化:PHP 侧 header('Content-Type: application/json')

  • json_encode(['html' => …, 'color' => 'blue']);JS 侧把第一个 then 换成 response.json()(自动解析),之后 data.color、data.html 随便取—— 键名与 PHP 数组键一一对应。传复杂数据,就走 JSON34。

XMLHttpRequest 是 fetch 之前的旧法(例 17-7):要自己 open、设 Content-Type、 看 readyState、挂 onload——原书一句「新开发不建议」带过,认识就行35。 框架:React(第 20 章)、Axios 可以让异步更省心;jQuery 还有存量但新项目别用—— 它的功能如今浏览器原生都有36。

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。 「响应到了」和「正文读完」是两件事,所以要两个 then30。
  • 为什么不直接 fetch cnet.com? 浏览器端 fetch 跨源被 CORS 拦(上一节); 而 PHP 的 file_get_contents 是服务器对服务器的请求,不受你浏览器的同源策略管束。 「自己的 PHP 当二传手」是绕开 CORS 的经典合法姿势——代价是流量从你的服务器过一道32。
  • 为什么感觉比直接开 cnet.com 慢? 页面要传两次:服务器先抓回来,再发给你的浏览器。 原书如实说明——异步省的是「整页刷新」,不是魔法加速28。

6. 作者的判断与证据

书中的说法成色
JS 校验不可信、PHP 必须复验全书安全主线的正式结论,机制解释(模拟表单/禁用 JS)无可辩驳13
邮箱格式别自己验务实判断,+号地址的反例一击即中;「用浏览器内建 + PHP filter_var」是标准答案15
rest 参数优于 arguments规范建议,论据是可见性(签名即文档)——可检验的工程理由2
不用正则解析 HTML业界共识(附 StackOverflow 名帖),给了正解(真解析器)22
新代码别用 XMLHttpRequest / jQuery时代裁决,与「浏览器原生已足够」的判断绑定——第 20 章选 React 而非 jQuery 一脉相承35
数字排序必须传比较函数机制事实,sort 默认字符串序是 JS 最著名的坑之一11
cachebusting 用随机参数机制事实 + 工程惯用法;URLSearchParams 自动编码是顺手的好理由33
服务器中转绕 CORS合法但有代价的折中(流量双程、延迟增加),原书如实给出慢的原因——诚实的工程权衡28
JSON 键名两端对应机制事实,PHP 数组键 ↔ JS 属性名一一对应,加字段不用改协议34
for...in 遍历对象、可冒充关联数组机制说明 + 坦白的命名误导;typeof 验证是诚实的收尾9
前端验证用 alert 列错教学简化:alert 阻断且丑,真实项目用页内错误提示(validateFields 返回的结构已为此备好——field+message 齐全);书中自己也承认「could further enhance」37

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 挂接)14
JS 用什么方法测字符串是否匹配正则?regex.test(string) 回真假;string.match(regex) 回结果24
匹配「非单词字符」的正则?/\W/(等价 /[^a-zA-Z0-9_]/)21
匹配 fox 或 fix?`/fox
一个单词 + 一个非单词字符?/\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(显示侧)+ 服务器回填同走它)17
哪个函数做异步通信?fetch27
fetch 怎么发 POST?第二参数 {method: "POST", body: FormData}28
fetch 返回什么、怎么用?返回 Promise;.then(fn) 挂回调,链式取结果——响应到达、正文读完各算一站29
服务器回 JSON 数组 {a, b},JS 怎么取?第一个 then 用 response.json(),之后 data.a、data.b34
https://book.example/ch18?q=6 的源?https://book.example(协议+域名+端口;路径和查询参数都不算源的一部分)38
example.com 的页面向 www.example.com 发请求,同源吗?不同源——子域不同即不同源38
怎么允许 localhost 的 JS 读 example.com 的响应?example.com 的响应带 Access-Control-Allow-Origin 头(精确或 *)32

9. 可带走的

  1. rest 参数 ...params 收任意多个参数;arguments 老用法认识即可。
  2. 方法链按句点切段读;函数无 return 隐式回 undefined;多值返回用数组。
  3. JS 的 class/constructor/this/static 与 PHP 同构;函数冒充类的老写法只读不写; 类名大写、实例小写、方法驼峰——命名约定跟着社区走,不自创。
  4. JS 没有关联数组——对象是替身,for...in 遍历键,obj.key/obj['key'] 取值。
  5. some/every/includes/map/filter/forEach/join/push/pop 是数组方法主力;sort 数字排序必须传比较函数。
  6. 匿名函数与箭头函数 (a,b) => a-b 是同一物的简写,回调场景遍地都是。
  7. 双端验证:JS 挡体验(快),PHP 挡安全(权威);preventDefault 拦提交,PHP 复验出错回填。
  8. 邮箱、URL 这类格式用内建校验(filter_var / type=email),不自造正则—— 自造正则必然误杀合法输入(邮箱里的 + 号就是活例)。
  9. 正则核心:量词(* + ?)、字符类([]、\d \w \s、[^...])、分组、锚(^ $);别用正则解析 HTML。
  10. JS: test/match/replace;PHP: preg_match/preg_match_all/preg_replace——同一语言两门宿主。
  11. fetch 返回 Promise,then 链串异步;JSON 走 response.json();innerHTML 慎用用户内容 (要纯文本展示就用 innerText/textContent)。
  12. 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 几乎同形39,值得并排记的是三处不同:

function 名(参数, ...) { 语句; return 值; }
  • 函数名大小写敏感:getInput、GETINPUT、getinput 是三个不同函数 ——PHP 那边大小写不敏感,换过来的人这里要收一收40。
  • 没有 return 就隐式返回 undefined——不像 PHP 那样返回 null, 是个全新的"空"值,第 13 章已经见过它41。
  • rest 参数 ...params:想收"不知道多少个"参数,不必像 Example 15-1 那样数出五个形参硬接;... 会把实参收成数组, 名字随你起,重要的是那三个点42。它是 PHP 的 func_get_args 的现代表亲,但语法层面就是声明的一部分。

9.2 匿名函数与箭头函数:sort 是最好的教具

第 15 章用数组排序把匿名函数讲透了:数字默认按字符串排, sort() 要排对,就得喂一个"比较 a 和 b"的函数;这个函数只用一次, 起名字是浪费,于是匿名函数登场43。它的返回约定要背:

比较函数返回 > 0  → b 排到 a 前面
比较函数返回 < 0  → a 排到 b 前面
返回 0            → 平手,谁前谁后都行

箭头函数是同一件事的缩写:(a, b) => a - b 与那个匿名函数 完全等价,更短、更常见44。第 17 章的 fetch().then(...) 里 会再见到它——箭头函数几乎就是"为回调而生的写法"。

9.3 校验总设计:submit 事件的闸门模型

第 16 章的校验哲学可以浓缩成一张闸门图:

用户点提交 → onsubmit 触发校验函数 → 全部通过?
   ├─ 是 → 放行,表单正常提交给服务器
   └─ 否 → event.preventDefault() 拦下提交,
           错误信息写到对应字段旁,光标 focus() 到第一个错处[^57]

三个配套机制:

  • preventDefault 是闸门的闩:它拦住浏览器的默认动作(提交), 不拦的话校验就白做了45。
  • 错误信息就地显示:不是弹一个总窗口,而是写到出错字段旁边—— 用户看到错在哪,而不是看到"有错"。
  • PHP 端照校不误:第 16 章明确——HTML/JS 校验是体验, 服务器端校验才是安全,同一套规则在 PHP 里要再实现一遍 (第 11 章已经立过这条规矩)。

9.4 正则表达式迷你课(第 16 章版)

第 16 章用一小节把正则表达式讲到"能干活"的程度,要点抓五个:

  • 两个语言通用:JavaScript 和 PHP 都内建正则, 学一遍两边受益46。
  • test() 问"有没有匹配",返回布尔;校验的典型用法是反着写—— "匹配到不该出现的字符"就报错(书里用"非允许字符"模式做 用户名字段校验)47。
  • 点号 . 匹配除换行外任何字符;要匹配真的点,用 \. 转义48。
  • \d 是"一位数字"的简写,重复次数用 {n}、*、+ 控制; 想"匹配整串"得加上首尾锚(书里Figure 16-3 拆了一个完整邮箱式 正则,值得照着拆一遍)49。
  • 正则不适合解析 HTML——原书专门提醒:总能构造出打败你 正则的合法 HTML,要解析 HTML 请用 DOM 工具50。

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 的一个子集51。

好处一句话:页面不必整体刷新,要更新的那块自己去后台取数据—— 传输量小了,响应快了,网页开始像"自给自足的应用"52。

fetch 的骨架,原书例子的浓缩:

fetch("http://localhost/18/urlpost.php", options)
  .then(text => document.getElementById("info").innerHTML = text)

三个心智模型,一个都不能省:

  • fetch 返回一个 Promise:它代表"一件异步的事"。 成了,叫 fulfilled,then 里的箭头函数就被叫去上班53。
  • 要串两个 then:响应体的 text() 方法自己也返回一个 Promise,所以"先等响应、再等正文、最后写页面"要两段54。
  • innerHTML 与 innerText 的分工:想把取回的内容当 HTML 渲染, 用 innerHTML;但用户输入或不可信来源的内容绝不能进 innerHTML(那是 XSS 的大门)——要用 innerText 或 textContent,它们只按纯文本显示55。第 11 章 htmlentities 的浏览器侧翻版,就是这一条。

9.7 为什么异步请求常常还得过自家 PHP 一道

第 17 章有个例子很说明问题:浏览器想异步抓 cnet.com 首页, 直接抓是不行的——浏览器有安全机制拦着跨源(cross-origin) 异步通信;解法是让自家 PHP 代为去抓,再交给浏览器56。

这一例同时立了两条:

  • 浏览器的安全边界(同源限制)不是障碍,是设计; 绕它的正路是服务端代理,不是客户端硬闯。
  • 全栈的意思在此显形:同一个需求,JS 在浏览器侧办不成, PHP 在服务器侧一行 file_get_contents 就办成—— 两个语言不是竞争关系,是上下游关系。

9.8 GET 与 POST 的异步版:参数怎么带

第 17 章的两个例子正好对应两种带参方式:

  • GET:参数拼在 URL 里("urlget.php?" + params), 适合查询类、无副作用的请求57。
  • 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 章)

  1. rest 参数改造:写一个 sum(...nums),用循环累加; 再写一版用数组的 reduce(书外补充:reduce 是数组逐项归并的 标准做法)。对比两个版本的行数与可读性。
  2. 排序实测:对一个含 10、9、100、25 的数组直接 sort(), 记录结果;再喂 (a,b) => a - b 对比——亲手撞一次"字符串排序", 比读十遍规则深刻。
  3. 正则工作坊:写"只允许字母数字下划线"的正则,用 test() 反写 校验函数;再写一个邮箱的宽松版(参照书内 Figure 16-3 的结构)。
  4. preventDefault 实验:onsubmit 里只写 preventDefault, 观察表单不再提交;去掉它再观察。体会"闸门"二字。
  5. 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 的原因54。

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 出来的叫实例58。

声明骨架(例 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 时括号里的那些59。 PHP 里这个角色叫 __construct,换名字不换职责。
  • this 指向"正在被造/正在被用的那个实例":constructor 里 this.forename = ... 是把参数存进这个对象; 原书特意解释 this 指向"being created"的实例60。
  • 方法调用用点号:user.showUser(),PHP 里是 ->。 点号在 JavaScript 里身兼两职——字符串拼接(第 13 章)与 属性访问——靠上下文区分。
  • 命名约定:类名首字母大写(User),实例名小写(user), 属性全小写,方法用驼峰并至少含一个大写字母61。 约定不是语法,坏不坏都能跑;但破约定的人要替所有读代码的人 多付一次"这是类还是变量"的判断税。

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(比较函数):数组排序时,对每对元素"回头问"你的比较函数62。
  • fetch().then(箭头函数):响应到了,"回头"执行你挂上去的链63。
  • 事件监听(第 16 章 onsubmit):用户一提交,"回头"跑你的校验64。

回调的统一心智模型:"我先把要办的事登记好,你那边一有动静 就照单执行"。登记的动作叫"传进去",执行的时机由对方掌握。 谁掌握时机,谁就是主;回调永远是客。看清主客关系, 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 章的函数纪律在这里收账:

  1. 纯函数:校验只看入参,不碰页面——于是能在控制台里 单独测(§9.10 练习 3 的回报在此兑现)。
  2. 返回约定统一:空串=通过、非空=错误信息,调用方一条 filter 就能分出黑白。
  3. 一字段一函数:别写"校验整个表单"的巨函数; 组合交给调用方,复用留给未来。

这个骨架与第 22 章项目里 PHP 侧的函数库(functions.php) 是同一个思想的两岸:函数小了,才谈得上复用与测试。

9.16 异步的"等"与"不等":再压一遍心智模型

把第 17 章的异步模型与前面所有"顺序执行"的代码摆在一起, 唯一的本质区别是:fetch 之后的代码不等它。

顺序代码:        A → B → C,严格先后
含 fetch 的代码:  A → fetch(后台进行中) → C 立刻跑;
                  B 挂在 then 上,响应就绪才插队执行

由此推出三条铁律:

  • then 里的代码运行时机不可假设:它一定晚于同期的同步代码, 具体多晚取决于网络。把依赖响应数据的代码写进 then 之外, 是最经典的异步 bug。
  • 两个 then 不是装饰:响应头先回来,正文读完才算数65。 跳过中间一环,拿到的就是残缺状态。
  • 错误分支也要挂: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),逐步核查:

  1. 输入:表单字段值——具体(书内例 16-x 的字段集);
  2. 校验:validateUser 返回空串/错误串——状态可见、可断言;
  3. 闸门:preventDefault 的两种走向——页面行为差异明确;
  4. 通信:fetch 的两个 then——每环拿到的对象不同(响应→正文);
  5. 输出: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 交接备忘:翻新者最易误判的两处

  1. 第 16 章的校验是演示级完备:正则讲到了能干活,但没穷尽; 引用时别说本书覆盖了正则表达式,说本书给了可用的入门。
  2. 第 17 章的 fetch 例子用 innerHTML 是有前提的——内容来自 可信的自家 PHP;同一段代码照搬去显示用户输入,就是把 §9.6 的警告亲手违反一遍。

9.23 全章的三处「原书立场」存档

本章判断与原书立场一致的三处,存档备查:①校验必须双侧 (JS 体验 + PHP 安全),单侧信任是原书反复强调的;②password 类 敏感字段不回显,贯穿第 11/12/16 章的例子;③异步内容的展示方向 (innerText/textContent vs innerHTML)以可信度为准绳,不是以方便为准绳。

9.24 结业一行

三章合流的最终形态,是把"校验、状态、异步"当成一个三角: 任意两边成立、第三边缺位,用户都能察觉;三边齐了,页面才开始 像一个应用,而不是一份会动的文档。

10. 原文地图

主题原书章原文位置
rest 参数与 argumentsChapter 15. JavaScript Functions, Objects, and Arraystext/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 JavaScripttext/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 与 PromiseChapter 17. Using Asynchronous Communicationtext/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」)

Footnotes

  1. 出处:「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」。 ↩ ↩2

  2. 出处:「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」。 ↩ ↩2

  3. 出处:「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 段。 ↩

  4. 出处:「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」)。 ↩

  5. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 307 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:307,搜「returning an array」);取单个返回值的示例见第 350 段。 ↩

  6. 出处:「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」)。类名大写、实例小写、方法驼峰的约定原文。 ↩

  7. 出处:「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」。 ↩

  8. 出处:「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」。 ↩

  9. 出处:「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"。 ↩ ↩2

  10. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 824 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:824,搜「checkerboard」)。8×8 跳棋盘二维数组例。 ↩

  11. 出处:「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」)。比较函数返回值决定次序的机制解释。 ↩ ↩2

  12. 出处:「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」。 ↩

  13. 出处:「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 的用户在同段。 ↩ ↩2

  14. 出处:「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」。 ↩ ↩2 ↩3

  15. 出处:「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」。 ↩ ↩2

  16. 出处:「Validating User Input with JavaScript」第 132 段(text/27-fm-validating-user-input-with-javascript.txt:132,搜「built-in validation」)。HTML 属性的能力与边界在同段说明。 ↩

  17. 出处:「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」)。 ↩ ↩2 ↩3

  18. 出处:「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」——一份规则两处复用的工程动机。 ↩

  19. 出处:「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」。 ↩

  20. 出处:「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」)。密码不入消毒、回填值只用于显示。 ↩

  21. 出处:「Validating User Input with JavaScript」第 623 段(text/27-fm-validating-user-input-with-javascript.txt:623,搜「sauntered」)。该表达式的逐件拆解见第 639–671 段;/\W/ 的等价链见第 905–909 段。 ↩ ↩2

  22. 出处:「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」。 ↩ ↩2

  23. 出处:「Validating User Input with JavaScript」第 1018 段(text/27-fm-validating-user-input-with-javascript.txt:1018,搜「global matching」)。/g、/i、/m 三修饰符及叠加示例见第 1037 段。 ↩

  24. 出处:「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」)。 ↩ ↩2

  25. 出处:「Validating User Input with JavaScript」第 691 段(text/27-fm-validating-user-input-with-javascript.txt:691,搜「a-zA-Z0-9_-」)与第 704–728 段(:712,搜「Opening bracket」)。六件拆解;三级浓缩 /\W/ 的推导见第 905–909 段。 ↩ ↩2 ↩3

  26. 出处:「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」。 ↩

  27. 出处:「Validating User Input with JavaScript」第 616 段(text/27-fm-validating-user-input-with-javascript.txt:616,搜「one solution」)与第 641 段(:641,搜「Opening slash」)。五件拆解与「sauntered across the keyboard」评语见第 623 段。 ↩ ↩2 ↩3

  28. 出处:「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」。 ↩ ↩2 ↩3 ↩4

  29. 出处:「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」。 ↩ ↩2

  30. 出处:「Chapter 17. Using Asynchronous Communication」第 118 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:118,搜「text method also returns a promise」)。两次 then 的原因。 ↩ ↩2

  31. 出处:「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」。 ↩

  32. 出处:「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 中转的同源逻辑。 ↩ ↩2 ↩3

  33. 出处:「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」。 ↩ ↩2

  34. 出处:「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 段。 ↩ ↩2 ↩3

  35. 出处:「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」。 ↩ ↩2

  36. 出处:「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」。 ↩

  37. 出处:「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 只是起点,结构才是交付物。 ↩

  38. 出处:「Chapter 17. Using Asynchronous Communication」第 211 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:211,搜「origin part」)。源 = schema + domain + port;四个不同源 URL 的对照见第 240–243 段。 ↩ ↩2

  39. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 29 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:29,搜「general syntax」)。 ↩

  40. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 69 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:69,搜「case-sensitive」)。 ↩

  41. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 77 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:77,搜「implicitly returns undefined」)。 ↩

  42. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 122 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:122,搜「rest parameter syntax」)。 ↩

  43. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 1286 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1286,搜「anonymous function」)。 ↩

  44. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 1330 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1330,搜「arrow function syntax」);等价写法见第 1343 段。 ↩

  45. 出处:「Chapter 16. Validating User Input with JavaScript」第 171 段(text/27-fm-validating-user-input-with-javascript.txt:171,搜「preventDefault」)。 ↩

  46. 出处:「Chapter 16. Validating User Input with JavaScript」第 383 段(text/27-fm-validating-user-input-with-javascript.txt:383,搜「supported by both JavaScript and PHP」)。 ↩

  47. 出处:「Chapter 16. Validating User Input with JavaScript」第 298 段(text/27-fm-validating-user-input-with-javascript.txt:298,搜「test method」)。 ↩

  48. 出处:「Chapter 16. Validating User Input with JavaScript」第 488 段(text/27-fm-validating-user-input-with-javascript.txt:488,搜「escape it」)。 ↩

  49. 出处:「Chapter 16. Validating User Input with JavaScript」第 623 段(text/27-fm-validating-user-input-with-javascript.txt:623,搜「break it apart」)。 ↩

  50. 出处:「Chapter 16. Validating User Input with JavaScript」第 437 段(text/27-fm-validating-user-input-with-javascript.txt:437,搜「defeat your regular expression」)。 ↩

  51. 出处:「Chapter 17. Using Asynchronous Communication」第 7 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:7,搜「mostly abandoned」)。 ↩

  52. 出处:「Chapter 17. Using Asynchronous Communication」第 15 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:15,搜「seamlessly dynamic」)。 ↩

  53. 出处:「Chapter 17. Using Asynchronous Communication」第 111 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:111,搜「Promise」)。 ↩

  54. 出处:「Chapter 17. Using Asynchronous Communication」第 118 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:118,搜「also returns a promise」)。 ↩ ↩2

  55. 出处:「Chapter 17. Using Asynchronous Communication」第 97 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:97,搜「innerText」)。 ↩

  56. 出处:「Chapter 17. Using Asynchronous Communication」第 183 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:183,搜「cross-origin」)。 ↩

  57. 出处:「Chapter 17. Using Asynchronous Communication」第 325 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:325,搜「params」)。 ↩

  58. 出处:「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」)。 ↩

  59. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 430 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:430,搜「automatically called」)。 ↩

  60. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 430 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:430,搜「instance being created」)。 ↩

  61. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 451 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:451,搜「capital letter」)。 ↩

  62. 出处:「Chapter 15. JavaScript Functions, Objects, and Arrays」第 1315 段(text/26-ch15-chapter-15-javascript-functions-objects-and-arra.txt:1315,搜「greater than zero」)。 ↩

  63. 出处:「Chapter 17. Using Asynchronous Communication」第 111 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:111,搜「arrow function passed in the then call」)。 ↩

  64. 出处:「Chapter 16. Validating User Input with JavaScript」第 122 段(text/27-fm-validating-user-input-with-javascript.txt:122,搜「preventDefault」)。 ↩

  65. 出处:「Chapter 17. Using Asynchronous Communication」第 118 段(text/28-ch17-chapter-17-using-asynchronous-communication.txt:118,搜「second then call」)。 ↩

On this page

JS 函数与对象、双端验证与正则、异步通信 — 前端三件套收口1. 顶层全景:三章一条线2. 函数高级件(原书第 15 章前半)2.1 rest 参数:随便几个参数都收得下2.2 方法链:一句话里连续加工2.3 class、this、static:与 PHP 的 OOP 同构2.4 「关联数组」:名不副实的名场面2.5 数组方法全家桶 + 匿名函数 + 箭头函数3. 双端验证:同一张表单的两次把关(原书第 16 章)3.1 为什么必须验两遍3.2 JS 端:submit 事件 + preventDefault3.3b 注册表单的主线走查:一次提交,两条命运3.3 PHP 端:同一套规则再跑一遍 + 出错回填4. 正则表达式:一门两边通用的微型语言4.1 核心构件(照原书的教法接住)4.2 JS 三方法与 PHP 三函数4.1b 把主线正则走一遍:用户名为什么是那串「猫踩键盘」4.1c 正则速查三例(原书 Table 16-2 精选)5. 异步通信:页面不刷新就和服务器说话(原书第 17 章)5.1 fetch 与 Promise5.2 CORS:为什么必须走自己的 PHP 中转5.3 GET、缓存、cachebusting 与 JSON5.4 把第一次异步走到底:urlpost 的完整时序6. 作者的判断与证据7. 边界与局限8. 章末自测:三章的问答题合答9. 可带走的9.1 三章的合龙:一个动作看清全书前端线9.1 函数三件套:声明、return、与 PHP 的三处不同9.2 匿名函数与箭头函数:sort 是最好的教具9.3 校验总设计:submit 事件的闸门模型9.4 正则表达式迷你课(第 16 章版)9.5 粘性表单:错误之后,用户的话还在9.6 异步通信:Ajax 一词的退休与 fetch 的登基9.7 为什么异步请求常常还得过自家 PHP 一道9.8 GET 与 POST 的异步版:参数怎么带9.9 三章合练:一个带校验的异步小应用9.10 练习场(第 15/16/17 章)9.11 FAQ(五问)9.12 类与对象:constructor、this 与命名约定(第 15 章版)9.13 数组方法巡礼:sort 之外的常用四位9.14 回调函数:本书后半的总开关9.15 校验函数 library 化:从一次用到处处用9.16 异步的"等"与"不等":再压一遍心智模型9.17 章末口袋卡(三章合订)9.18 三章的错法分类学:语法错、逻辑错、时序错9.19 与样板书要求的最后一次对表:主线例子完备性核查9.20 三章机制的第 22 章预告:它们将在项目里换成什么名字9.21 全章验收:十三章之后的 JavaScript 能力清单9.22 交接备忘:翻新者最易误判的两处9.23 全章的三处「原书立场」存档9.24 结业一行10. 原文地图Footnotes