JavaScript escape的替代方案是什么?

31

我知道escape函数已被弃用,应该使用encodeURI或encodeURIComponent。然而,encodeUri和encodeUriComponent与escape的功能不同。

我想在JavaScript中创建带有瑞典文字符号(åäö)的mailto链接。以下是escape、encodeURIComponent和encodeURI之间的比较:

var subject="åäö";
var body="bodyåäö";

console.log("mailto:?subject="+escape(subject)+"&body=" + escape(body));
console.log("mailto:?subject="+encodeURIComponent(subject)+"&body=" + encodeURIComponent(body));
console.log("mailto:?subject="+encodeURI(subject)+"&body=" + encodeURI(body));  

Output:
mailto:?subject=My%20subject%20with%20%E5%E4%F6&body=My%20body%20with%20more%20characters%20and%20swedish%20%E5%E4%F6
mailto:?subject=My%20subject%20with%20%C3%A5%C3%A4%C3%B6&body=My%20body%20with%20more%20characters%20and%20swedish%20%C3%A5%C3%A4%C3%B6
mailto:?subject=My%20subject%20with%20%C3%A5%C3%A4%C3%B6&body=My%20body%20with%20more%20characters%20and%20swedish%20%C3%A5%C3%A4%C3%B6 

只有使用"escape"创建的mailto链接才能在IE或Chrome中打开格式正确的邮件。当使用encodeURI或encodeURIComponent时,主题会显示:

My subject with åäö

而且页面的样式看起来也很混乱。

除了escape函数,是否还有其他函数可以使用以获取可用的mailto链接?


你使用的是什么编码方式?你尝试过使用 utf-8 吗? - Cyclonecode
我正在使用UTF-8编码。 - gusjap
1
我现在注意到在Firefox中转义不起作用,所以我必须在Firefox情况下使用encodeURIComponent。Firefox中的错误: _ERROR_ILLEGAL_VALUE:组件返回失败代码:0x80070057(NS_ERROR_ILLEGAL_VALUE)[nsIDOMLocation.href] - gusjap
2
我想到的最好解决方案是定义自己的转义函数,而不是使用已弃用的函数。以下是一个实现转义函数的示例: http://cwestblog.com/2011/05/23/escape-unescape-deprecated/ - gusjap
4个回答

11

escape() 是在 B.2.1.2 escape 章节中定义的,而 Annex B 的介绍文本 中提到:

... 这个附录中指定的所有语言特性和行为都有一个或多个不良特征,在没有遗留使用情况的情况下,将从本规范中删除...

对于代码单元值小于等于 0xFF 的字符,escape() 会生成一个两位数的转义序列:%xx。这基本上意味着,escape() 会使用 latin-1 编码,将仅包含来自 U+0000U+00FF 字符的字符串转换为百分号编码字符串。

对于代码单元值大于的字符,将使用四位数格式%uxxxx。这在 mailto:-URI(如 RFC6068 中定义的那样)中的 hfields 部分(存储主题和正文)不允许使用。

mailtoURI    = "mailto:" [ to ] [ hfields ]
to           = addr-spec *("," addr-spec )
hfields      = "?" hfield *( "&" hfield )
hfield       = hfname "=" hfvalue
hfname       = *qchar
hfvalue      = *qchar
...
qchar        = unreserved / pct-encoded / some-delims
some-delims  = "!" / "$" / "'" / "(" / ")" / "*"
               / "+" / "," / ";" / ":" / "@"

unreservedpct-encodedSTD66中有定义:

unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
pct-encoded   = "%" HEXDIG HEXDIG

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

使用一个自己实现的版本,它的行为与escape完全相同,并不能解决任何问题——相反,继续使用escape,因为它不会很快被移除。



总之:如果所有字符都在范围U+0000U+00FF之间,则您以前对escape()的使用会生成拉丁1百分比编码的mailto-URI,否则会生成无效的URI(如果某些应用程序考虑了javascript-encode/decode兼容性,则可能仍然可以正确解释它们)。

使用encodeURIComponent()生成UTF8百分比编码的mailto-URI更加准确(没有创建无效URI的风险),并且具有未来的可靠性(不要使用encodeURI(),它不会转义?/等)。RFC6068在许多地方要求使用UTF-8(但允许在组成的电子邮件消息中使用其他编码)。

例如:

text_latin1="Swedish åäö"
text_other="Emoji "

document.getElementById('escape-latin-1-link').href="mailto:?subject="+escape(text_latin1);
document.getElementById('escape-other-chars-link').href="mailto:?subject="+escape(text_other);
document.getElementById('utf8-link').href="mailto:?subject="+encodeURIComponent(text_latin1);
document.getElementById('utf8-other-chars-link').href="mailto:?subject="+encodeURIComponent(text_other);

function mime_word(text){
  q_encoded = encodeURIComponent(text) //to utf8 percent encoded
  .replace(/[_!'()*]/g, function(c){return '%'+c.charCodeAt(0).toString(16).toUpperCase();})// encode some more chars as utf8
  .replace(/%20/g,'_') // mime Q-encoding is using underscore as space
  .replace(/%/g,'='); //mime Q-encoding uses equal instead of percent
  return encodeURIComponent('=?utf-8?Q?'+q_encoded+'?=');//add mime word stuff and escape for uri
}

//don't use mime_word for body!!!
document.getElementById('mime-word-link').href="mailto:?subject="+mime_word(text_latin1);
document.getElementById('mime-word-other-chars-link').href="mailto:?subject="+mime_word(text_other);
<a id="escape-latin-1-link">escape()-latin1</a><br/>
<a id="escape-other-chars-link">escape()-emoji</a><br/>
<a id="utf8-link">utf8</a><br/>
<a id="utf8-other-chars-link">utf8-emoji</a><br/>
<a id="mime-word-link">mime-word</a><br/>
<a id="mime-word-other-chars-link">mime-word-emoji</a><br/>

对我来说,在Thunderbird中,UTF-8链接和Mime-Word链接都可以正常工作。只有纯文本的UTF-8链接可以在Windows 10内置的Mailapp和最新版本的Outlook中正常工作。


在Windows 10上测试Chrome 74.0.3729.169(64位)时,只有UTF-8链接能够产生正确的Unicode字符。其他链接则会导致出现未确定的字符(�),或者仅保留%十六进制转义码的字符串。 - C Perkins
@CPerkins,您指定了您用于单击链接的浏览器,但据我所知,该行为与浏览器无关。但是,结果取决于打开mailto:链接时解释的邮件客户端。因此,如果您可以指定单击链接时打开的邮件客户端,那将非常好。如果链接在您的浏览器中打开一个新标签页,则表示已注册Webmail服务作为mailto:处理程序。在这种情况下,请指定使用的Webmail服务(gmail.com或outlook.com或roundcube等)。 - T S
我打算提到我正在使用基于浏览器的邮件客户端,因此在这种情况下,浏览器的行为和对URI的初始解释肯定是相关的。为进一步解释,这些mailto URI 是在Firefox 67中生成并单击的,然后打开了Chrome。无论如何,浏览器都不是无关紧要的,因为这都是关于在浏览器中生成正确链接的问题,因此在生成URI到将其传递给mailto客户端之间,我想在那里也存在依赖浏览器的情况。顺便说一句,我确认Thunderbird可以正确解释UTF-8和mime-word URIs。 - C Perkins

6
直接引用 MDN文档 的说法:

该函数主要用于URL查询(紧随问号后面的URL部分),而不是用于转义普通字符串字面量,这些字符串使用格式“\xHH”进行转义(其中HH是两个十六进制数字,“\xHH\xHH”形式用于高位Unicode字符)。

你遇到的问题是由于escape()不支持UTF-8,而encodeURI()encodeURIComponent()支持。
但要绝对清楚永远不要使用encodeURI()encodeURIComponent() 让我们试一试:

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

输入:@#*。输出:%40%23*。通常情况下,一旦用户输入的内容被清理过,我感到可以信任这个经过清理的输入。但是如果我在我的Linux系统上运行rm *以删除由用户指定的文件,那么即使我完全在服务器端进行编码,也会从根本上删除我的系统中的所有文件。 这是encodeURI()encodeURIComponent()中的一个严重错误,MDN Web文档清楚地指出了解决方案。

当尝试对完整URL(即example.com?arg=val)进行编码时,请使用fixedEncodeURI(),该方法的定义及进一步说明已在MDN encodeURI() 文档中定义...

function fixedEncodeURI(str) {
   return encodeURI(str).replace(/%5B/g, '[').replace(/%5D/g, ']');
}

或者,您可能需要使用fixedEncodeURIComponent()来编码 URL 的一部分(即example.com?arg=val中的argval),这在MDN encodeURIComponent() 文档中有定义和进一步解释......

function fixedEncodeURIComponent(str) {
 return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
   return '%' + c.charCodeAt(0).toString(16);
 });
}

如果您对 fixedEncodeURI()fixedEncodeURIComponent()escape() 的区别感到困惑,我通常会用以下方式简化:

  • fixedEncodeURI()不会+@?=:#;,$& 编码为它们的 http 编码等价物(因为 &+ 是常见的 URL 运算符)
  • fixedEncodeURIComponent() +@?=:#;,$& 编码为它们的 http 编码等价物。

encodeURI不是用于在shell中转义字符的函数(escape也不是),它用于将数据编码为适合用作URI参数的格式。你的“fixedEncodeURI”对此也没有改变任何东西。 - T S

0

可能有用:

/**
 * Escape non printable char. eg: "\x08", "\xAAAA", "\r" or "A"
 * @param {number} code 
 * @returns {string}
 */
function escape(code) {
    if (code > 0x7f) {
        return `\\x${code.toString(16).padStart(2, '0')}`
    }
    const char = String.fromCharCode(code)
    const mayEscaped = JSON.stringify(char)
    if (mayEscaped != `"${char}"`) {
        if (mayEscaped.length == 4) {
            // escaped "\r"
            return mayEscaped.slice(1, 3)
        }
        // escaped "\u0000"
        return '\\x' + mayEscaped.slice(code > 0xff ? 3 : 5, 7)
    } else {
        return char
    }
}


-8

在 JavaScript 版本 1.5 中,escape() 函数已被弃用。请改用 encodeURI()encodeURIComponent()

例子

string:            "May/June 2016, Volume 72, Issue 3"
escape:            "May/June%202016%2C%20Volume%2072%2C%20Issue%203"
encodeURI:         "May/June%202016,%20Volume%2072,%20Issue%203"
encodeURIComponent:"May%2FJune%202016%2C%20Volume%2072%2C%20Issue%203"

源代码 https://www.w3schools.com/jsref/jsref_escape.asp


6
这两个函数的作用与转义不同。问题甚至已经说明了这一点。此外,W3Schools并不是一个好的链接资源。 - Ralph King
s = 'a?b/c<d>e"f'g'; console.log(escape(s), encodeURI(s), encodeURIComponent(s)) #=> a%3Fb/c%3Cd%3Ee%22f%27g a?b/c%3Cd%3Ee%22f'g a%3Fb%2Fc%3Cd%3Ee%22f'g,只有 escape 能够正确地防止 XSS 攻击(无法转义引号属性)。 - localhostdotdev

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