11.值

原文: http://exploringjs.com/impatient-js/ch_values.html

在本章中,我们将研究 JavaScript 具有哪些值。

支持工具:===

在本章中,我们偶尔会使用严格相等运算符。如果ab相等,a === b将评估为true。具体含义在关于运算符的章节中有所解释。

11.1。什么是类型?

在本章中,我将类型视为值集。例如,类型boolean是集{falsetrue}。

11.2。 JavaScript 的类型层次结构

Figure 6: A partial hierarchy of JavaScript’s types. Missing are the classes for errors, the classes associated with primitive types, and more. The diagram hints at the fact that not all objects are instances of Object.

Figure 6: A partial hierarchy of JavaScript’s types. Missing are the classes for errors, the classes associated with primitive types, and more. The diagram hints at the fact that not all objects are instances of Object.

6 显示了 JavaScript 的类型层次结构。我们从该图中学到了什么?

  • JavaScript 区分两种值:原始值和对象。我们很快就会看到有什么区别。
  • 该图区分了类Object的对象和实例。 Object的每个实例也是一个对象,但反之亦然。但是,实际上您在实践中遇到的所有对象都是Object的实例。例如,通过对象字面值创建的对象是。关于该主题的更多细节在关于原型链和类的章节中进行了解释。

11.3。语言规范的类型

ECMAScript 规范只知道总共 7 种类型。这些类型的名称是(我使用 TypeScript 的名称,而不是规范的名称):

  • undefined:唯一元素undefined
  • null:唯一元素null
  • boolean:包含falsetrue元素。
  • number:所有数字的类型(例如-1233.141)。
  • string:所有字符串的类型(例如'abc')。
  • symbol:所有符号的类型(例如Symbol('My Symbol'))。
  • object:所有对象的类型(与Object不同,类Object及其子类的所有实例的类型)。

11.4。原始值与对象

规范对值进行了重要区分:

  • _ 原始值 _ 是undefinednullbooleannumberstringsymbol类型的元素。
  • 所有其他值都是 _ 对象 _。

与 Java 相比(这里激发了 JavaScript),原始值不是二等公民。它们和对象之间的区别更加微妙。简而言之,它是:

  • 原始值:是 JavaScript 中的原子数据块。
    • 它们是 _ 传递的值 _:当原始值分配给变量或传递给函数时,它们的内容被复制。
    • 它们 _ 按值 _ 进行比较:比较两个原始值时,比较它们的内容。
  • 对象:是复合数据。
    • 它们是 _ 通过身份 _(我的术语)传递:当对象被分配给变量或传递给函数时,它们的 _ 身份 _(思考指针)被复制。
    • 它们是 _ 通过身份 _(我的术语)进行比较:当比较两个对象时,他们的身份进行比较。

除此之外,原始值和对象非常相似:它们都具有 _ 属性 _(键值条目),并且可以在相同的位置使用。

接下来,我们将更深入地研究原始值和对象。

11.4.1。原始值(简称:基元)

11.4.1.1。原始是不可改变的

您无法更改,添加或删除基元的属性:

let str = 'abc';
assert.equal(str.length, 3);
assert.throws(
  () => { str.length = 1 },
  /^TypeError: Cannot assign to read only property 'length'/
);
11.4.1.2。原始值 _ 通过值 _ 传递

基元 _ 通过值 _ 传递:变量(包括参数)存储基元的内容。将原始值分配给变量或将其作为参数传递给函数时,会复制其内容。

let x = 123;
let y = x;
assert.equal(y, 123);
11.4.1.3。原始值 _ 按值 _ 进行比较

基元 _ 按值 _ 进行比较:当比较两个原始值时,我们比较它们的内容。

assert.equal(123 === 123, true);
assert.equal('abc' === 'abc', true);

要了解这种比较方式有什么特别之处,请继续阅读并找出对象的比较方式。

11.4.2。对象

关于 OOP 的两章详细介绍了对象。在这里,我们主要关注它们与原始值的区别。

让我们首先探讨创建对象的两种常见方法:

  • 对象字面值:

    const obj = {
        first: 'Jane',
        last: 'Doe',
    };
    

    对象字面值以花括号{}开头和结尾。它创建一个具有两个属性的对象。第一个属性具有键'first'(字符串)和值'Jane'。第二个属性具有键'last'和值'Doe'。有关对象字面值的更多信息,请参阅关于单个对象的章节。

  • 数组字面值:

    const arr = ['foo', 'bar'];
    

    Array 字面值以方括号[]开头和结尾。它创建一个带有两个 _ 元素 _ 的数组:'foo''bar'。有关数组字面值的更多信息,请参阅关于数组的章节。

11.4.2.1。默认情况下,对象是可变的

默认情况下,您可以自由更改,添加和删除对象的属性:

const obj = {};

obj.foo = 'abc'; // add a property
assert.equal(obj.foo, 'abc');

obj.foo = 'def'; // change a property
assert.equal(obj.foo, 'def');
11.4.2.2。对象是 _ 通过身份传递 _

对象是 _ 通过身份传递 _(我的术语):变量(包括参数)存储对象的 _ 身份 _。

对象的标识就像是 _ 堆 _ 上的对象实际数据的指针(或透明引用)(想想 JavaScript 引擎的共享主内存)。

将对象分配给变量或将其作为参数传递给函数时,会复制其标识。每个对象字面值在堆上创建一个新对象并返回其标识。

const a = {}; // fresh empty object
// Pass the identity in `a` to `b`:
const b = a;

// Now `a` and `b` point to the same object
// (they “share” that object):
assert.equal(a === b, true);

// Changing `a` also changes `b`:
a.foo = 123;
assert.equal(b.foo, 123);

JavaScript 使用 _ 垃圾收集 _ 自动管理内存:

let obj = { prop: 'value' };
obj = {};

现在obj的旧值{ prop: 'value' }是 _ 垃圾 _(不再使用)。 JavaScript 会自动 _ 垃圾收集 _ 它(从内存中删除),在某个时间点(如果有足够的可用内存,可能永远不会)。

详情:通过身份传递

“通过标识传递”意味着对象(透明引用)的标识按值传递。这种方法也称为“通过共享传递”

11.4.2.3。通过身份比较对象

通过标识(我的术语)比较对象 :如果两个变量包含相同的对象标识,则它们仅相等。如果它们引用具有相同内容的不同对象,则它们不相等。

const obj = {}; // fresh empty object
assert.equal(obj === obj, true); // same identity
assert.equal({} === {}, false); // different identities, same content

11.5。运算符typeofinstanceof:值的类型是什么?

两个运算符typeofinstanceof可以确定给定值x的类型:

if (typeof x === 'string') ···
if (x instanceof Array) ···

他们有什么不同?

  • typeof区分规范的 7 种类型(减去一个遗漏,加上一个补充)。
  • instanceof测试哪个类创建了给定值。

经验法则:typeof用于原始值,instanceof用于对象

11.5.1。 typeof

Table 2: The results of the typeof operator.

x typeof x
undefined 'undefined'
null 'object'
布尔 'boolean'
'number'
'string'
符号 'symbol'
功能 'function'
所有其他对象 'object'

TBL。 2 列出typeof的所有结果。它们大致对应于语言规范的 7 种类型。唉,有两个不同之处,它们是语言怪癖:

  • typeof null返回'object'而不是'null'。那是一个错误。不幸的是,它无法修复。 TC39 尝试这样做,但它在网络上打破了太多代码。
  • 函数的typeof应该是'object'(函数是对象)。为功能引入单独的类别令人困惑。

练习:typeof 的两个练习

  • exercises/operators/typeof_exrc.js

  • 奖金:exercises/operators/is_object_test.js

11.5.2。 instanceof

该运算符回答了问题:是否有x类创建了值x

x instanceof C

例如:

> (function() {}) instanceof Function
true
> ({}) instanceof Object
true
> [] instanceof Array
true

原始值不是任何实例:

> 123 instanceof Number
false
> '' instanceof String
false
> '' instanceof Object
false

练习:instanceof

exercises/operators/instanceof_exrc.js

11.6。类和构造函数

JavaScript 的对象原始工厂是 _ 构造函数 _:普通函数,如果通过new运算符调用它们,则返回自身的“实例”。

ES6 引入了 _ 类 _,它们主要是构造函数的更好的语法。

在本书中,我可以互换地使用术语 _ 构造函数 _ 和 _ 类 _。

类可以看作是将规范的单一类型object划分为子类型 - 它们给出了比规范中有限的 7 种类型更多的类型。每个类都是由它创建的对象的类型。

11.6.1。与基本类型关联的构造函数

每个基本类型(undefinednull的规范内部类型除外)都有一个关联的 _ 构造函数 _(思考类):

  • 构造函数Boolean与布尔值相关联。
  • 构造函数Number与数字相关联。
  • 构造函数String与字符串相关联。
  • 构造函数Symbol与符号相关联。

每个功能都扮演着几个角色。例如,Number

  • 您可以将其用作函数并将值转换为数字:

    assert.equal(Number('123'), 123);
    
  • Number.prototype提供数字的属性。例如,方法.toString()

    assert.equal((123).toString, Number.prototype.toString);
    
  • Number是数字工具函数的命名空间/容器对象。例如:

    assert.equal(Number.isInteger(123), true);
    
  • 最后,您还可以将Number用作类并创建数字对象。这些对象与实数不同,应该避免。

    assert.notEqual(new Number(123), 123);
    assert.equal(new Number(123).valueOf(), 123);
    
11.6.1.1。包装原始值

与基本类型相关的构造函数也称为 _ 包装类型 _,因为它们提供了将原始值转换为对象的规范方法。在此过程中,原始值被“包装”在对象中。

const prim = true;
assert.equal(typeof prim, 'boolean');
assert.equal(prim instanceof Boolean, false);

const wrapped = Object(prim);
assert.equal(typeof wrapped, 'object');
assert.equal(wrapped instanceof Boolean, true);

assert.equal(wrapped.valueOf(), prim); // unwrap

包装在实践中很少有用,但它在语言规范内部使用,以提供原语属性。

11.7。在类型之间转换

在 JavaScript 中,有两种方法可以将值转换为其他类型:

  • 显式转换:通过String()等功能。
  • _ 强制 _(自动转换):当操作接收到无法使用的操作数/参数时发生。

11.7.1。类型之间的显式转换

与基本类型关联的函数显式地将值转换为该类型:

> Boolean(0)
false
> Number('123')
123
> String(123)
'123'

您还可以使用Object()将值转换为对象:

> typeof Object(123)
'object'

11.7.2。强制(类型之间的自动转换)

对于许多操作,如果操作数/参数的类型不适合,JavaScript 会自动转换它们。这种自动转换称为 _ 强制 _。

例如,乘法运算符将其操作数强制转换为数字:

> '7' * '3'
21

许多内置函数也强制执行。例如,parseInt()将其参数强制转换为字符串(解析在第一个不是数字的字符处停止):

> parseInt(123.45)
123

练习:将值转换为基元

exercises/values/conversion_exrc.js

测验

参见测验应用程序


书籍推荐