首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

标准库中的新对象和类型-1

标准库中的新对象和类型-1

模块对于日常编程,模块可能是对 ECMAScript 6 最明显的库增强。目前为止,根据 Node.js                约定,我们要求文件使用一个名为 exports                的全局变量对象来描述返回的值。不再需要这样做了!ECMAScript 6 使用 import 和                export 语句来形式化模块的概念。您可能已推断出,export 用于声明来自                ECMAScript 文件的已命名值(通常是类或函数,但有时也包括变量),而 import                用于从该文件将这些已导出的名称抓取到一个不同的文件中。
备注:出于安全原因,developerWorks 沙箱不允许导入文件。如果想试用此代码,需要创建您自己的                    output.js 文件并将其导入您的代码中。
基本地讲,如果您有一个想要作为模块对待的文件(我们称之为 output),只需要 export                您想用在别处使用的 Symbol,就像这样:
// output.js
export function out() {
  console.log("OUT: ", arguments);
}




在函数前输入关键字 export,这会告诉 ECMAScript                需要将此文件作为模块对待。因此,该函数将可供其他任何导入它的文件使用:
import { output } from 'output.js';
out("I'm using output!");




您可能已注意到,import                    语法有一个重要缺陷:为了使用该模块,您需要知道希望导入的所有名称。(但一些人可能认为这是一个优势,因为它可以确保导入者不会在不知情的情况下导入Symbol。)如果想获取从一个模块已导出的所有名称,可以使用通配符                (*) 导入语法,但您需要定义一个模块名称来限定它们的范围:
import * as Output from 'output.js';
Output.out("I'm using output!");




要求提供模块名称可以实现一种范围限定机制;如果两个模块分别在没有模块名称的情况下导出一个 out,新的 out                会静默地替换之前的版本。输入的每个名称包装在模块名称中,以便进一步减少歧义。
SymbolsECMAScript 6 中引入的一个细微特性是新的 Symbol 类型。从表面上看,它似乎很普通:基本地讲,一个                Symbol 实例是一个不能复制到其他任何地方的唯一名称。就这么简单。
回想一下,ECMAScript                对象是一个名称-值对的集合,其中的值可以是数据(字符串、数字、对象引用等)或行为(采用函数引用的形式)。通常,如果您知道对象的名称,就可以获得它的值,这没什么疑问。
但是,在某些情况下,对象的所有者需要确保所选名称没有与其他名称冲突。在这种情况下,无需使用传统的基于 String                的名称,可以使用 Symbol 实例。Symbol 确保名称不会冲突。
例如,我们首先看看典型的 Person 类型:
清单 1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
  constructor(firstName, lastName, age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
};

var p = new Person("Fred", "Flintstone", 45);
console.log(p["firstName"]);

for (let m in p) {
  console.log(m, "=", p[m]);
}




3 个对象字段可供任何知道其名字的人轻松访问。名称可通过简单地迭代对象的内容来获得。尽管 ECMAScript                从未被认为是一种高度安全的语言,但这个示例无疑舍弃了最基本的封装。
使用 Symbol 实现访问控制假设您需要让一些字段保持隐藏。可以首先让它们可通过 Symbol 名称而不是标准字符串进行访问:
清单 2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var firstNameS = Symbol("firstName");
var lastNameS = Symbol("lastName");
var ageS = Symbol("age");
  
class Person {
  constructor(firstName, lastName, age) {
    this[firstNameS] = firstName;
    this[lastNameS] = lastName;
    this[ageS] = age;
  }
};

var p = new Person("Fred", "Flintstone", 45);
console.log(p["firstName"]); // "undefined"

for (let m in p) {
  console.log(m, "=", p[m]); // prints nothing
                             // because the fields are hidden
}
  
p.firstName = "Barney";
console.log(p["firstName"]);
  
console.log(p[firstNameS]); // "Fred"




如上所示,可以使用 Symbol 函数创建 Symbol                的实例。然后,每个实例可在关注的对象上用作名称。如果有人尝试使用正常的基于 String 的名称(例如                firstName)访问该字段,将会获得不明确的结果,因为数据不再位于该名称下。根据新规范,JavaScript                在标准对象迭代期间甚至不会显示基于 Symbol 的名称。任何尝试使用跨该对象的传统反射的行为都将失败。
同样需要注意的是,如果有人想从外部向该对象添加新成员(的一个例子),字符串 firstName                的使用将不会与现有成员冲突或取代现有成员。对于必须向现有对象添加额外行为或成员的库和框架,这一点特别重要                —几乎所有现代框架目前都在使用它。
但是,从清单 5 的最后一行可以看出,如果调用方拥有 Symbol                实例,不要犹豫,可像之前一样使用它访问数据。不同于其他语言中的 private                关键字,Symbol 无法轻松地执行访问控制。
返回列表