Skip to content

数据的引用和传递

JS中分为两种类型的数据类型:

基本数据类型:string number boolean null undefined

引用数据类型:array object ...

数据的引用

在JS中,基本数据类型的存储会在内存空间开辟一块新的地址。

js
var a = 10;//a开辟了一个自己的空间 a:10
var b = a;//b也开辟了一个自己的空间 但是只是把a的值给复制到了自己的空间里。

b = 20;//此时修改b,并不会影响到a,  b:20  a:10

引用数据类型就不一样了,引用数据类型存储的是地址。

js
var obj = {
    name:"momoca",
    age:20
}
//等号右侧的对象也会开启一块空间,然后将该空间的地址赋值给左边的obj变量

obj.name="张三";//会修改到原对象,因为顺着obj存储的地址找下去了。

var obj2 = obj;//此时,将obj的引用地址赋值给了obj2,现在obj2和obj用的是同一个地址。
obj2.age=30;//obj也跟着修改了。

案例

下面我们疯狂的看一些示例:

js
var user1 = {
  name: 'monica',
  addr: {
    province: '黑龙江',
    city: '哈尔滨',
  },
  loves: ['音乐', '电影'],
};

var address = user1.addr;

address = {
  province: '四川',
  city: '成都',
};

console.log(address.city, user1.addr.city);//成都  哈尔滨

数据的传递

传递一般都是在函数中进行的

js
/**
 * 交换两个变量的值
 * @param {*} a 变量1
 * @param {*} b 变量2
 */
function swap(a, b) {
    let temp = a;
    b = a;
    a = temp;
}

var a = 1, b = 2;
swap(a, b);
console.log(a, b); // 1 2

/* 
1. 这个无法进行交换,无法使用函数实现 
因为参数传递进来时,形参的a b 都是独立开辟了一个空间
和外面的ab不会产生任何联系
*/

那么怎么解决上面这个转换的问题呢?我们可以传进来一个对象

js
/**
 * 交换对象两个属性的值
 * @param {Object} obj 对象
 * @param {string} key1 属性名1
 * @param {string} key2 属性名2
 */
function swap(obj, key1, key2) {
    let temp = obj[key1];
    obj[key1] = obj[key2];
    obj[key2] = temp;
}

var obj = { a: 1, b: 2 };
swap(obj, 'a', 'b');
console.log(obj); // {a: 2, b: 1}

/* 这个可以的原因是因为:
当obj传递进来时,形参的obj和外面的obj是同一个地址,所以引用是一样的。
*/
js
/**
 * 交换数组两个位置的值
 * @param {Array} arr 数组
 * @param {number} i1 下标1
 * @param {number} i2 下标2
 */
function swap(arr, i1, i2) {
    let temp = arr[i1];
    arr[i1] = arr[i2];
    arr[i2] = temp;
}


var arr = [1, 2, 3];
swap(arr, 0, 2);
console.log(arr); // [3, 2, 1]

/* 
这个能交换的原因也很简单,因为数组也是引用类型的对象
*/
js
/* 工具函数 */
function includes(arr, target) {
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) {
            return true
        }
    }
    return false
}


/**
 * 修改对象,仅保留需要的属性
 * @param {Object} obj 要修改的对象
 * @param {Array<string>} keys 需要保留的属性名数组
 */
function pick(obj, keys) {
    for (let k in obj) {
        if (!includes(keys, k)) {
            delete obj[k];
        }
    }
}


var obj = { a: 1, b: 2, c: 3 };
pick(obj, ['a', 'c']);
console.log(obj); // {a: 1, c: 3}

/*要想操作对象,直接在传入的形参的对象进行操作即可,因为会修改引用地址的数据*/

面试题

对于这种面试题,我们就老老实实的画图,画出对应的内存空间和地址指向,然后去分析。

函数形参所产生的变量,一定会在函数执行之后就不存在了,画图分析的时候注意及时清理。

  1. 字节
js
// 下面代码输出什么?
var foo = {
  n: 0,
  k: {
    n: 0,
  },
};
var bar = foo.k;
bar.n++;
bar = {
  n: 10,
};
bar = foo;
bar.n++;
bar = foo.n;
bar++;
console.log(foo.n, foo.k.n);// 1  1
  1. 京东
js
// 下面的代码输出什么(京东)?
var foo = {
  n: 1,
};

var arr = [foo];

function method1(arr) {
  var bar = arr[0];
  arr.push(bar);
  bar.n++;
  arr = [bar];
  arr.push(bar);
  arr[1].n++;
}
function method2(foo) {
  foo.n++;
}
function method3(n) {
  n++;
}
method1(arr);
method2(foo);
method3(foo.n);

console.log(foo.n, arr.length);//4 2
js
// 下面的代码输出什么(字节)?
var foo = { bar: 1 };
var arr1 = [1, 2, foo];
var arr2 = arr1.slice(1);
arr2[0]++;
arr2[1].bar++;
foo.bar++;
arr1[2].bar++;
console.log(arr1[1] === arr2[0]);//false
console.log(arr1[2] === arr2[1]);//true
console.log(foo.bar);//4

MIT License