学习链接

URL 对象

【Q429】实现一个函数用来解析 URL 的 querystring

【Q440】实现一个函数用来对 URL 的 querystring 进行编码

解析 URL

URL 对象

  1. new URL(url, [base])
  • **url** —— 完整的 URL,或者仅路径(如果设置了 base),
  • **base** —— 可选的 base URL:如果设置了此参数,且参数 url 只有路径,则会根据这个 base 生成 URL。
  1. let url = new URL('https://test.com/profile/user');
  2. let newUrl1 = new URL('/tester', 'https://test.com/profile/user');
  3. let newUrl2 = new URL('/tester', url);
  4. let newUrl3 = new URL('/tester', 'https://test.com/profile/user'); //
  5. let newUrl4 = new URL('/tester', 'https://test.com/profile/');
  6. let newUrl5 = new URL('tester', 'https://test.com/profile/user');
  7. let newUrl6 = new URL('tester', 'https://test.com/profile/');
  8. let newUrl7 = new URL('//tester', 'https://test.com/profile/');
  9. console.log('newUrl1.href: ', newUrl1.href); // https://test.com/tester
  10. console.log('newUrl2.href: ', newUrl2.href); // https://test.com/tester
  11. console.log('newUrl3.href: ', newUrl3.href); // https://test.com/tester
  12. console.log('newUrl4.href: ', newUrl4.href); // https://test.com/tester
  13. console.log('newUrl5.href: ', newUrl5.href); // https://test.com/profile/tester
  14. console.log('newUrl6.href: ', newUrl6.href); // https://test.com/profile/tester
  15. console.log('newUrl7.href: ', newUrl7.href); // https://tester/

解析URL - 图1

自动编码,转换不合法字符。

  1. (new URL('https://site.com/pat h')).href // 'https://site.com/pat%20h'

编码字符串

  1. https://site.com:8080/path/page?p1=v1&p2=v2#hash

在 URL 中 :?=&# 这类字符是被允许的。

另一方面,对于 URL 的单个组件,例如一个搜索参数,则必须对这些字符进行编码,以免破坏 URL 的格式。

  • encodeURI 仅编码 URL 中完全禁止的字符。
  • encodeURIComponent 也编码这类字符,此外,还编码 #$&+,/:;=?@ 字符。

所以,对于一个 URL 整体,我们可以使用 encodeURI

而对于 URL 参数,我们应该改用 encodeURIComponent

解析 URL 的 queryString

  1. function parse(urlStr) {
  2. const queryString = urlStr.match(/(?<=\?)[^\?#]*/)?.[0];
  3. // const queryString = new URL(urlStr).search.slice(1);
  4. if (!queryString) return {};
  5. queryObj = queryString.split('&').reduce((params, block) => {
  6. // const [k, v = ''] = decodeURIComponent(block).split('=');
  7. // 如此操作无法正确识别 title=1%2B1%3D2 即 1+1=2
  8. // 如果未赋值,则默认为空字符串
  9. const [k, v = ''] = block.split('=').map(e => decodeURIComponent(e));
  10. if (params[k] !== undefined) {
  11. // 处理 key 出现多次的情况,设置为数组
  12. params[k] = [].concat(params[k], v);
  13. } else {
  14. params[k] = v;
  15. }
  16. return params;
  17. }, {})
  18. return queryObj;
  19. }
  20. // {}
  21. const str1 = 'https://shanyue.tech'
  22. // {a: ''}
  23. const str2 = 'https://shanyue.tech?a'
  24. // {name: '名字'}
  25. const str3 = 'https://shanyue.tech?name=%E5%90%8D%E5%AD%97'
  26. // {name: '名字', a: 3}
  27. const str4 = 'https://shanyue.tech?name=%E5%90%8D%E5%AD%97&a=3'
  28. // {name: '名字', a: [3, 4]}
  29. const str5 = 'https://shanyue.tech?name=%E5%90%8D%E5%AD%97&a=3&a=4'
  30. // {name: '名字', a: 3}
  31. const str6 = 'https://shanyue.tech?name=%E5%90%8D%E5%AD%97&a=3#hash'
  32. // {name: '1+1=2'}
  33. const str7 = 'https://shanyue.tech?name=1%2B1%3D2'
  34. console.log('parse(str1): ', parse(str1));
  35. console.log('parse(str2): ', parse(str2));
  36. console.log('parse(str3): ', parse(str3));
  37. console.log('parse(str4): ', parse(str4));
  38. console.log('parse(str5): ', parse(str5));
  39. console.log('parse(str6): ', parse(str6));
  40. console.log('parse(str7): ', parse(str7));

编码 URL 的 queryString

  1. function stringify(queryObj) {
  2. const queryArr = [];
  3. for (let [k, v] of Object.entries(queryObj)) {
  4. if (v === null || v === undefined || typeof v === 'object') {
  5. v = '';
  6. }
  7. queryArr.push(encodeURIComponent(k) + '=' + encodeURIComponent(v));
  8. }
  9. return queryArr.join('&');
  10. }
  11. const data = {
  12. a: 123,
  13. b: '啊?',
  14. c: null
  15. }
  16. const queryString = stringify(data);
  17. console.log('queryString: ', queryString);