数据的引用和传递
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}
/*要想操作对象,直接在传入的形参的对象进行操作即可,因为会修改引用地址的数据*/面试题
对于这种面试题,我们就老老实实的画图,画出对应的内存空间和地址指向,然后去分析。
函数形参所产生的变量,一定会在函数执行之后就不存在了,画图分析的时候注意及时清理。
- 字节
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- 京东
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 2js
// 下面的代码输出什么(字节)?
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