关于javascript:你什么时候应该使用escape而不是encodeURI / encodeURIComponent?

When are you supposed to use escape instead of encodeURI / encodeURIComponent?

当对发送到Web服务器的查询字符串进行编码时-您何时使用escape(),何时使用encodeURI()encodeURIComponent()

使用逃逸:

1
escape("% +&=");

使用encodeuri()/encodeuricomponent())

1
2
3
encodeURI("http://www.google.com?var1=value1&var2=value2");

encodeURIComponent("var1=value1&var2=value2");


逃逸()

不要使用它!escape()的定义见第B.2.1.2节"转义",附录B的引言文本称:

... All of the language features and behaviours specified in this annex have one or more undesirable characteristics and in the absence of legacy usage would be removed from this specification. ...
... Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code....

Behaviour:

https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/escape

特殊字符编码,但:@*.-.除外。/

字符的十六进制形式,其代码单位值为0xff或更小,是一个两位数的转义序列:%xx

对于代码单位较大的字符,使用四位格式%uxxxx。在查询字符串(如RFC3986中定义)中不允许这样做:

1
2
3
4
5
6
query       = *( pchar /"/" /"?" )
pchar         = unreserved / pct-encoded / sub-delims /":" /"@"
unreserved    = ALPHA / DIGIT /"-" /"." /"_" /"~"
pct-encoded   ="%" HEXDIG HEXDIG
sub-delims    ="!" /"$" /"&" /"'" /"(" /")"
              /"*" /"+" /"," /";" /"="

只有在百分号后面直接跟两个十六进制数字时才允许使用百分号,不允许使用百分号后面跟u

EnCODURURE()

当需要工作的URL时,请使用encodeuri。打这个电话:

1
encodeURI("http://www.example.org/a file with spaces.html")

得到:

1
http://www.example.org/a%20file%20with%20spaces.html

不要调用encodeuricomponent,因为它会破坏URL并返回

1
http%3A%2F%2Fwww.example.org%2Fa%20file%20with%20spaces.html

编码成分()

要对URL参数的值进行编码时,请使用encodeuricomponent。

4

然后您可以创建所需的URL:

1
var url ="http://example.net/?param1=" + p1 +"&param2=99";

你会得到这个完整的网址:

http://example.net/?param1=http%3A%2F%2Fexample.org%2F%Ffa%3D12%26b%3D55¶m2=99

注意,编码成分不能逃逸'字符。一个常见的错误是使用它来创建HTML属性,如href='MyUrl',这可能会导致注入错误。如果要从字符串构造HTML,请使用"而不是'作为属性引号,或者添加一个额外的编码层('可以编码为%27)。

有关此类编码的详细信息,请查看:http://en.wikipedia.org/wiki/percent-encoding


encodeURI()encodeURIComponent()的区别是由encodeuricomponent而不是encodeuri编码的11个字符:

Table with the ten differences between encodeURI and encodeURIComponent

我用Google Chrome中的console.table轻松生成了这个表,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
var arr = [];
for(var i=0;i<256;i++) {
  var char=String.fromCharCode(i);
  if(encodeURI(char)!==encodeURIComponent(char)) {
    arr.push({
      character:char,
      encodeURI:encodeURI(char),
      encodeURIComponent:encodeURIComponent(char)
    });
  }
}
console.table(arr);


我发现这篇文章很有启发性:javascript疯狂:查询字符串解析

当我试图理解为什么解码组件没有正确解码"+"时,我发现了它。以下是摘录:

1
2
3
4
5
6
7
8
9
10
11
String:                        "A + B"
Expected Query String Encoding:"A+%2B+B"
escape("A + B") =              "A%20+%20B"     Wrong!
encodeURI("A + B") =           "A%20+%20B"     Wrong!
encodeURIComponent("A + B") =  "A%20%2B%20B"   Acceptable, but strange

Encoded String:                "A+%2B+B"
Expected Decoding:             "A + B"
unescape("A+%2B+B") =          "A+++B"       Wrong!
decodeURI("A+%2B+B") =         "A+++B"       Wrong!
decodeURIComponent("A+%2B+B") ="A+++B"       Wrong!


encodeuricomponent不编码-_.!~*'(),导致在XML字符串中向php发布数据时出现问题。

例如:

encodeURI的一般转义
%3Cxml%3E%3Ctext%20x=%22100%22%20y=%22150%22%20value=%22It's%20a%20value%20with%20single%20quote%22%20/%3E%20%3C/xml%3E

可以看到,单引号没有编码。为了解决问题,我创建了两个函数来解决项目中的问题,用于编码URL:

1
2
3
function encodeData(s:String):String{
    return encodeURIComponent(s).replace(/\-/g,"%2D").replace(/\_/g,"%5F").replace(/\./g,"%2E").replace(/\!/g,"%21").replace(/\~/g,"%7E").replace(/\*/g,"%2A").replace(/\'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29");
}

对于解码URL:

1
2
3
4
5
6
7
function decodeData(s:String):String{
    try{
        return decodeURIComponent(s.replace(/\%2D/g,"-").replace(/\%5F/g,"_").replace(/\%2E/g,".").replace(/\%21/g,"!").replace(/\%7E/g,"~").replace(/\%2A/g,"*").replace(/\%27/g,"'").replace(/\%28/g,"(").replace(/\%29/g,")"));
    }catch (e:Error) {
    }
    return"";
}


encodeuri()-escape()函数用于JavaScript转义,而不是HTTP。


小型比较表JAVA与JavaScript与PHP。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1. Java URLEncoder.encode (using UTF8 charset)
2. JavaScript encodeURIComponent
3. JavaScript escape
4. PHP urlencode
5. PHP rawurlencode

char   JAVA JavaScript --PHP---
[ ]     +    %20  %20  +    %20
[!]     %21  !    %21  %21  %21
[*]     *    *    *    %2A  %2A
[']     %27  '    %27  %27  %27
[(]     %28  (    %28  %28  %28
[)]     %29  )    %29  %29  %29
[;]     %3B  %3B  %3B  %3B  %3B
[:]     %3A  %3A  %3A  %3A  %3A
[@]     %40  %40  @    %40  %40
[&]     %26  %26  %26  %26  %26
[=]     %3D  %3D  %3D  %3D  %3D
[+]     %2B  %2B  +    %2B  %2B
[$]     %24  %24  %24  %24  %24
[,]     %2C  %2C  %2C  %2C  %2C
[/]     %2F  %2F  /    %2F  %2F
[?]     %3F  %3F  %3F  %3F  %3F
[#]     %23  %23  %23  %23  %23
[[]     %5B  %5B  %5B  %5B  %5B
[]]     %5D  %5D  %5D  %5D  %5D
----------------------------------------
[~]     %7E  ~    %7E  %7E  ~
[-]     -    -    -    -    -
[_]     _    _    _    _    _
[%]     %25  %25  %25  %25  %25
[\]     %5C  %5C  %5C  %5C  %5C
----------------------------------------
char  -JAVA-  --JavaScript--  -----PHP------
[?]   %C3%A4  %C3%A4  %E4     %C3%A4  %C3%A4
[ф]   %D1%84  %D1%84  %u0444  %D1%84  %D1%84


我建议不要使用这些方法中的一种。写出你自己的函数,它能做正确的事情。

MDN给出了一个很好的URL编码示例,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var fileName = 'my file(2).txt';
var header ="Content-Disposition: attachment; filename*=UTF-8''" + encodeRFC5987ValueChars(fileName);

console.log(header);
// logs"Content-Disposition: attachment; filename*=UTF-8''my%20file%282%29.txt"


function encodeRFC5987ValueChars (str) {
    return encodeURIComponent(str).
        // Note that although RFC3986 reserves"!", RFC5987 does not,
        // so we do not need to escape it
        replace(/['()]/g, escape). // i.e., %27 %28 %29
        replace(/\*/g, '%2A').
            // The following are not required for percent-encoding per RFC5987,
            //  so we can allow for a little better readability over the wire: |`^
            replace(/%(?:7C|60|5E)/g, unescape);
}

https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/encodeuricomponent


还要记住,它们都对不同的字符集进行编码,并适当地选择所需的字符集。encodeUri()编码的字符数少于encodeUriComponent(),后者比escape()编码的字符数更少(也不同于dannyp的点)。


为了编码,javascript提供了三个内置函数-

  • escape()-不编码@*/+。此方法在ECMA 3之后被弃用,因此应避免使用。

  • encodeuri()-不编码~!@#$&*()=:/,;?+'。它假定URI是一个完整的URI,因此不会对URI中具有特殊含义的保留字符进行编码。此方法用于转换完整的URL,而不是某些特殊的URL段。示例-encodeURI('http://stackoverflow.com');将给出-http://stackoverflow.com

  • encodeuricomponent()-不编码- _ . ! ~ * ' ( )。此函数通过用表示字符的UTF-8编码的一个、两个、三个或四个转义序列替换特定字符的每个实例来对统一资源标识符(URI)组件进行编码。此方法应用于转换URL的组件。例如,需要附加一些用户输入示例-encodeURI('http://stackoverflow.com');将给出-http%3a%2f%2fstackoverflow.com

  • 所有这些编码都是以UTF 8执行的,即字符将以UTF-8格式转换。

    encodeuricomponent与encodeuri的不同之处在于,它对encodeuri的保留字符和数字符号进行编码。


    我发现,即使在掌握了各种方法的各种用途和功能之后,使用各种方法进行试验也是一种良好的健全性检查。

    为此,我发现这个网站非常有用,以证实我的怀疑,我正在做适当的事情。它也被证明有助于解码一个编码组成的字符串,这可能是相当困难的解释。一个很好的书签:

    http://www.the-art-of-web.com/javascript/escape/


    受约翰桌子的启发,我决定扩大桌子。我想看看哪些ASCII字符被编码。

    screenshot of console.table

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var ascii =" !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";

    var encoded = [];

    ascii.split("").forEach(function (char) {
        var obj = { char };
        if (char != encodeURI(char))
            obj.encodeURI = encodeURI(char);
        if (char != encodeURIComponent(char))
            obj.encodeURIComponent = encodeURIComponent(char);
        if (obj.encodeURI || obj.encodeURIComponent)
            encoded.push(obj);
    });

    console.table(encoded);

    表仅显示编码字符。空单元格表示原始字符和编码字符相同。

    另外,我要为urlencode()rawurlencode()添加另一个表。唯一的区别似乎是空间字符的编码。

    screenshot of console.table

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?php
    $ascii = str_split(" !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", 1);
    $encoded = [];
    foreach ($ascii as $char) {
        $obj = ["char" => $char];
        if ($char != urlencode($char))
            $obj["urlencode"] = urlencode($char);
        if ($char != rawurlencode($char))
            $obj["rawurlencode"] = rawurlencode($char);
        if (isset($obj["rawurlencode"]) || isset($obj["rawurlencode"]))
            $encoded[] = $obj;
    }
    echo"var encoded =" . json_encode($encoded) .";";
    ?>
    console.table(encoded);


    现代改写@johann echavaria的答案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    console.log(
        Array(256)
            .fill()
            .map((ignore, i) => String.fromCharCode(i))
            .filter(
                (char) =>
                    encodeURI(char) !== encodeURIComponent(char)
                        ? {
                              character: char,
                              encodeURI: encodeURI(char),
                              encodeURIComponent: encodeURIComponent(char)
                          }
                        : false
            )
    )

    或者,如果可以使用表,则将console.log替换为console.table(用于更漂亮的输出)。


    公认的答案是好的。在最后一部分进行扩展:

    Note that encodeURIComponent does not escape the ' character. A common
    bug is to use it to create html attributes such as href='MyUrl', which
    could suffer an injection bug. If you are constructing html from
    strings, either use" instead of ' for attribute quotes, or add an
    extra layer of encoding (' can be encoded as %27).

    如果您希望在安全方面,也应该对未保留字符的百分比编码进行编码。

    您可以使用此方法来转义它们(源代码mozilla)

    1
    2
    3
    4
    5
    6
    7
    function fixedEncodeURIComponent(str) {
      return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
        return '%' + c.charCodeAt(0).toString(16);
      });
    }

    // fixedEncodeURIComponent("'") -->"%27"

    我有这个功能…

    1
    2
    3
    4
    5
    6
    7
    var escapeURIparam = function(url) {
        if (encodeURIComponent) url = encodeURIComponent(url);
        else if (encodeURI) url = encodeURI(url);
        else url = escape(url);
        url = url.replace(/\+/g, '%2B'); // Force the replacement of"+"
        return url;
    };