5 基本引用类型

这篇博客介绍了JavaScript中的引用类型,如Date和String,强调它们不是传统意义上的类。文章详细讲解了对象实例化、concat()方法用于字符串拼接、slice()、substr()和substring()用于提取子字符串,以及全局对象Global和其window代理。此外,还提到了eval()方法的安全风险以及获取Global对象的技巧。
摘要由CSDN通过智能技术生成

引用值(或者对象)是某个特定引用类型的实例。在 ECMAScript 中,引用类型是把数据和功能组织到一起的结构,经常被人错误地称作“类”。虽然从技术上讲 JavaScript 是一门面向对象语言,但ECMAScript 缺少传统的面向对象编程语言所具备的某些基本结构,包括类和接口。引用类型有时候也被称为对象定义,因为它们描述了自己的对象应有的属性和方法。

注意: 引用类型虽然有点像类,但跟类并不是一个概念。为避免混淆,本文后面不会使用术语“类”

对象被认为是某个特定引用类型的实例。新对象通过使用 new 操作符后跟一个构造函数(constructor)来创建。构造函数就是用来创建新对象的函数,比如下面这行代码:

let now = new Date();

这行代码创建了引用类型 Date 的一个新实例,并将它保存在变量 now 中。Date()在这里就是构造函数,它负责创建一个只有默认属性和方法的简单对象。ECMAScript 提供了很多像 Date 这样的原生引用类型,帮助开发者实现常见的任务。

注意: 函数也是一种引用类型,但有关函数的内容太多了,因此在第十节进行介绍。

3 原始值包装类型

3.3 String
3.3.3 字符串操作方法

本节介绍几个操作字符串值的方法。首先是 concat(),用于将一个或多个字符串拼接成一个新字符串。观察下面的例子:

let stringValue = "hello "; 
let result = stringValue.concat("world"); 

console.log(result); 		// "hello world" 
console.log(stringValue); 	// "hello" 

在这个例子中,对 stringValue 调用 concat() 方法的结果是得到"hello world", 但 stringValue 的值保持不变。concat()方法可以接收任意多个参数,因此可以一次性拼接多个字符串,如下所示:

let stringValue = "hello "; 
let result = stringValue.concat("world", "!"); 

console.log(result); 			// "hello world!" 
console.log(stringValue); 		// "hello" 

这个修改后的例子将字符串"world"和"!"追加到了"hello "后面。虽然 concat() 方法可以拼接字符串,但更常用的方式是使用加号操作符+。而且多数情况下,对于拼接多个字符串来说,使用加号更方便。

ECMAScript 提供了 3 个从字符串中提取子字符串的方法:slice()substr()substring()。这3个方法都返回调用它们的字符串的一个子字符串,而且都接收一或两个参数。第一个参数表示子字符串开始的位置,第二个参数表示子字符串结束的位置。对 slice()substring()而言,第二个参数是提取结束的位置(即该位置之前的字符会被提取出来)。对 substr()而言,第二个参数表示返回的子字符串数量。任何情况下,省略第二个参数都意味着提取到字符串末尾。与 concat()方法一样,slice()、substr()和 substring()也不会修改调用它们的字符串,而只会返回提取到的原始新字符串值。来看下面的例子:

let stringValue = "hello world"; 
console.log(stringValue.slice(3)); 			// "lo world" 
console.log(stringValue.substring(3)); 		// "lo world" 
console.log(stringValue.substr(3)); 		// "lo world" 
console.log(stringValue.slice(3, 7)); 		// "lo w" 
console.log(stringValue.substring(3,7)); 	// "lo w" 
console.log(stringValue.substr(3, 7)); 		// "lo worl"

在这个例子中,slice()、substr()和 substring()是以相同方式被调用的,而且多数情况下返回的值也相同。如果只传一个参数 3,则所有方法都将返回"lo world",因为"hello"中"l"位置为 3。如果传入两个参数 3 和 7,则 slice()和 substring()返回"lo w"(因为"world"中"o"在位置 7,不包含),而 substr()返回"lo worl",因为第二个参数对它而言表示返回的字符数。

当某个参数是负值时,这 3 个方法的行为又有不同。比如,slice()方法将所有负值参数都当成字符串长度加上负参数值。

而 substr()方法将第一个负参数值当成字符串长度加上该值,将第二个负参数值转换为 0。substring()方法会将所有负参数值都转换为 0。看下面的例子:

let stringValue = "hello world"; 
console.log(stringValue.slice(-3)); // "rld" 
console.log(stringValue.substring(-3)); // "hello world" 
console.log(stringValue.substr(-3)); // "rld" 
console.log(stringValue.slice(3, -4)); // "lo w" 
console.log(stringValue.substring(3, -4)); // "hel" 
console.log(stringValue.substr(3, -4)); // "" (empty string) 

这个例子明确演示了 3 个方法的差异。在给 slice()和 substr()传入负参数时,它们的返回结果相同。这是因为-3 会被转换为 8(长度加上负参数),实际上调用的是 slice(8)和 substr(8)。而substring()方法返回整个字符串,因为-3 会转换为 0。

在第二个参数是负值时,这 3 个方法各不相同。slice()方法将第二个参数转换为 7,实际上相当于调用 slice(3, 7),因此返回"lo w"。而 substring()方法会将第二个参数转换为 0,相当于调用substring(3, 0),等价于 substring(0, 3),这是因为这个方法会将较小的参数作为起点,将较大的参数作为终点。对 substr()来说,第二个参数会被转换为 0,意味着返回的字符串包含零个字符,因而会返回一个空字符串。

3.3.9 字符迭代与解构

待补充 @@iterator是什么

字符串的原型上暴露了一个@@iterator 方法,表示可以迭代字符串的每个字符。可以像下面这样手动使用迭代器:

let message = "abc"; 
let stringIterator = message[Symbol.iterator](); 

console.log(stringIterator.next()); // {value: "a", done: false} 
console.log(stringIterator.next()); // {value: "b", done: false} 
console.log(stringIterator.next()); // {value: "c", done: false} 
console.log(stringIterator.next()); // {value: undefined, done: true}

在 for-of 循环中可以通过这个迭代器按序访问每个字符:

for (const c of "abcde") { 
 	console.log(c); 
} 

// a 
// b 
// c 
// d 
// e 

有了这个迭代器之后,字符串就可以通过解构操作符来解构了。比如,可以更方便地把字符串分割为字符数组:

let message = "abcde"; 

console.log([...message]); // ["a", "b", "c", "d", "e"] 

4 单例内置对象

ECMA-262 对内置对象的定义是“任何由 ECMAScript 实现提供、与宿主环境无关,并在 ECMAScript 程序开始执行时就存在的对象”。这就意味着,开发者不用显式地实例化内置对象,因为它们已经实例化好了。前面我们已经接触了大部分内置对象,包括 Object、Array 和 String。本节介绍 ECMA-262 定义的另外两个单例内置对象:Global 和 Math。

4.1 Global

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

待补充 153

4.1.2 The eval() Method

This method works like an entire ECMAScript interpreter and accepts one argument, a string of ECMAScript (or JavaScript) to execute. Here’s an example:

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

This line is functionally equivalent to the following:

console.log("hi"); 

When the interpreter finds an eval() call, it interprets the argument into actual ECMAScript statements and then inserts it into place. Code executed by eval() is considered to be part of the execution context in which the call is made, and the executed code has the same scope chain as that context. This means variables that are defined in the containing context can be referenced inside an eval() call, such as in this example:

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

Here, the variable msg is defined outside the context of the eval() call, yet the call to console.log() still displays the text “hello world” because the second line is replaced with a real line of code. Likewise, you can define a function or variables inside an eval() call that can be referenced by the code outside, as follows:

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

Here, the sayHi() function is defined inside an eval() call. Because that call is replaced with the actual function, it is possible to call sayHi() on the following line. This works the same for variables:

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

Any variables or functions created inside of eval() will not be hoisted, as they are contained within a string when the code is being parsed. They are created only at the time of eval() execution.

In strict mode, variables and functions created inside of eval() are not accessible outside, so these last two examples would cause errors. Also, in strict mode, assigning a value to eval causes an error:

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

NOTE The capability to interpret strings of code is very powerful but also
very dangerous. Use extreme caution with eval(), especially when passing
user-entered data into it, as this method exposes a large attack surface for XSS
exploits. A mischievous user could insert values that might compromise your site
or application security.

待补充 160

4.1.4 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 对象那么简单。关于 window 对象的更多介绍,请参考第 12 章。

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

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

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

4.1.4 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 对象那么简单。关于 window 对象的更多介绍,请参考十二节

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值