Javascript 引用与绑定...有什么区别?
Javascript reference vs binding...what's the difference?
我最近在 Kyle Simpson 的《你不知道的 JS》中阅读了以下内容:ES6
“ES6 模块导出] 实际绑定(几乎像指针)到内部模块定义中的标识符。”
我的困惑是这些绑定与引用有何不同...
我了解到JS中的reference只适用于非原始类型(如对象),因此给定
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
除了原始值也可以共享一个绑定(而引用仅限于非原始类型)之外,绑定是否就像引用一样?
我觉得我在这里遗漏了什么...
效果并不特定于原始值,就像访问对象上的 属性。
例如:
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
,并且具有完全相同的值,无论是原始值还是引用。
A binding is a very generic term for "what a name refers to". Every identifier in a scope is bound to something. Usually they resolve to variables in a variable environment (storage slots in an environment record),但也有例外(例如with
或全局对象)。
A reference is a term for a pointer to some kind of structure. For example, objects are known as "reference values" 因为它们引用具有标识的可变属性的容器。
ES6 模块现在引入了一种新的绑定类型,一种以前未知的类型。它不是一个普通的变量,而是字面上对另一个变量的引用——从另一个模块导出的变量。如果模块变量发生变化,这将通过导入反映出来——它们都指向同一个环境记录槽。
export
声明添加了从本地名称到 module interface, while an import
declaration adds a mapping from a name in the respective module interface to a local name. When a module is instantiated, an indirect binding is created 中的名称的映射,该映射指向与导出模块中的本地绑定相同的环境。
尽管@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
。
名称绑定决定标识符在特定范围内引用哪个变量。
原始类型和引用类型
绑定的关联值可以表示
- 一个value/primitive类型
- 或引用类型
原始类型在 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
的导出绑定。现在我们不仅可以共享引用类型,还可以跨模块共享原语。
我最近在 Kyle Simpson 的《你不知道的 JS》中阅读了以下内容:ES6
“ES6 模块导出] 实际绑定(几乎像指针)到内部模块定义中的标识符。”
我的困惑是这些绑定与引用有何不同...
我了解到JS中的reference只适用于非原始类型(如对象),因此给定
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
除了原始值也可以共享一个绑定(而引用仅限于非原始类型)之外,绑定是否就像引用一样?
我觉得我在这里遗漏了什么...
效果并不特定于原始值,就像访问对象上的 属性。
例如:
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
,并且具有完全相同的值,无论是原始值还是引用。
A binding is a very generic term for "what a name refers to". Every identifier in a scope is bound to something. Usually they resolve to variables in a variable environment (storage slots in an environment record),但也有例外(例如with
或全局对象)。
A reference is a term for a pointer to some kind of structure. For example, objects are known as "reference values" 因为它们引用具有标识的可变属性的容器。
ES6 模块现在引入了一种新的绑定类型,一种以前未知的类型。它不是一个普通的变量,而是字面上对另一个变量的引用——从另一个模块导出的变量。如果模块变量发生变化,这将通过导入反映出来——它们都指向同一个环境记录槽。
export
声明添加了从本地名称到 module interface, while an import
declaration adds a mapping from a name in the respective module interface to a local name. When a module is instantiated, an indirect binding is created 中的名称的映射,该映射指向与导出模块中的本地绑定相同的环境。
尽管@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
。
名称绑定决定标识符在特定范围内引用哪个变量。
原始类型和引用类型
绑定的关联值可以表示
- 一个value/primitive类型
- 或引用类型
原始类型在 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
的导出绑定。现在我们不仅可以共享引用类型,还可以跨模块共享原语。