带前端新人那会儿,我最常听到的一句话就是:“JS基础我都看完了,但一到写代码就卡住。”其实这不怪大家,基础语法和真实场景之间隔着一层东西,那层东西叫“案例”。它能把零散的知识点串成一条线,让你知道什么场景该用什么写法。这个系列就是干这件事的。这篇《JS基础案例02》聚焦前端日常开发里曝光率最高的几个场景:字符串处理、数组操作、表单联动、异步任务,还有面试绕不开的作用域与闭包。每个案例我都会给完整代码、解释背后的原理,再把我实际踩过的坑一并说出来,适合刚入门前端、正在刷面试题的朋友,也适合写了一阵子代码但感觉基础不牢的同行查漏补缺。
1. 案例一:字符串处理与URL校验
1.1 为什么字符串操作是前端的基本功
但凡做过表单校验、搜索过滤、路由参数解析这类功能,你就绕不开字符串。热搜词里“js判断字符串是否包含”和“js验证url有效性”这两个问题我几乎每次带新人都能碰到,很能说明问题。它们看起来简单,但真要处理边界条件时,能写对的人并不多。
我们先从最常用的几个API说起。
includes(searchString, position):判断一个字符串是否包含另一个字符串,返回布尔值。ES6引入,比indexOf可读性好太多。第二个参数是起始搜索位置,默认0。startsWith(searchString, position):判断字符串是否以某个子串开头。endsWith(searchString, endPosition):判断字符串是否以某个子串结尾。indexOf(searchString):返回子串第一次出现的索引,找不到返回-1。老牌API,现在仍有很多兼容性场景在用。match()和replace():配合正则做模式匹配和替换。
这些API本身没什么难理解的,难点在于“什么时候用哪个”。我的原则是:简单包含判断用includes;需要知道位置用indexOf;需要模式匹配,比如校验手机号、邮箱格式,直接用正则配合test()。
1.2 URL有效性的校验:正则外有更稳的方案
“js验证url有效性”这个问题挺有意思。很多人第一反应就是去写正则,但URL的正则相当难写完美,既要匹配协议、域名,又要管端口、路径、查询参数,边界情况多得吓人。而且网上流传的正则很多都有问题,要么漏掉IPv6地址,要么不支持带特殊字符的路径。
我的建议是,能不用正则就不用。浏览器环境里有一个现成的方案:URL构造函数。
javascript复制function isValidUrl(urlString) {
try {
new URL(urlString);
return true;
} catch (error) {
return false;
}
}
console.log(isValidUrl('https://example.com/path?name=js')); // true
console.log(isValidUrl('ftp://example.com')); // true,URL也支持其他协议
console.log(isValidUrl('not-a-url')); // false
这个方案的原理是:new URL()会按照WHATWG URL标准去解析字符串,如果格式不合法,直接抛TypeError。我们只需要捕获异常就行,完全不用自己造轮子。
但你注意,这个函数只是判断“格式上合法”,不代表这个URL一定能访问。比如https://不存在的域名23333.com,new URL()也能解析成功。所以如果你需要做“可访问性”校验,那就得用fetch去请求了(会有跨域问题,一般需要后端配合)。
另外有个细节:如果只允许http和https协议,可以加一层协议白名单判断。
javascript复制function isWebUrl(urlString) {
try {
const parsed = new URL(urlString);
return ['http:', 'https:'].includes(parsed.protocol);
} catch (error) {
return false;
}
}
提示:
URL.prototype.protocol返回的值是带冒号的,比如'https:',用includes判断时记得带冒号,别写成'http'。
1.3 实操:写一个用户输入的表单URL校验
我结合真实项目需求,把URL校验放进表单场景里。假设有一个用户资料编辑页,需要用户填写个人主页地址,前端提交前要校验格式并标准化。
javascript复制function validateAndNormalizeUrl(input) {
if (!input || typeof input !== 'string') {
return { valid: false, reason: 'URL不能为空' };
}
let trimmedUrl = input.trim();
// 用户经常不写协议头,这里自动补上
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmedUrl)) {
trimmedUrl = 'https://' + trimmedUrl;
}
try {
const parsed = new URL(trimmedUrl);
if (!['http:', 'https:'].includes(parsed.protocol)) {
return { valid: false, reason: '仅支持http和https协议' };
}
if (!parsed.hostname.includes('.') && parsed.hostname !== 'localhost') {
return { valid: false, reason: '域名格式不正确' };
}
return { valid: true, url: parsed.toString() };
} catch (error) {
return { valid: false, reason: 'URL格式不正确' };
}
}
console.log(validateAndNormalizeUrl('example.com/profile'));
// { valid: true, url: 'https://example.com/profile' }
这段代码做了三件事:去掉首尾空格、自动补协议头、用URL对象做解析和标准化。第5行那个正则的作用是判断字符串开头是否已经是协议格式,[a-zA-Z][a-zA-Z0-9+.-]*匹配协议名,:\/\/匹配://。这里注意转义,正则里的/要先写成\/。
为什么还要检查域名里有没有点?因为URL解析器对单段域名(比如https://abc)是放行的。但用户表单里如果填abc,多半是漏写了域名后缀,拦截掉体验更好。当然localhost本身合法,需要单独放行。
1.4 字符串处理的高频坑
处理字符串踩坑最多的是中文字符和转义问题。String.prototype.length统计的是UTF-16码元,不是“字符个数”。一个中文字符在UTF-16里通常占1个码元,但某些生僻字或emoji会占2个码元,length就会比预期多。
javascript复制const emoji = '💻';
console.log(emoji.length); // 2,而不是1
``
