JavaScript 单例内置对象 Global 详解

本文详细介绍了ECMAScript中的Global对象,包括其默认实现(浏览器中的window对象)、URI编码方法(encodeURI和encodeURIComponent的区别),以及eval()方法的强大功能及其潜在风险。还探讨了Global对象的属性和获取Global对象的不同方式,提醒开发者在使用时需谨慎对待eval()的使用场景。
摘要由CSDN通过智能技术生成

单例内置对象 Global

​ ECMA-262 对内置对象的定义是“任何由 ECMAScript 实现提供、与宿主环境无关,并在 ECMAScript 程序开始执行时就存在的对象”。这就意味着,开发者不用显式地实例化内置对象,因为它们已经实例化好了。

​ 当代码开始执行时,全局上下文中会存在两个内置对象:Global 和 Math。其中,Global 对象在大多数 ECMAScript 实现中无法直接访问。不过,浏览器将其实现为 window 对象。所有全局变量和函数都是 Global 对象的属性。

​ Global 对象是 ECMAScript 中最特别的对象,因为代码不会显式地访问它。ECMA-262 规定 Global 对象为一种兜底对象,它所针对的是不属于任何对象的属性和方法。事实上,不存在全局变量或全局函数这种东西。在全局作用域中定义的变量和函数都会变成 Global 对象的属性 。包括函数, 也包括 isNaN()、isFinite()、parseInt() 和 parseFloat(),实际上都是 Global 对象的方法。除了这些,Global 对象上还有另外一些方法。

URL 编码方法

​ encodeURI() 和 encodeURIComponent() 方法用于编码统一资源标识符(URI),以便传给浏览器。 有效的 URI 不能包含某些字符,比如空格。使用 URI 编码方法来编码 URI 可以让浏览器能够理解它们, 同时又以特殊的 UTF-8 编码替换掉所有无效字符。 ecnodeURI() 方法用于对整个 URI 进行编码,比如"www.wrox.com/illegal value.js"。而 encodeURIComponent() 方法用于编码 URI 中单独的组件,比如前面 URL 中的"illegal value.js"。这两个方法的主要区别是,encodeURI() 不会编码属于 URL 组件的特殊字符,比如冒号、斜杠、问号、 井号,而 encodeURIComponent() 会编码它发现的所有非标准字符。来看下面的例子:

let uri = "http://www.wrox.com/illegal value.js#start"; 

// "http://www.wrox.com/illegal%20value.js#start" ·
console.log(encodeURI(uri)); 

// "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start" 
console.log(encodeURIComponent(uri));

​ 这里使用 encodeURI() 编码后,除空格被替换为%20 之外,没有任何变化。而 encodeURIComponent() 方法将所有非字母字符都替换成了相应的编码形式。这就是使用 encodeURI() 编码整个 URI,但只使用 encodeURIComponent() 编码那些会追加到已有 URI 后面的字符串的原因。

注意:一般来说,使用 encodeURIComponent() 应该比使用 encodeURI() 的频率更高, 这是因为编码查询字符串参数比编码基准 URI 的次数更多。

​ 与 encodeURI() 和 encodeURIComponent() 相对的是 decodeURI() 和 decodeURIComponent()。 decodeURI() 只对使用 encodeURI() 编码过的字符解码。例如,%20 会被替换为空格,但%23 不会被替换为井号(#),因为井号不是由 encodeURI() 替换的。类似地,decodeURIComponent() 解码所有被 encodeURIComponent() 编码的字符,基本上就是解码所有特殊值。来看下面的例子:

let uri = "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start"; 

// http%3A%2F%2Fwww.wrox.com%2Fillegal value.js%23start 
console.log(decodeURI(uri)); 

// http:// www.wrox.com/illegal value.js#start 
console.log(decodeURIComponent(uri)); 

​ 这里,uri 变量中包含一个使用 encodeURIComponent() 编码过的字符串。首先输出的是使用 decodeURI()解码的结果,可以看到只用空格替换了%20。然后是使用 decodeURIComponent() 解码的结果,其中替换了所有特殊字符,并输出了没有包含任何转义的字符串。(这个字符串不是有效的 URL。)

注意:URI方法 encodeURI()、encodeURIComponent()、decodeURI() 和 decodeURIComponent() 取代了 escape() 和 unescape() 方法,后者在 ECMA-262 第 3 版中就已经废弃了。URI 方法始终是首选方法,因为它们对所有 Unicode 字符进行编码,而原来的方法只能正确编码 ASCII 字符。不要在生产环境中使用 escape() 和 unescape()。

eval() 方法

​ eval() 方法可能是整个 ECMAScript 语言中最强大的了 。这个方法就是一个完整的 ECMAScript 解释器,它接收一个参数,即一个要执行的 ECMAScript(JavaScript)字符串。来看一个例子:

eval("console.log('hi')"); 

// 上面这行代码的功能与下一行等价:
console.log("hi"); 

​ 当解释器发现 eval() 调用时,会将参数解释为实际的 ECMAScript 语句,然后将其插入到该位置。 通过 eval() 执行的代码属于该调用所在上下文,被执行的代码与该上下文拥有相同的作用域链。这意味着定义在包含上下文中的变量可以在 eval()调用内部被引用,比如下面这个例子:

let msg = "hello world"; 
eval("console.log(msg)"); // "hello world" 

​ 这里,变量 msg 是在 eval() 调用的外部上下文中定义的,而 console.log() 显示了文本"hello world"。这是因为第二行代码会被替换成一行真正的函数调用代码。类似地,可以在 eval() 内部定义一个函数或变量,然后在外部代码中引用,如下所示:

eval("function sayHi() { console.log('hi'); }"); 
sayHi(); 

​ 这里,函数 sayHi() 是在 eval() 内部定义的。因为该调用会被替换为真正的函数定义,所以才可能在下一行代码中调用 sayHi()。对于变量也是一样的:

eval("let msg = 'hello world';"); 
console.log(msg); // Reference Error: msg is not defined 

通过 eval() 定义的任何变量和函数都不会被提升,这是因为在解析代码的时候,它们是被包含在一个字符串中的。它们只是在 eval() 执行的时候才会被创建。 **在严格模式下,在 eval() 内部创建的变量和函数无法被外部访问。**换句话说,最后两个例子会报错。同样,在严格模式下,赋值给 eval 也会导致错误:

"use strict"; 
eval = "hi"; // 导致错误

注意:解释代码字符串的能力是非常强大的,但也非常危险。在使用 eval() 的时候必须极为慎重,特别是在解释用户输入的内容时。因为这个方法会对 XSS 利用暴露出很大的攻击面。恶意用户可能插入会导致你网站或应用崩溃的代码。

Global 对象属性

​ Global 对象有很多属性,像 undefined、NaN 和 Infinity 等特殊值都是 Global 对象的属性。此外,所有原生引用类型构造函数,比如 Object 和 Function,也都是 Global 对象的属性。下表列出了所有这些属性。

属性说明
undefined特殊值undefined
NaN特殊值 NaN
Infinity特殊值 Infinity
ObjectObject 的构造函数
ArrayArray 的构造函数
FunctionFunction 的构造函数
BooleanBoolean 的构造函数
StringString 的构造函数
NumberNumber 的构造函数
DateDate 的构造函数
RegExpRegExp 的构造函数
SymbolSymbol 的伪构造函数
ErrorError 的构造函数
EvalErrorEvalError 的构造函数
RangeErrorRangeError 的构造函数
ReferenceErrorReferenceError 的构造函数
SyntaxErrorSyntaxError 的构造函数
TypeErrorTypeError 的构造函数
URIErrorURIError 的构造函数

window 对象

​ 虽然 ECMA-262 没有规定直接访问 Global 对象的方式,但浏览器将 window 对象实现为 Global 对象的代理。因此,所有全局作用域中声明的变量和函数都变成了 window 的属性。来看下面的例子:

var color = "red"; 

function sayColor() {
    console.log(window.color); 
} 

window.sayColor(); // "red"

​ 这里定义了一个名为 color 的全局变量和一个名为 sayColor() 的全局函数。在sayColor() 内部, 通过 window.color 访问了 color 变量,说明全局变量变成了 window 的属性。接着,又通过 window 对象直接调用了 window.sayColor() 函数,从而输出字符串。

注意:window 对象在 JavaScript 中远不止实现了 ECMAScript 的 Global 对象那么简单。

另一种获取 Global 对象的方式是使用如下的代码:

let global = function() {
 	return this; 
}(); 

​ 这段代码创建一个立即调用的函数表达式,返回了 this 的值。如前所述,当一个函数在没有明确 (通过成为某个对象的方法,或者通过 call()/apply())指定 this 值的情况下执行时,this 值等于 Global 对象。因此,调用一个简单返回 this 的函数是在任何执行上下文中获取 Global 对象的通用方式。

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值