在JavaScript中,对于UTF-8字符串,使用encodeURI()和escape()有什么区别?

30

我正在JavaScript中处理utf-8字符串并需要对其进行转义。

在我的浏览器中,escape() / unescape() 和 encodeURI() / decodeURI()均可用。

escape()

> var hello = "안녕하세요"
> var hello_escaped = escape(hello)
> hello_escaped
  "%uC548%uB155%uD558%uC138%uC694"
> var hello_unescaped = unescape(hello_escaped)
> hello_unescaped
  "안녕하세요"

encodeURI()

> var hello = "안녕하세요"    
> var hello_encoded = encodeURI(hello)
> hello_encoded
  "%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94"
> var hello_decoded = decodeURI(hello_encoded)
> hello_decoded
  "안녕하세요"

然而,Mozilla 表示 escape() 被弃用了

虽然 encodeURI() 和 decodeURI() 可以处理上述 utf-8 字符串,但文档(以及函数名称本身)告诉我这些方法是用于 URI 的;我没有在任何地方看到有提到 utf-8 字符串的内容。

简单来说,使用 encodeURI() 和 decodeURI() 处理 utf-8 字符串是否可行?


2
是的,那些都没问题;escape() 无法正确处理 UTF。话虽如此,你可能想要使用 encodeURIComponent(),我看到它比 "仅仅" 使用 encodeURI() 更常见。 - dandavis
3
好的,我将尽力完成任务。以下是需要翻译的内容:https://dev59.com/OXVD5IYBdhLWcg3wI3-L 最佳实践:使用 escape()、encodeURI() 还是 encodeURIComponent()?在 JavaScript 中,有三种对 URL 进行编码的方法:escape()、encodeURI() 和 encodeURIComponent()。针对不同情况,应该使用哪一种方法呢?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Functions/encodeURIComponentencodeURIComponent()encodeURIComponent() 函数将字符串作为 URI 组件进行编码。它主要用于将部分 URI 中可能包含的非法字符进行编码,以便这些字符能够被正确地处理和传输。 - Ian
1
encodeURI() 不使用 UTF-8,而 escape() 使用 UTF-8。 - KingRider
3个回答

29

你好!

当涉及到escapeunescape时,我遵循两个规则:

  1. 尽可能避免使用它们。
  2. 如果无法避免,请使用它们。

尽可能避免使用它们:

正如在问题中提到的那样,escapeunescape已被弃用。一般来说,应避免使用弃用的函数。

所以,如果encodeURIComponentencodeURI可以解决您的问题,您应该使用它们,而不是escape

当您不能轻易避免它们时使用它们:

浏览器将尽可能努力实现向后兼容性。所有主要浏览器都已经实现了escapeunescape;为什么他们会取消实现它们呢?

如果新的规范需要这样做,浏览器必须重新定义escapeunescape。但等等!编写规范的人非常聪明。他们也对不破坏向后兼容性感兴趣!

我知道上述论点很薄弱。但相信我,在浏览器中,已废弃的东西是起作用的。这甚至包括已废弃的HTML标记,例如<xmp><center>

使用escapeunescape

因此,自然而然的下一个问题是,什么时候使用escapeunescape

最近,当我在CloudBrave 上工作时,我必须处理utf8latin1和互相转换。

阅读了一堆博客文章后,我意识到这是多么简单:

var utf8_to_latin1 = function (s) {
    return unescape(encodeURIComponent(s));
};
var latin1_to_utf8 = function (s) {
    return decodeURIComponent(escape(s));
};

不使用 escapeunescape 进行这些互相转换会比较复杂。如果不回避 escapeunescape,生活会变得更加简单。

希望这能有所帮助。


谢谢你。
对我有用。
- undefined

2

使用encodeURI()encodeURIComponent()绝对不可取的 我们来试一下:

console.log(encodeURIComponent('@#*'));

输入:@#*。输出:%40%23*。那么,*字符到底发生了什么?为什么它没有被转换?想象一下:你问一个用户要删除哪个文件,他们的回答是*。在服务器端,你使用encodeURIComponent()进行转换,然后运行rm *。好吧,我有消息告诉你:使用encodeURIComponent()意味着你刚刚删除了所有文件。

当尝试编码完整的URL(即example.com?arg=val)时,请使用fixedEncodeURI(),该函数在MDN encodeURI()文档中定义和进一步解释...

function fixedEncodeURI(str) {
   return encodeURI(str).replace(/%5B/g, '[').replace(/%5D/g, ']');
}
或者,在尝试编码URL的一部分时(即 example.com?arg = val 中的 arg val ),您可能需要使用fixedEncodeURIComponent(),在 MDN encodeURIComponent()文档 中定义并进一步解释了这一点... 请注意保留HTML标签。
function fixedEncodeURIComponent(str) {
 return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
   return '%' + c.charCodeAt(0).toString(16);
 });
}
如果您无法根据上述说明区分它们,我喜欢用以下方式简化说明:
  • fixedEncodeURI()不会+@?=:#;,$& 编码为它们的 http 编码等效项(因为 &+ 是常见的 URL 运算符)
  • fixedEncodeURIComponent() 编码 +@?=:#;,$& 为它们的 http 编码等效项。

你为何要避免使用这个命令的例子非常具体。“在服务器端,您可以使用encodeURIComponent()进行转换,然后运行rm*”。 为什么有人要在服务器上调用该Javascript命令,然后使用此转义字符串执行系统命令? encodeURIComponent的目的是编码URI组件(顾名思义),而不是编码命令行参数。 如果没有更好的例子,您的反对意见只会给与问类似问题的人带来不必要的负担。:-/ - David Gausmann

1

Mozilla 表示 escape() 已经过时。

是的,您应该避免使用 escape()unescape()


简单来说,使用encodeURI()和decodeURI()对于utf-8字符串可以吗?
是的,但根据您的输入形式和所需的输出形式,您可能需要进行一些额外的工作。
从您的问题中,我假设您有一个JavaScript字符串,并且您想将编码转换为UTF-8,并最终以某种转义形式存储该字符串。
首先,重要的是要注意,JavaScript字符串编码是UCS-2,类似于UTF-16,不同于UTF-8。
参见:https://mathiasbynens.be/notes/javascript-encoding encodeURIComponent()非常适合此任务,因为它将UCS-2 JavaScript字符串转换为UTF-8并将其转义为%nn子字符串序列的形式,其中每个nn都是每个字节的两个十六进制数字。
但是,encodeURIComponent()不会转义ASCII范围内的字母、数字和少量其他字符。但这很容易解决。
例如,如果您想将JavaScript字符串转换为表示原始字符串UTF-8编码的数字数组,则可以使用此函数:
//
// Convert JavaScript UCS2 string to array of bytes representing the string UTF8 encoded
//

function StringUTF8AsBytesArrayFromString( s )
{
    var i,
        n,
        u;

    u = [];
    s = encodeURIComponent( s );

    n = s.length;
    for( i = 0; i < n; i++ )
    {
        if( s.charAt( i ) == '%' )
        {
            u.push( parseInt( s.substring( i + 1, i + 3 ), 16 ) );
            i += 2;
        }
        else
        {
            u.push( s.charCodeAt( i ) );
        }
    }

    return u;
}

如果您想将字符串转换为其十六进制表示形式:
//
// Convert JavaScript UCS2 string to hex string representing the bytes of the string UTF8 encoded
//

function StringUTF8AsHexFromString( s )
{
    var u,
        i,
        n,
        s;

    u = StringUTF8AsBytesArrayFromString( s );
    n = u.length;
    s = '';    

    for( i = 0; i < n; i++ )
    {
        s += ( u[ i ] < 16 ? '0' : '' ) + u[ i ].toString( 16 );
    }

    return s;
}

如果你将for循环中的那一行改为:
s += '%' + ( u[ i ] < 16 ? '0' : '' ) + u[ i ].toString( 16 );
(在每个十六进制数字前面添加%符号)
生成的转义字符串(UTF-8编码)可以使用decodeURIComponent()转换回JavaScript UCS-2字符串。

请参阅 String.prototype.codePointAt() 和 String.fromCharCode(),以进行与 utf-8 兼容的单个字符转换。 - here
更正 - String.fromCodePoint(); - here
我想提供一个解决方案,它既适用于传统浏览器(其中String.fromCodePoint不可用),也适用于当前浏览器和未来可能放弃escape/unescape的浏览器。 - Paolo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接