JavaScript 中的值类型和引用类型

JavaScript 的变量类型:

  • 值类型(基本类型):字符串(string)、数值(number)、布尔值(boolean)、undefined、null (这5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值)(ECMAScript 2016新增了一种基本数据类型:symbol)
  • 引用类型:对象(Object)、数组(Array)、函数(Function)

值类型

值类型的特点:

  1. 占用空间固定,保存在栈中(当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;栈中存储的是基础变量以及一些对象的引用变量,基础变量的值是存储在栈中,而引用变量存储在栈中的是指向堆中的数组或者对象的地址,这就是为何修改引用类型总会影响到其他指向这个地址的引用变量。

  2. 保存与复制的是值本身

  3. 使用 typeof 检测数据的类型

    TypeResult
    Undefined"undefined"
    Null"object" (see below)
    Boolean"boolean"
    Number"number"
    BigInt"bigint"
    String"string"
    Symbol"symbol"
    Function (implements [[Call]] in ECMA-262 terms; classes are functions as well)"function"
    Any other object"object"

    In the first implementation of JavaScript, JavaScript values were represented as a type tag and a value. The type tag for objects was 0. null was represented as the NULL pointer (0x00 in most platforms). Consequently, null had 0 as type tag, hence the typeof return value "object". (reference)
    A fix was proposed for ECMAScript (via an opt-in), but was rejected. It would have resulted in typeof null === 'null'.

  4. 基本类型数据是值类型

JavaScript 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息

  • 000:对象
  • 010:浮点数
  • 100:字符串
  • 110:布尔
  • 1:整数 对于 undefinednull 来说,这两个值的信息存储是有点特殊的。

null:所有机器码均为0

undefined:用 −2^30 整数来表示

但是没人说一下 Symbol 和 BigInt 的

引用类型

引用类型的特点:

  1. 占用空间不固定,保存在堆中(当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。)
  2. 保存与复制的是指向对象的一个指针
  3. 使用 instanceof 检测数据类型, 语法为: object instanceof constructor
    javascript
    1const arr = [1, 2]; 
    2// 判断Object的prototype有没有在数组的原型链上 
    3console.log(arr instanceof Object); // true 
    4// 数组arr的原型 
    5const proto1 = Object.getPrototypeOf(arr); 
    6console.log(proto1); // [] 
    7// 数组arr的原型的原型 
    8const proto2 = Object.getPrototypeOf(proto1); 
    9console.log(proto2); // [] 
    10// Object的prototype 
    11console.log(Object.prototype); 
    12// 判断arr的原型是否与Object的prototype相等 
    13console.log(proto1 === Object.prototype); // false 
    14// 判断arr的原型的原型是否与Object的prototype相等 
    15console.log(proto2 === Object.prototype); // true
    The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value. Its behavior can be customized with Symbol.hasInstance.
  4. 使用 new() 方法构造出的对象是引用型

内存空间

  • 基本类型,保存在 stack 内存中
  • 引用类型,保存在 heap 内存中
  • 事件循环机制 是在队列 queue 中
  • 闭包引用保存在 heap 中

声明提升

  • Language-defined: 所有的作用域默认都会给出thisarguments两个变量名
  • Formal parameters: 形参, function(z) { console.log(z)} z就是形参
  • Function declarations: 函数声明 function () {}
  • Variable declarations: 变量声明 var b = xx var a = function () {}

变量提升,改变解析顺序。

  • 函数声明: 会被提升到作用域的最顶部.
  • 变量声明: 只是定义了, 执行还是按照顺序,没执行到哪儿的, 会返回undefined.

Variables declared by var keyword are scoped to the immediate function body (hence the function scope) while let variables are scoped to the immediate enclosing block denoted by { } (hence the block scope).
While variables declared with var keyword are hoisted (initialized with undefined before the code is run) which means they are accessible in their enclosing scope even before they are declared:

js
1var a = 1;
2function f() {
3  console.log(a);
4  var a = 2;
5}
6f();
  • f() 函数声明提升在前
  • var a = 1 变量声明在后
  • a = 1
  • f()执行
  • console.log(a) 沿着作用域链,var = 2 变量声明了,但是值是undefined
  • 所以返回undefined

let variables are not initialized until their definition is evaluated. Accessing them before the initialization results in a ReferenceError. The variable is said to be in "temporal dead zone" from the start of the block until the initialization is processed.

js
1let a = 1;
2function f() {
3  console.log(a); // 1
4  a = 2;
5}
6f();

At the top level, let, unlike var, does not create a property on the global object:

js
1var foo = "Foo";  // globally scoped
2let bar = "Bar"; // not allowed to be globally scoped
3
4console.log(window.foo); // Foo
5console.log(window.bar); // undefined

In strict mode, var will let you re-declare the same variable in the same scope while let raises a SyntaxError.

js
1'use strict';
2var foo = "foo1";
3var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.
4
5let bar = "bar1"; 
6let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

strict mode

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.

js
1function myStrictFunction() {
2  // because this is a module, I'm strict by default
3}
4export default myStrictFunction;

Copy to Clipboard

All parts of a class's body are strict mode code, including both class declarations and class expressions.

js
1class C1 {
2  // All code here is evaluated in strict mode
3  test() {
4    delete Object.prototype;
5  }
6}
7new C1().test(); // TypeError, because test() is in strict mode
8
9const C2 = class {
10  // All code here is evaluated in strict mode
11};
12
13// Code here may not be in strict mode
14delete Object.prototype; // Will not throw error