转行学前端的第 50 天 : 了解 ECMAScript Global 对象

1,175 阅读7分钟

我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。

今日学习目标

昨天基于搜索来基础学习 RegExp 断言。今天主要是基于搜索来学习 Global 对象中的编码方法和eval方法,又是适合学习的一天,加油,小又又!!!!


今日学习概要

  • URI 编码方法
  • eval()

URI 编码方法

decodeURI()

基础语法

decodeURI(encodedURI)

参数说明

  • encodedURI

一个完整的编码过的 URI


返回值说明

返回一个给定编码统一资源标识符(URI)的未编码版本的新字符串。

异常

encodedURI 包含无效字符序列时,引发URIError(“格式错误的 URI 序列”)异常。


详细说明

decodeURI()函数能解码由encodeURI 创建或其它流程得到的统一资源标识符(URI)。

将已编码 URI 中所有能识别的转义序列转换成原字符,但不能解码那些不会被 encodeURI 编码的内容(例如 "#")。


案例

解码一个西里尔字母(Cyrillic)URL

decodeURI("https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B");
// "https://developer.mozilla.org/ru/docs/JavaScript_шеллы"

捕捉异常

try {
  var a = decodeURI('%E0%A4%A');
} catch(e) {
  console.error(e);
}

// URIError: malformed URI sequence

decodeURIComponent()

基础语法

decodeURIComponent(encodedURI)

参数说明

  • encodedURI

编码后的部分 URI


返回值说明

一个解码后的统一资源标识符(URI)字符串,处理前的 URI 经过了给定格式的编码。

异常

当该方法使用不当时,将会抛出一个 URIError(“格式错误的 URI 序列”)异常。


详细说明

decodeURIComponent() 方法用于解码由 encodeURIComponent 方法或者其它类似方法编码的部分统一资源标识符(URI)。


案例

解码一个西里尔字母的 URL

decodeURIComponent("JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B");
// "JavaScript_шеллы"

捕捉异常

try {
  var a = decodeURIComponent('%E0%A4%A');
} catch(e) {
  console.error(e);
}

// URIError: malformed URI sequence

encodeURI()

基础语法

encodeURI(URI)

参数说明

  • URI

一个完整的 URI.


返回值说明

一个新字符串, 表示提供的字符串编码为统一资源标识符 (URI)。


基础说明

encodeURI() 函数通过将特定字符的每个实例替换为一个、两个、三或四转义序列来对统一资源标识符 (URI) 进行编码 (该字符的 UTF-8 编码仅为四转义序列)由两个 "代理" 字符组成)。


转译详细说明

假定一个URI是完整的URI,那么无需对那些保留的并且在 URI 中有特殊意思的字符进行编码。

http://username:password@www.example.com:80/path/to/file.php?foo=316&bar=this+has+spaces#anchor

encodeURI 会替换所有的字符,但不包括以下字符,即使它们具有适当的 UTF-8 转义序列:

类型 包含
保留字符 ; , / ? : @ & = + $
非转义的字符 字母 数字 - _ . ! ~ * ' ( )
数字符号 #

请注意,encodeURI 自身无法产生能适用于 HTTP GETPOST 请求的URI,例如对于 XMLHTTPRequests, 因为 "&", "+", 和 "=" 不会被编码,然而在 GETPOST 请求中它们是特殊字符。

然而encodeURIComponent这个方法会对这些字符编码。

另外,如果试图编码一个非高-低位完整的代理字符,将会抛出一个 URIError 错误,例如:

// 编码高-低位完整字符 ok
console.log(encodeURI('\uD800\uDFFF'));

// 编码单独的高位字符抛出 "Uncaught URIError: URI malformed"
console.log(encodeURI('\uD800'));

// 编码单独的低位字符抛出 "Uncaught URIError: URI malformed"
console.log(encodeURI('\uDFFF'));

并且需要注意,如果URL需要遵循较新的RFC3986标准,那么方括号是被保留的(给IPv6),因此对于那些没有被编码的URL部分(例如主机),可以使用下面的代码:

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

encodeURIComponent()

基础语法

encodeURIComponent(str);

参数说明

  • str

String. URI 的组成部分。


返回值说明

原字符串作为URI组成部分被编码后形成的字符串。


详细说明

encodeURIComponent()是对统一资源标识符(URI)的组成部分进行编码的方法。它使用一到四个转义序列来表示字符串中的每个字符的UTF-8编码(只有由两个Unicode代理区字符组成的字符才用四个转义字符编码)。


转译详细说明

encodeURIComponent 转义除了以下字符外的所有字符:

不转义的字符: A-Z a-z 0-9 - _ . ! ~ * ' ( )


转译不同点

encodeURIComponent()encodeURI 有以下几个不同点:

var set1 = ";,/?:@&=+$";  // 保留字符
var set2 = "-_.!~*'()";   // 不转义字符
var set3 = "#";           // 数字标志
var set4 = "ABC abc 123"; // 字母数字字符和空格

console.log(encodeURI(set1)); // ;,/?:@&=+$
console.log(encodeURI(set2)); // -_.!~*'()
console.log(encodeURI(set3)); // #
console.log(encodeURI(set4)); // ABC%20abc%20123 (the space gets encoded as %20)

console.log(encodeURIComponent(set1)); // %3B%2C%2F%3F%3A%40%26%3D%2B%24
console.log(encodeURIComponent(set2)); // -_.!~*'()
console.log(encodeURIComponent(set3)); // %23
console.log(encodeURIComponent(set4)); // ABC%20abc%20123 (the space gets encoded as %20)

注意,如果试图编码一个非高-低位完整的代理字符,将会抛出一个 URIError 错误,例如:

// 高低位完整
alert(encodeURIComponent('\uD800\uDFFF'));

// 只有高位,将抛出"URIError: malformed URI sequence"
alert(encodeURIComponent('\uD800'));

// 只有低位,将抛出"URIError: malformed URI sequence"
alert(encodeURIComponent('\uDFFF')); 

为了避免服务器收到不可预知的请求,对任何用户输入的作为URI部分的内容你都需要用encodeURIComponent进行转义。

比如,一个用户可能会输入"Thyme &time=again"作为comment变量的一部分。

如果不使用encodeURIComponent对此内容进行转义,服务器得到的将是comment=Thyme%20&time=again

请注意,"&"符号和"="符号产生了一个新的键值对,所以服务器得到两个键值对(一个键值对是comment=Thyme,另一个则是time=again),而不是一个键值对。

对于 application/x-www-form-urlencoded (POST) 这种数据方式,空格需要被替换成 '+',所以通常使用 encodeURIComponent 的时候还会把 "%20" 替换为 "+"。

为了更严格的遵循 RFC 3986(它保留 !, ', (, ), 和 *),即使这些字符并没有正式划定 URI 的用途,下面这种方式是比较安全的:

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

案例

下面这个例子提供了 UTF-8Content-DispositionLink 的服务器响应头信息的参数(例如 UTF-8 文件名)

var fileName = 'my file(2).txt';
var header = "Content-Disposition: attachment; filename*=UTF-8''" 
             + encodeRFC5987ValueChars(fileName);

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


function encodeRFC5987ValueChars (str) {
    return encodeURIComponent(str).
        // 注意,仅管 RFC3986 保留 "!",但 RFC5987 并没有
        // 所以我们并不需要过滤它
        replace(/['()]/g, escape). // i.e., %27 %28 %29
        replace(/\*/g, '%2A').
            // 下面的并不是 RFC5987 中 URI 编码必须的
            // 所以对于 |`^ 这3个字符我们可以稍稍提高一点可读性
            replace(/%(?:7C|60|5E)/g, unescape);
}

eval()

基础语法

eval(string)

参数说明

  • string

一个表示 JavaScript 表达式、语句或一系列语句的字符串。表达式可以包含变量与已存在对象的属性。

如果字符串表示的是表达式,eval() 会对表达式进行求值。

如果参数表示一个或多个 JavaScript 语句,那么eval() 就会执行这些语句。


算术表达式

如果你以字符串的形式构造了算术表达式,那么可以在后面用 eval()对它求值。

例如,假设你有一个变量 x,可以通过将表达式的字符串值(例如 3 * x + 2)赋值给一个变量,然后在你的代码后面的其他地方调用 eval(),来推迟涉及 x 的表达式的求值。


参数不是字符串

如果 eval() 的参数不是字符串,eval() 会将参数原封不动地返回。在下面的例子中,String 构造器被指定,而 eval() 返回了 String 对象而不是执行字符串。

eval(new String("2 + 2")); // 返回了包含"2 + 2"的字符串对象
eval("2 + 2");             // returns 4

你可以使用一些通用的方法来绕过这个限制,例如使用 toString()

var expression = new String("2 + 2");
eval(expression.toString());

返回值说明

返回字符串中代码的返回值。如果返回值为空,则返回 undefined


详细说明

eval() 是全局对象的一个函数属性,会将传入的字符串当做 JavaScript 代码进行执行。

不需要用 eval() 来执行一个算术表达式:因为 JavaScript 可以自动为算术表达式求值。


间接的使用 eval()

如果你间接的使用 eval(),比如通过一个引用来调用它,而不是直接的调用 eval

ECMAScript 5 起,它工作在全局作用域下,而不是局部作用域中。

这就意味着,例如,下面的代码的作用声明创建一个全局函数,并且 eval 中的这些代码在执行期间不能在被调用的作用域中访问局部变量。

function test() {
  var x = 2, y = 4;
  console.log(eval('x + y'));  // 直接调用,使用本地作用域,结果是 6
  var geval = eval; // 等价于在全局作用域调用
  console.log(geval('x + y')); // 间接调用,使用全局作用域,throws ReferenceError 因为`x`未定义
  (0, eval)('x + y'); // 另一个间接调用的例子
​}

避免使用eval()

eval() 是一个危险的函数, 它使用与调用者相同的权限执行代码。如果你用 eval()运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在网页/扩展程序的权限下,在用户计算机上运行恶意代码。

更重要的是,第三方代码可以看到某一个 eval() 被调用时的作用域,这也有可能导致一些不同方式的攻击。

相似的 Function 就不容易被攻击。

eval() 通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。


案例

在下面的代码中,两种包含了 eval() 的声明都返回了 42。第一种是对字符串 "x + y + 1" 求值;第二种是对字符串 "42" 求值。

var x = 2;
var y = 39;
var z = "42";
eval("x + y + 1"); // returns 42
eval(z);           // returns 42 

今日学习总结


今日心情

今天主要是基于搜索来学习 Global 对象中的编码方法和eval方法, 希望明天学到更多的内容~~~~

本文使用 mdnice 排版