Escape、encodeURI、encodeURIComponent 编码函数详解

Escape、encodeURI、encodeURIComponent:JavaScript 编码函数详解

在 Web 开发中,正确处理 URL 和特殊字符至关重要。JavaScript 提供了三个主要函数用于编码:escape()encodeURI()encodeURIComponent()。让我们深入探讨它们的作用、区别以及最佳实践。

核心区别对比表

特性 escape() encodeURI() encodeURIComponent()
是否废弃
保留字符 A-Z a-z 0-9 * @ - _ + . / A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) # A-Z a-z 0-9 - _ . ! ~ * ' ( )
编码结果 %XX%uXXXX %XX %XX
使用场景 不应使用 完整 URL URL 参数值
解码函数 unescape() decodeURI() decodeURIComponent()

详细分析与示例

1. escape() - 已废弃的函数

// 示例
console.log(escape("Hello World!"));   // "Hello%20World%21"
console.log(escape("测试"));          // "%u6D4B%u8BD5" (Unicode 转义)
console.log(escape("email@example.com")); // "email@example.com"
console.log(escape("/path/to/file"));   // "/path/to/file" (斜杠未编码)

特点:

  • 已从 ECMAScript 标准中移除,不应在新项目中使用
  • 对 ASCII 字母数字和 * @ - _ + . / 不编码
  • 空格转为 %20
  • 其他字符转为 %XX%uXXXX 格式
  • 不适用于 URL 编码,因为未正确处理 URI 保留字符

2. encodeURI() - 用于完整 URL

// 示例
const url = "https://example.com/路径/文件.html?query=值#片段";
console.log(encodeURI(url));
// "https://example.com/%E8%B7%AF%E5%BE%84/%E6%96%87%E4%BB%B6.html?query=%E5%80%BC#%E7%89%87%E6%AE%B5"

特点:

  • 设计用于编码整个 URI
  • 保留 URI 完整结构(协议、主机、路径、查询参数分隔符等)
  • 不编码A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) #
  • 编码非 ASCII 字符(如中文)和空格
  • 不会破坏 URL 结构

3. encodeURIComponent() - 用于 URL 组件

// 示例
const paramValue = "name=John&Doe&age=30";
console.log(encodeURIComponent(paramValue));
// "name%3DJohn%26Doe%26age%3D30"

const fullURL = `https://example.com/search?q=${encodeURIComponent("咖啡 & 茶")}`;
console.log(fullURL);
// "https://example.com/search?q=%E5%92%96%E5%95%A1%20%26%20%E8%8C%B6"

特点:

  • 设计用于编码 URI 组件(查询参数、路径片段等)
  • 编码所有非标准字符,包括 ?, =, &, # 等保留字符
  • 不编码A-Z a-z 0-9 - _ . ! ~ * ' ( )
  • 确保组件值不会破坏 URL 结构
  • 适用于 Cookie 值、表单数据等需要严格编码的场景

何时使用哪个函数?

✅ 推荐使用场景

// 场景1:构建完整URL(使用 encodeURI)
const baseURL = "https://example.com/用户资料/我的文档.html";
const safeURL = encodeURI(baseURL);
console.log(safeURL); 
// "https://example.com/%E7%94%A8%E6%88%B7%E8%B5%84%E6%96%99/%E6%88%91%E7%9A%84%E6%96%87%E6%A1%A3.html"

// 场景2:编码查询参数值(使用 encodeURIComponent)
const query = "search&sort=desc";
const apiUrl = `https://api.example.com/data?q=${encodeURIComponent(query)}`;
console.log(apiUrl);
// "https://api.example.com/data?q=search%26sort%3Ddesc"

// 场景3:编码特殊值
const cookieValue = "user=123; session=abc";
document.cookie = `data=${encodeURIComponent(cookieValue)}`;

❌ 避免使用的场景

// 错误1:使用 escape() 编码URL
const badURL = escape("https://example.com/测试?q=a&b");
console.log(badURL); 
// "https%3A//example.com/%u6D4B%u8BD5%3Fq%3Da%26b" - 破坏了协议部分

// 错误2:使用 encodeURI() 编码查询参数
const unsafeParam = encodeURI("a=1&b=2");
const badAPI = `https://api.example.com?params=${unsafeParam}`;
console.log(badAPI);
// "https://api.example.com?params=a=1&b=2" - & 字符未被编码,会解析为两个参数

// 错误3:混合使用
const mixed = encodeURI("path/") + encodeURIComponent("file?name=test");
console.log(mixed);
// "path/file%3Fname%3Dtest" - 路径结构被破坏

关键结论

  1. escape() 已废弃 - 不应在新代码中使用,仅存在于旧浏览器中用于向后兼容

  2. encodeURI() 用于完整 URL

    • 保留 URL 结构(:, /, ?, &, = 等)
    • 编码路径中的特殊字符和空格
    • 不编码查询参数中的保留字符
  3. encodeURIComponent() 用于 URL 组件

    • 编码所有特殊字符,包括保留字符
    • 适用于查询参数值、路径片段等
    • 确保组件值不会破坏 URL 结构
  4. 最佳实践

    // 构建安全URL的推荐方法
    function buildURL(base, path, queryParams) {
      const encodedPath = encodeURI(path);
      const queryString = Object.entries(queryParams)
        .map(([key, value]) => 
          `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
        )
        .join('&');
      
      return `${base}/${encodedPath}?${queryString}`;
    }
    
    // 示例用法
    const url = buildURL('https://api.example.com', 'user/profile', {
      id: '123#test',
      name: 'John & Jane'
    });
    
    console.log(url);
    // "https://api.example.com/user/profile?id=123%23test&name=John%20%26%20Jane"
    

记住这些函数的区别和正确用法,可以避免 URL 解析错误、安全问题(如注入攻击)和跨浏览器兼容性问题。在现代 Web 开发中,始终优先使用 encodeURI()encodeURIComponent(),并完全避免使用 escape()

可视化对比工具

https://www.quanxiaoha.com/tools/url-decode

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>URL编码函数对比工具</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            color: #333;
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
            overflow: hidden;
            padding: 30px;
        }
        
        header {
            text-align: center;
            margin-bottom: 30px;
            padding-bottom: 20px;
            border-bottom: 1px solid #eee;
        }
        
        h1 {
            font-size: 2.5rem;
            color: #1a2a6c;
            margin-bottom: 10px;
        }
        
        .subtitle {
            color: #555;
            font-size: 1.2rem;
            max-width: 800px;
            margin: 0 auto;
            line-height: 1.6;
        }
        
        .input-section {
            background: #f8f9fa;
            border-radius: 10px;
            padding: 25px;
            margin-bottom: 30px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
        }
        
        .input-group {
            display: flex;
            gap: 15px;
            margin-bottom: 20px;
        }
        
        input {
            flex: 1;
            padding: 15px;
            border: 2px solid #ddd;
            border-radius: 8px;
            font-size: 1.1rem;
            transition: all 0.3s;
        }
        
        input:focus {
            outline: none;
            border-color: #4a6fcb;
            box-shadow: 0 0 0 3px rgba(74, 111, 203, 0.2);
        }
        
        button {
            background: #4a6fcb;
            color: white;
            border: none;
            border-radius: 8px;
            padding: 0 25px;
            font-size: 1.1rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
        }
        
        button:hover {
            background: #3a5bb0;
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
        }
        
        .presets {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }
        
        .preset-btn {
            background: #e9ecef;
            border: none;
            border-radius: 20px;
            padding: 8px 15px;
            font-size: 0.9rem;
            cursor: pointer;
            transition: all 0.2s;
        }
        
        .preset-btn:hover {
            background: #dde2e6;
            transform: translateY(-1px);
        }
        
        .results-container {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
            gap: 25px;
            margin-bottom: 30px;
        }
        
        .result-card {
            background: white;
            border-radius: 12px;
            overflow: hidden;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
            transition: transform 0.3s;
            border: 1px solid #eee;
        }
        
        .result-card:hover {
            transform: translateY(-5px);
        }
        
        .card-header {
            padding: 20px;
            color: white;
            display: flex;
            align-items: center;
            gap: 12px;
        }
        
        .card-header i {
            font-size: 1.8rem;
        }
        
        .escape .card-header { background: #dc3545; }
        .uri .card-header { background: #0d6efd; }
        .component .card-header { background: #198754; }
        
        .card-body {
            padding: 25px;
            min-height: 250px;
            display: flex;
            flex-direction: column;
        }
        
        .result-content {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 15px;
            flex-grow: 1;
            font-family: 'Courier New', monospace;
            overflow-x: auto;
            white-space: pre-wrap;
            word-break: break-all;
            margin-bottom: 20px;
        }
        
        .info-box {
            background: #e9f7ef;
            border-left: 4px solid #198754;
            padding: 12px 15px;
            border-radius: 4px;
            font-size: 0.95rem;
            margin-top: auto;
        }
        
        .warning-box {
            background: #fce8e6;
            border-left: 4px solid #dc3545;
            padding: 12px 15px;
            border-radius: 4px;
            font-size: 0.95rem;
        }
        
        .comparison-section {
            background: #f8f9fa;
            border-radius: 10px;
            padding: 25px;
            margin-top: 20px;
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 15px;
        }
        
        th, td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid #dee2e6;
        }
        
        th {
            background-color: #e9ecef;
            font-weight: 600;
        }
        
        tr:hover {
            background-color: rgba(0, 0, 0, 0.02);
        }
        
        .check {
            color: #198754;
            font-weight: bold;
        }
        
        .cross {
            color: #dc3545;
            font-weight: bold;
        }
        
        footer {
            text-align: center;
            margin-top: 30px;
            padding-top: 20px;
            color: #6c757d;
            font-size: 0.9rem;
            border-top: 1px solid #eee;
        }
        
        @media (max-width: 768px) {
            .input-group {
                flex-direction: column;
            }
            
            button {
                padding: 15px;
            }
            
            .results-container {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1><i class="fas fa-link"></i> URL编码函数对比工具</h1>
            <p class="subtitle">直观比较JavaScript中escape(), encodeURI()和encodeURIComponent()三种编码函数的行为和区别</p>
        </header>
        
        <section class="input-section">
            <div class="input-group">
                <input type="text" id="inputText" value="https://example.com/路径?q=搜索&sort=desc#section" placeholder="输入要编码的文本...">
                <button id="encodeBtn"><i class="fas fa-code"></i> 编码文本</button>
            </div>
            
            <h3>预设示例:</h3>
            <div class="presets">
                <button class="preset-btn" data-value="Hello World!">Hello World!</button>
                <button class="preset-btn" data-value="name=John Doe&age=30">查询参数</button>
                <button class="preset-btn" data-value="中文/测试">中文字符</button>
                <button class="preset-btn" data-value="email@example.com">邮箱地址</button>
                <button class="preset-btn" data-value="符号: !@#$%^&*()">特殊符号</button>
            </div>
        </section>
        
        <div class="results-container">
            <div class="result-card escape">
                <div class="card-header">
                    <i class="fas fa-exclamation-triangle"></i>
                    <h2>escape()</h2>
                </div>
                <div class="card-body">
                    <h3>编码结果:</h3>
                    <div class="result-content" id="escapeResult">等待输入...</div>
                    <div class="warning-box">
                        <i class="fas fa-exclamation-circle"></i> 警告:此函数已被弃用,不应用于新项目。它不会对 "+" 字符进行编码,可能会导致问题。
                    </div>
                </div>
            </div>
            
            <div class="result-card uri">
                <div class="card-header">
                    <i class="fas fa-globe"></i>
                    <h2>encodeURI()</h2>
                </div>
                <div class="card-body">
                    <h3>编码结果:</h3>
                    <div class="result-content" id="uriResult">等待输入...</div>
                    <div class="info-box">
                        <i class="fas fa-info-circle"></i> 适用于编码完整URL,保留URL结构(如 :, /, ?, &, = 等字符)
                    </div>
                </div>
            </div>
            
            <div class="result-card component">
                <div class="card-header">
                    <i class="fas fa-cog"></i>
                    <h2>encodeURIComponent()</h2>
                </div>
                <div class="card-body">
                    <h3>编码结果:</h3>
                    <div class="result-content" id="componentResult">等待输入...</div>
                    <div class="info-box">
                        <i class="fas fa-info-circle"></i> 适用于编码URL组件(查询参数值等),编码所有特殊字符
                    </div>
                </div>
            </div>
        </div>
        
        <section class="comparison-section">
            <h2><i class="fas fa-balance-scale"></i> 函数对比</h2>
            <table>
                <thead>
                    <tr>
                        <th>特性</th>
                        <th>escape()</th>
                        <th>encodeURI()</th>
                        <th>encodeURIComponent()</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>是否标准</td>
                        <td class="cross">已废弃</td>
                        <td class="check">标准</td>
                        <td class="check">标准</td>
                    </tr>
                    <tr>
                        <td>是否编码URL保留字符 (:/?#[]@!$&'()*+,;=)</td>
                        <td class="cross">部分编码</td>
                        <td class="cross">不编码</td>
                        <td class="check">全部编码</td>
                    </tr>
                    <tr>
                        <td>是否编码非保留字符 (字母数字和 -_.~)</td>
                        <td class="cross">部分编码</td>
                        <td class="cross">不编码</td>
                        <td class="cross">不编码</td>
                    </tr>
                    <tr>
                        <td>是否编码Unicode字符</td>
                        <td class="check">%uXXXX格式</td>
                        <td class="check">UTF-8编码</td>
                        <td class="check">UTF-8编码</td>
                    </tr>
                    <tr>
                        <td>空格编码</td>
                        <td>%20</td>
                        <td>%20</td>
                        <td>%20</td>
                    </tr>
                    <tr>
                        <td>最佳使用场景</td>
                        <td>无 - 不应使用</td>
                        <td>完整URL编码</td>
                        <td>URL参数值编码</td>
                    </tr>
                </tbody>
            </table>
        </section>
        
        <footer>
            <p>© 2023 URL编码函数对比工具 | 在Web开发中请优先使用encodeURI()和encodeURIComponent()</p>
        </footer>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const inputText = document.getElementById('inputText');
            const encodeBtn = document.getElementById('encodeBtn');
            const escapeResult = document.getElementById('escapeResult');
            const uriResult = document.getElementById('uriResult');
            const componentResult = document.getElementById('componentResult');
            const presetBtns = document.querySelectorAll('.preset-btn');
            
            // 初始编码示例文本
            encodeText();
            
            // 编码按钮事件
            encodeBtn.addEventListener('click', encodeText);
            
            // 预设按钮事件
            presetBtns.forEach(btn => {
                btn.addEventListener('click', function() {
                    inputText.value = this.getAttribute('data-value');
                    encodeText();
                });
            });
            
            // 编码函数
            function encodeText() {
                const text = inputText.value;
                
                if (!text.trim()) {
                    escapeResult.textContent = '请输入文本';
                    uriResult.textContent = '请输入文本';
                    componentResult.textContent = '请输入文本';
                    return;
                }
                
                // 使用不同的编码函数
                escapeResult.textContent = escape(text);
                uriResult.textContent = encodeURI(text);
                componentResult.textContent = encodeURIComponent(text);
            }
            
            // 按Enter键也可以触发编码
            inputText.addEventListener('keyup', function(e) {
                if (e.key === 'Enter') {
                    encodeText();
                }
            });
        });
    </script>
</body>
</html>
posted @ 2025-06-27 15:49  张浩伟  阅读(305)  评论(0)    收藏  举报