15

我最近在 Kyle Simpson 的 You Don't Know 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

绑定就像引用一样,除了原始值也可以共享绑定(而引用仅限于非原始类型)?

我觉得我在这里错过了一些东西......

4

3 回答 3

15

绑定是“名称所指的内容”的一个非常通用的术语。范围内的每个标识符都绑定到某些东西。通常它们解析为变量环境中的变量(环境记录中的存储槽),但也有例外(例如with或全局对象)。

引用是指向某种结构的指针的术语。例如,对象被称为“引用值”,因为它们引用具有标识的可变属性容器。

ES6 模块现在引入了一种新的绑定类型,这是以前未知的。它不是一个普通的变量,而是对另一个变量的引用——从另一个模块导出的那个。如果模块变量发生变化,这将通过导入反映出来——它们都指向同一个环境记录槽。声明添加了从本地名称到模块接口
名称 的映射,而声明添加了从相应模块接口中的名称到本地名称的映射。实例化模块时,会创建一个间接绑定,该绑定指向与导出模块中的本地绑定相同的环境。exportimport

于 2016-08-31T22:26:56.637 回答
9

尽管@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的是绑定到 value 的周围范围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 模块绑定

ES6 模块导出了一种新的名称绑定类型,这在 JavaScript 中是以前未知的。导入模块的导出绑定时A,会创建导入名称和引用的绑定。但是,这个引用不是指一个对象,而是指A. 现在我们不仅可以共享引用类型,还可以跨模块共享原语。

于 2016-09-03T08:02:10.120 回答
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}

a所以bar's绑定到 foo's a,并且具有完全相同的值,无论是原始值还是引用。

于 2016-08-31T22:23:14.267 回答