云计算百科
云计算领域专业知识百科平台

对象的引用

分享个很有意思的东西

今天在看新项目的代码的时候,发现一个很神奇的现象。

当接口进来的时候,被拦截器拦截了,此时拦截器给当前构造器中private readonly userJwtPayload: UserJwtPayload 进行了 Object.assign(this.userJwtPayload, payload);就是给它进行了赋值。之后回到控制器,在构造器中也有private readonly userJwtPayload: UserJwtPayload,此时,直接输出userJwtPayload就是有值的了。

这是为什么呢?

因为这两个类得到的 userJwtPayload 并不是两个独立的对象,而是指向内存中同一个对象的“钥匙”或“地址”(专业术语叫“引用”)。

所以,不是“一个类赋值,另一个类也自动被赋值”,而是“一个类通过钥匙进入房间修改了东西,另一个类晚点用同一把钥匙进入同一个房间,自然能看到被修改过的东西”**。

让我们用一个更详细的比喻和技术解释来拆解它。

1. 钥匙的比喻:对象 vs 对象的引用

想象一下,UserJwtPayload 这个对象实例是一个房子 🏠。

  • NestJS的依赖注入容器:是这栋房子的物业管理员 👨‍💼。

  • JwtInterceptor:是房子的保安 👮。

  • 你的控制器 (Controller):是房子的主人 🧑‍💻。

现在,一个请求来了,整个流程是这样的:

  • 物业管理员(DI容器) 为这次请求专门准备了一间全新的、空荡荡的房子(创建了一个 UserJwtPayload 对象实例)。

  • 物业管理员复制了两把指向这间房子的钥匙 🔑。

  • 他先把一把钥匙 🔑 交给了保安(JwtInterceptor)。保安在他的构造函数里拿到了这把钥匙,并把它存放在口袋里(this.userJwtPayload)。

  • 保安用钥匙开门进入房子,检查了访客的身份(解析JWT),然后在房子的桌子上放上了一份访客资料档案(Object.assign(this.userJwtPayload, payload))。然后保安就锁门出来了。

  • 接着,物业管理员把另一把一模一样的钥匙 🔑 交给了主人(你的控制器)。主人在他的构造函数里也拿到了这把钥匙,并把它也放在口袋里(this.userJwtPayload)。

  • 当主人需要用户信息时,他用自己的钥匙开门,进入的还是之前那间房子。他一进门,就看到了桌子上那份被保安放上去的访客资料档案。

  • 结论: 保安和主人并没有互相通信。他们只是通过物业管理员,拿到了通往同一个地方的钥匙。因为他们操作的是同一个房子,所以保安做的改动,主人自然能看到。

    2. 技术层面的解释:值 vs 引用

    在大多数编程语言中,变量可以存储两种东西:

    • 值类型 (Value Types):像数字、布尔值。当你把一个数字变量赋给另一个变量时,你是复制了这个值。

      let a = 10;
      let b = a; // b 得到的是 10 这个值的副本
      b = 20; // 修改 b 不会影响 a
      console.log(a); // 输出 10

    • 引用类型 (Reference Types):像对象、数组。当你把一个对象变量赋给另一个变量时,你复制的是指向该对象的内存地址(引用),而不是对象本身。

      let objA = { name: 'Alice' };
      let objB = objA; // objB 得到的是指向 { name: 'Alice' } 这个对象的内存地址的副本

      // 通过 objB 修改对象
      objB.name = 'Bob';

      // 因为 objA 和 objB 指向同一个对象,所以通过 objA 也能看到修改
      console.log(objA.name); // 输出 'Bob'

    我的 JwtInterceptor 和 Controller 就是上面 objA 和 objB 的情况。

  • NestJS DI 容器 new UserJwtPayload(),在内存中创建了一个对象。

  • 它把这个对象的引用(内存地址)传给了 JwtInterceptor 的构造函数。JwtInterceptor 内部的 this.userJwtPayload 保存了这个引用。

  • 它又把同一个引用传给了 Controller 的构造函数。Controller 内部的 this.userJwtPayload 也保存了这个引用。

  • 因此,JwtInterceptor 里的 this.userJwtPayload 和 Controller 里的 this.userJwtPayload 是两个不同的变量,但它们存储了相同的值——那个指向唯一 UserJwtPayload 实例的内存地址。

    关于 readonly

    你可能会想,readonly 不是只读吗,为什么还能改?

    readonly 关键字限制的是变量本身不能被重新赋值。也就是说,你不能让 this.userJwtPayload 这个“口袋”去装一把新钥匙。

    // 在 Interceptor 或 Controller 内部,这样做会报错
    this.userJwtPayload = new UserJwtPayload(); // 错误!不能给 readonly 属性重新赋值

    但是,readonly 不限制你通过这把钥匙去修改房子里的东西。Object.assign(this.userJwtPayload, payload) 正是在做这件事:它没有改变 this.userJwtPayload 指向的那个对象,而是修改了那个对象内部的属性。这是完全允许的。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 对象的引用
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!