《javascript高级程序设计》学习笔记 | 4.1.原始值与引用值

关注前端小讴,阅读更多原创技术文章

原始值与引用值

  • JS 变量是松散类型的:① 不必规定变量的数据类型 ② 变量的值和数据类型可随时改变
  • JS 变量可以包含 2 种类型的数据:原始值引用值

    • 原始值是简单数据(6 种原始值:Undefined、Null、Boolean、Number、String、Symbol),按值访问,操作实际值
    • 引用值是保存在内存中的对象,按引用访问,操作对该对象的引用(而非对象本身)

相关代码 →

动态属性

  • 对于引用值,可以随时添加、修改、删除其属性和方法
let personRefer = new Object() // 创建对象
personRefer.name = 'Nicholas' // 添加属性并赋值
console.log(personRefer.name) // 'Nicholas'
  • 对于原始值,不能拥有属性(尝试添加属性不会报错)
let namePrim = 'Nicholas' // 原始值
namePrim.age = 27 // 给原始值添加属性
console.log(namePrim.age) // undefined,不报错但无效
  • 用 new 关键字创建原始值包装类型对象,属于(类似于原始类型的)特殊引用类型
let name1 = 'Nicholas' // 原始类型
let name2 = new String('Matt') // 原始值包装类型
name1.age = 27
name2.age = 26
console.log(name1.age) // undefined,原始类型不能有属性
console.log(name2.age) // 26,引用类型可以有属性
console.log(typeof name1) // string,原始类型
console.log(typeof name2) // object,引用类型

复制值

  • 原始值复制时,新值是旧值的副本,新值和旧值相互独立使用、互不干扰
let num1Prim = 5
let num2Prim = num1Prim
console.log(num2Prim) // 5,原始值,复制的值是副本
num2Prim = 6 // 副本发生改变
console.log(num2Prim) // 6
console.log(num1Prim) // 5,被复制的值无变化
  • 引用值复制时,新值和旧值共同指向堆内存中的同一个对象,新值或旧值发生改变相互影响
let obj1Refer = new Object()
let obj2Refer = obj1Refer // 引用值,复制的值是指针
obj2Refer.name = 'Nicholas' // 一个对象发生改变
console.log(obj2Refer.name) // 'Nicholas'
console.log(obj1Refer.name) // 'Nicholas',影响另一个对象
delete obj1Refer.name // 一个对象发生改变
console.log(obj1Refer.name) // undefined
console.log(obj2Refer.name) // undefined,影响另一个对象

传递参数

  • ECMAScript 中所有函数的参数都是按值传递的——函数外的值被复制到函数内部的参数时,和一个变量复制到另一个变量一样:

    • 原始值作为参数时,函数内部改变参数,函数外的值不受影响
    • 引用值作为参数时,函数内部改变对象的属性,函数外的对象受影响
/* 原始值作为参数 */
let count = 10 // 函数外,原始值作为参数
function addTen(num) {
  num += 10 // 函数内,参数的值发生改变
  return num
}
let result = addTen(count)
console.log(result) // 30
console.log(count) // 20,未受影响

/* 引用值作为参数 */
let person = new Object() // 函数外,引用值作为参数
function setName(obj) {
  obj.name = 'Nicholas' // 函数内,obj和外部参数person指向同一个对象,并改变了这个对象的属性
}
setName(person)
console.log(person.name) // 'Nicholas',受影响
  • 尽管用引用值作为参数时,函数内部改变对象的属性会影响函数外的对象,但参数仍然是按值传递的,而不是按引用传递

    • 引用值作为参数时,如果在函数内部重写对象,函数外的对象不受影响,原始的引用仍然没变
    • 如果是按引用传递,重写函数内的对象参数后,原始引用应当发生改变
let person2 = new Object()
function setName2(obj) {
  obj.name = 'Nicholas' // 改变参数的属性,参数受影响
  obj = new Object() // 重写参数,参数不受该影响
  obj.name = 'Greg'
}
setName2(person2)
console.log(person2.name) // 'Nicholas'

确定类型

let str = 'Nicholas'
let num = 30
let boo = true
let u
let n = null
let sym = Symbol()
let f = new Function()
let o = new Object()
let a = new Array()
let r = new RegExp()
  • typeof 操作符可检测的类型:String、Number、Boolean、Symbol、Undefined、Function(不包含 Null 的所有原始值和 Function)
console.log(typeof str) // string,原始值
console.log(typeof num) // number,原始值,原始值
console.log(typeof boo) // boolean,原始值
console.log(typeof u) // undefined,原始值
console.log(typeof sym) // symbol,原始值
console.log(typeof f) // function,引用值但是Function会返回function
  • typeof 操作符检测任何引用值或 null,结果都是 object
console.log(typeof n) // object,原始值但是Null会返回object
console.log(typeof o) // object,除Function之外的引用值都返回object
console.log(typeof a) // object,除Function之外的引用值都返回object
console.log(typeof r) // object,除Function之外的引用值都返回object
  • instanceof 操作符可检测的类型:Object(所有引用值)
console.log(o instanceof Object) // true,o是Object的实例
console.log(f instanceof Function) // true,f是Function的实例
console.log(f instanceof Array) // false,f不是Array的实例
console.log(a instanceof Array) // true,a是Array的实例
console.log(r instanceof RegExp) // true,r是RegExp的实例
  • 所有引用值都是 Object 的实例
console.log(f instanceof Object) // true,所有引用类型都是Object的实例
console.log(a instanceof Object) // true,所有引用类型都是Object的实例
console.log(r instanceof Object) // true,所有引用类型都是Object的实例
  • instanceof 操作符检测任何原始值,结果都是 false
console.log(n instanceof Object) // false,原始值不是对象,不是Object的实例

总结 & 问点

原始值 引用值
简单数据 保存在内存中的对象
按值访问 按引用访问
操作实际值 操作对该对象的引用(而非对象本身)
不能拥有属性 随时增、删、改其属性和方法
复制的值是副本,相互不影响 复制的值是指针,相互影响
作为参数时,函数内部改变值不影响参数 作为参数时,函数内部改变属性会影响参数,重写不影响
typeof 判断类型(null 返回 object) instanceof 判断类型
  • 如何理解 JS 的变量是松散类型的?
  • 在访问方式和操作方式上,原始值和引用值有什么不同?
  • 在通过变量复制时,原始值和引用值有什么不同?
  • 作为函数的参数传递时,原始值和引用值有什么不同?
  • 如何“按值传递函数的参数”?为什么引用值作为函数的参数也是按值传递而不是按引用传递的?
  • 如何判断一个原始值或引用值的具体类型?

你可能感兴趣的:(《javascript高级程序设计》学习笔记 | 4.1.原始值与引用值)