我最近在Kyle Simpson的“你不知道JS:ES6”中阅读了以下内容
" [ES6模块导出]实际绑定(几乎像指针)到内部模块定义中的标识符。"
我的困惑是这些绑定与参考文献有何不同......
我知道JS中的引用仅适用于非基本类型(如对象),所以给定
let object1 = {a: 1};
let object2 = object1;
object1
和object2
现在引用(它们都是引用)相同的对象
如果我向object2
添加了一个属性,我还要向object1
object2.b = 2;
console.log(object1.b); // 2
我可以看到绑定可以应用于基本类型和非基本类型
// foo.js
export let count = 1;
export function incrementCount() { count++; }
// bar.js
import {count, incrementCount} from foo;
console.log(count); // 1
incrementCount();
console.log(count); // 2
绑定就像引用一样,除了原始值也可以共享绑定(而引用仅限于非基本类型)?
我觉得我在这里错过了一些东西......
答案 0 :(得分:13)
binding是"名称所指的"的非常通用的术语。范围中的每个标识符都绑定到某个东西。通常它们会解析变量环境中的变量(environment record中的存储槽),但也有例外(例如with
或全局对象)。
reference是指向某种结构的指针。例如,对象被称为" reference values"因为他们用身份引用了可变属性的容器。
ES6模块现在正在引入一种新型绑定,一种以前未知的绑定。它不是通常的变量,而是字面上对另一个变量的引用 - 从另一个模块导出的变量。如果模块变量发生变化,这将由导入反映出来 - 它们都指向相同的环境记录槽
export
声明添加了从本地名称到module interface中的名称的映射,而import
声明添加了从相应模块接口中的名称到本地名称的映射。实例化模块时,indirect binding is created指向与导出模块中的本地绑定相同的环境。
答案 1 :(得分:7)
尽管@ Bergi的答案非常好,但我还是希望为背景知识较少的用户提供更详细的回复。
名称绑定是根据Javascript的词法范围规则将标识符与指定的内存块(变量)相关联。名称绑定是必需的,因为标识符可以存在于不同的范围中,因此可用于不同的变量:
function f() { let x = 0 }
let x = 1;
{ let x = 2 }
{ let x = 3;
{ let x = 4;
{ console.log(x) } // logs 4
}
}
完成名称绑定过程后,console.log(x)
引用周围范围的x
,其范围绑定到值4
。
名称绑定决定标识符在特定范围内引用的变量。
绑定的关联值可以表示
原始类型在Javascript中是不可变的。将原语传递给函数或将其分配给另一个标识符时,实际上是使用值的副本进行操作。原始值的标识由其值给出,即它没有标识。
引用类型在Javascript中是可变的。将引用类型传递给函数或将其分配给另一个标识符时,实际上是使用其引用的副本进行操作,该引用也是一个值。因此,您传递一个引用,而不是引用 - 这种区别是至关重要的:Javascript只有按值调用的评估策略,而不是按引用调用。引用类型具有与其值分隔的标识。因此,它们可以跨名称绑定共享。
const x = "no identity",
y = "no identity";
const o = {foo: "identity"},
p = {foo: "identity"};
// value types don't have identity
console.log(x === y); // true
// reference types have identity
console.log(o === p); // false
let q = o;
// mutations of reference types can be shared
q.bar = "mutation";
console.log(o); // {foo: "identity", bar: "mutation"}
// but rebindings can't be chared
q = {baz: "rebinding"};
console.log(q); // {baz: "rebinding"}
console.log(o); // {foo: "identity", bar: "mutation"}
引用会创建标识并能够共享相应的值。
ES6模块导出一种新类型的名称绑定,以前在JavaScript中是未知的。导入模块A
的导出绑定时,将创建导入名称和引用的绑定。但是,此引用不是指对象,而是指A
的导出绑定。现在我们不仅可以共享引用类型,还可以共享模块中的基元。
答案 2 :(得分:1)
效果并非特定于原始值,它就像访问对象上的属性一样。
例如:
let foo = {a: 1};
let bar = foo;
foo = {b: 2};
console.log(bar); // {a: 1}
可是:
// foo.js
let a = {a: 1};
function mutateA() {
a = {b: 2};
}
export a;
export mutateA;
// bar.js
import {a, mutateA} from foo;
console.log(a); // {a: 1}
mutateA();
console.log(a); // {b: 2}
所以bar a
绑定到foo的a
,并且具有完全相同的值,无论是原语还是引用。