JavaScript Reference Type解读

Reference Type

在 JavaScript 中,引用类型(Reference Type)是一种数据存储和操作的方式。

Reference Type 是 ECMA 中的一个“规范类型”。我们不能直接使用它,但它被用在 JavaScript 语言内部。

Reference Type 的值是一个三个值的组合 (base, propertyname, strict)

  • base 是对象。
    在 JavaScript 中,所有对象或者函数都有所属对象。在全局上下文中,base 等同于全局对象(global);在函数的执行上下文中,base 等同于变量对象(vo)或活动对象(ao);而在处理对象属性时,base 等同于所属的对象(owerObject)。
  • propertyname 是属性名。
  • strictuse strict 模式下为 true

用伪代码表示:

var valueOfReferenceType = {
  base: // 对象的所属对象
  propertyname: // 属性名
};

示例:

let obj1 = { name: 'Alice' };
console.log(obj1.name); // Alice

当访问 obj1.name 时,得到的引用类型的值中,base 就是 obj1 对象本身,propertyname 就是属性name

引用类型本身并不保存真正的值,而是存储对数据实际存储位置的引用(类似于指针)。这意味着多个变量可以引用同一个对象,对其中一个变量所做的修改会反映在其他引用该对象的变量上。
常见的引用类型包括对象(Object)、数组(Array)和函数(Function)、日期(Date)、正则表达式(RegExp)。

以Object为例,解读Reference Type

在 JavaScript 中,对象(Object)是一种引用类型(Reference Type)。

当我们在读取对象的某个属性的时候,'.'返回的准确来说不是属性的值,而是一个特殊的Reference Type类型的值,在这个其中存着属性的值和它的来源对象。

示例:

let obj1 = { name: 'Alice' };
let obj2 = obj1;  // obj2 复制了 obj1 的引用

console.log(obj1.name ===  obj2.name); // true

function updateObj(obj) {
	obj.age = 30
	obj.name = 'Bob'
}
updateObj(obj2);
console.log(obj2); // { name: 'Bob', age: 30 }
console.log(obj1.name, obj1.age); // 'Bob', 30   因为 obj1 和 obj2 指向同一个对象
console.log(obj1.name ===  obj2.name); // true

在示例中,let obj1 = { name: 'Alice' };做了2件事:

  1. 在内存的堆中创建了一个对象,这个对象具有一个名为 name 的属性,其值为 "Alice"
  2. 然后,在栈中创建了变量 obj1obj1存储的是指向堆中的对象{ name: 'Alice' }的引用(而不是对象本身的值)。

let obj2 = obj1;obj1 的引用赋值给了 obj2 。因此,obj1obj2 保存的都是对同一个对象的引用。
当调用 updateObj(obj2) 函数时,虽然传递的是 obj2 ,但由于是引用传递,实际上传递的是指向堆中对象的引用。在函数内部对这个对象进行修改,会同时反映在 obj1obj2 上。

打个比方,对象{ name: 'Alice' }就像是家里大门的锁,一家几口人都各有1把钥匙能开门回家。使用钥匙obj1的人开门回家,并做好了饭。使用钥匙obj2开门回家的人,就可以直接恰饭了。

以函数为例,解读Reference Type

obj.method() 语句中有两个操作:

  1. 首先,点 '.' 取了属性 obj.method 的值。
  2. 接着 () 执行了它。

在通过点 '.' 操作符或方括号[]操作符调用方法时,this的指向与引用类型的对象有关。
在一些复杂的表达式中,可能会导致this丢失或指向不正确。

示例:

let user = {
  name: "Alice",
  hi() { console.log(this.name); },
  bye() { console.log("Bye"); }
};

user.hi(); // 正常运行,在hi()方法内部,this 正确地指向了 user 对象

// 现在让我们基于 name 来选择调用 user.hi 或 user.bye
(user.name == "Alice" ? user.hi : user.bye)(); // Error! 
//Uncaught TypeError: Cannot read properties of undefined (reading 'name')

在示例中,根据三元运算(user.name == "John" ? user.hi : user.bye)判断具体使用哪个方法,结果是使用user.hi。接着该方法被通过 () 立刻调用。
执行user.hi报错Uncaught TypeError: Cannot read properties of undefined (reading 'name')

为什么会报错?

在 JavaScript 中,条件表达式返回的是函数本身,而不是函数的调用结果。

因此,在严格模式下:

  • 执行user.hi()语句,对属性 user.hi 访问的结果不是一个函数,而是一个 Reference Type 的值。当 () 被在 Reference Type 上调用时,它们会接收到关于对象和对象的方法的完整信息,然后可以设置正确的 this(在此处 = user)。
// user.hi() Reference Type 的值
(user, "hi", true)

其中,user 是对象本身,"hi" 是属性名,true 表示处于严格模式。

  • 当执行 hi = user.hi 后,hi 仅仅是函数本身,即hi() { console.log(this.name); }。它丢失了原有的 Reference Type 信息,也就丢失了与特定对象user的关联以及严格模式的相关设置。
    在严格模式下,如果直接调用变量函数 hithis的值会是undefined

(user.name == "Alice" ? user.hi : user.bye)();的详细写法如下:

// 把获取方法和调用方法拆成2个步骤
let func;
if(user.name == "Alice") {
  func = user.hi; // 把函数 user.hi 赋值给了变量 func
}ele{
  func = user.bye; // 把函数 user.bye 赋值给了变量 func
}
func(); // Uncaught TypeError: Cannot read properties of undefined (reading 'name')

当执行 func() 时,this 的指向不是 user 对象,thisundefined,导致无法正确访问 name 属性,引发错误。

解决方案:可以使用call、apply或bind方法来明确指定this的指向。

let func = (user.name == "Alice"? user.hi : user.bye);
func.call(user); 
// 通过 call 方法将 user 对象作为 this 传递给函数

Reference Type 是一个特殊的“中间人”内部类型,目的是从 '.' 传递信息给 () 调用。

总结

Reference Type 是语言内部的一个类型。

引用类型具有以下特点:

  • 存储方式
    引用类型的值在内存中的存储分为两部分,一部分是对象本身,存储在堆(heap)中;另一部分是对象的引用地址,存储在栈(stack)中。变量实际上存储的是这个引用地址。
    赋值了对象的变量存储的不是对象本身,而是该对象“在内存中的地址”。换句话说就是对该对象的“引用”。
  • 引用共享
    对象是“通过引用”存储和复制的。
  • 传递方式
    在函数传参时,引用类型是按引用传递。这意味着函数内部对参数的修改会影响到原始的对象或数组。
  • 可变性
    引用类型的值是可变的。例如,可以修改对象的属性或数组的元素。
  • 比较方式
    对于严格相等(===),比较的是引用地址是否相等,即是否是同一个对象;对于宽松相等(==),会进行类型转换后再比较值。
  • this 指向:在通过点'.'操作符或方括号[]操作符调用方法时,this的指向与引用类型的对象有关。在一些复杂的表达式中,可能会导致this丢失或指向不正确。
  • 28
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值