JS - 变量 的复制、传参、类型检测

52 阅读3分钟

把值赋值给变量的时候,js 引擎必须确定这个值是原始值还是引用值,原始值的变量是按值访问(操作的就是存储在变量中的实际值);

引用值是保存在内存中的值(js 不允许直接访问内存位置,不能直接操作对象所在的内存空间),操作的是对该对象的引用而非对象本身,即保存引用值的变量是按引用访问的

和其他语言不同的是: js 中的字符串不是使用对象表示的

变量

原始值和引用值

原始值不能有属性、而引用类型可以有属性

const renference = {};
renference.name = "zhang3";
log(renference); // { name: 'zhang3' }

const primitive = "3";
// 给原始值添加属性不会报错,但是也不会添加成功
primitive.name = "test";

log(primitive); // log: 3 ,挂在的属性并不存在

值的赋值

  • 原始值的赋值,原始值会被复制到新的变量的位置上,即新赋值的变量和原来的变量无关,两者相互独立
  • 引用值从一个变量赋值给另一个变量时,复制的值其实是一个指针,它指向存储在堆内存中的对象。赋值操作实际上两个变量指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来
const oldPrimitiveVal = 3;
// 这里是值的拷贝,两个变量独立而互不干扰
let newPrimitiveVal = oldPrimitiveVal;
log(newPrimitiveVal); // log: 3

newPrimitiveVal = 33; // 改变新的原始类型的值
log(newPrimitiveVal); // log: 33; 改变成功
log(oldPrimitiveVal); // log: 3; 不会影响原来的值

// 引用类型的赋值
const oldObj = {
  name: "zhang3",
  age: 23,
};

// 这里其实是将 newObj 指向 oldObj 中的内存对象,即共享 同一个堆内存
const newObj = oldObj;
log(newObj); // log: { name: 'zhang3', age: 23 }; 赋值成功
newObj.age = 33; // 改变 newObj.age 会改变共享内存中的值,因此 oldObj 也会改动
log(newObj); // log: { name: 'zhang3', age: 33 }
log(oldObj); // log: { name: 'zhang3', age: 33 }; look! 它也被改变了

按值传递参数

在 js 中,将一个传参只有按值传递,因为值会被复制到一个局部变量 arguments 对象的一个槽位中(ECMAScript 中函数的参数就是局部变量)

const update = (obj) => {
  // obj 和外部传递进来的 对象 指向同一堆内存,修改 obj 的属性将会影响到指向同一堆内存的相应属性的值
  obj.name = "newName";
  // 但是 obj 指向了新的 堆内存,因为不是按引用传参数,所以不会影响外部对象的 指向
  obj = {};
  obj.name = "lastName";
};

const outterObj = {};
update(outterObj); // outterObj 的值被复制到参数 obj 中
log(outterObj); // log: { name: 'newName' }; 如果 是按照引用传值则 outterObj 的 name 应该是 lastName

值的检查注意事项

注意 typeof操作符在用于检测函数时也会返回"function"。当在Safari(直到Safari 5)和Chrome(直到Chrome 7)中用于检测正则表达式时,由于实现细节的原因,typeof也会返回"function"。ECMA-262规定,任何实现内部[[Call]]方法的对象都应该在typeof检测时返回"function"。因为上述浏览器中的正则表达式实现了这个方法,所以typeof对正则表达式也返回"function"。在IE和Firefox中,typeof对正则表达式返回"object"。

typeof null 返回的结果为 object