Javascript参考与绑定......有什么区别?

时间:2016-08-31 22:06:28

标签: javascript reference ecmascript-6 es6-modules

我最近在Kyle Simpson的“你不知道JS:ES6”中阅读了以下内容

" [ES6模块导出]实际绑定(几乎像指针)到内部模块定义中的标识符。"

我的困惑是这些绑定与参考文献有何不同......

我知道JS中的引用仅适用于非基本类型(如对象),所以给定

let object1 = {a: 1};
let object2 = object1;

object1object2现在引用(它们都是引用)相同的对象 如果我向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

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

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

3 个答案:

答案 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模块绑定

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,并且具有完全相同的值,无论是原语还是引用。