基本数据: Undefined、Null、Boolean、Number、String、Symbol
引用数据: Object、Array、Function、RegExp、Date
注意: javascript不允许直接访问内存位置,因此就不能直接操作对象所在的内存空间
一、基本数据与引用数据区别
基本数据:
- 大小是固定的,因此数据是保存在栈里的
- 是按值访问的
- 从一个变量到另一个变量复制原始值会创建该值的第二个副本
引用数据:
- 引用值是对象,存储在堆里,大小是不固定的
- 从一个变量到另一个变量复制引用值只会复制指针,因此结果是两个变量都指向同一个对象
- 保存引用值的数据是按引用访问的,它在栈只保存了指针,实际的内容是存在堆里,存在栈里的指针指向堆里的实际内容(当被访问时,是先访问指针,然后沿着指针的指向找到堆里的实际内容)
当它们复制值时:
基本数据:把基本数据通过变量赋值给另一个变量时,基本数据会被复制到新变量的位置
引用数据:把引用数据通过变量赋值给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区别就在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象,操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变量会在另一个对象上反映出来,就会有深拷贝与浅拷贝
二、深拷贝与浅拷贝的区别
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆数据开辟一个新的区域存放新对象,新旧对象不共享同一个内存,且修改对象不会影响另一个对象
浅拷贝:如果对象的属性是基本类型,那拷贝的就是基本类型的值;如果是引用类型,那拷贝的就是指针,新旧对象共享同一块内存
三、深拷贝与浅拷贝的一些实现
浅拷贝
- 展开运算符... ,实现浅拷贝
let obj1 = {
name: 'jack',
age: 19,
hobby: ['swimming', 'walking', 'go']
}
let obj2 = {...obj1}
obj2.age=30
obj2.hobby[0] = 'haha'
// obj1 ==> { age: 19, name: "jack", hobby: ["haha", "walking", "go"] }
以上:浅拷贝,第一层的数据如果是基本类型,是可以实现拷贝的,但是数据是引用类型,修改新对象的属性就会影响原对象的属性
- Object.assign(),实现浅拷贝
let obj2 = Object.assign({}, obj1);
- Array.prototype.concat()
let arr2 = arr1.concat([]);
- Array.prototype.slice()
let arr2 = arr1.slice();
注意:当原数据(object)只有一层时,就是深拷贝; 当Array只有一层的时候,是深拷贝;所以当原数据进行浅拷贝,改变arr2的arr[1],而原数据arr1中的arr1[1]没有改变
深拷贝
- JSON.parse(JSON.stringify())
let obj2 = JSON.parse(JSON.stringify(obj1))
let arr2 = JSON.parse(JSON.stringify(arr1))
Object、Array可以通过JSON.parse(JSON.stringify())实现深拷贝,但是Date跟Function却不能
- jQuery.extend()方法
// 需要引入jQuery库哦
let obj2 = jQuery.extend(true, {}, obj1)
- 手写递归实现深拷贝
// 检测数据类型的功能函数
const checkedType = (target) => Object.prototype.toString.call(target).replace(/\[object (\w+)\]/, "$1").toLowerCase();
// 实现深拷贝(Object/Array)
const clone = (target) => {
let result;
let type = checkedType(target);
if(type === 'object') result = {};
else if(type === 'array') result = [];
else return target;
for (let key in target) {
if(checkedType(target[key]) === 'object' || checkedType(target[key]) === 'array') {
result[key] = clone(target[key]);
} else {
result[key] = target[key];
}
}
return result;
}
调用一下手写递归实现深拷贝方法:
const obj = {
name: 'Chen',
detail: {
age: '18',
height: '180',
bodyWeight: '68'
},
hobby: ['see a film', 'write the code', 'play basketball', 'tourism']
}
const obj1 = clone(obj);
console.log(obj1); // { name: 'Chen',detail: { age: '18', height: '180', bodyWeight: '68' }, hobby: [ 'see a film', 'write the code', 'play basketball', 'tourism' ]}
console.log(obj1 === obj); // false
判断数据类型
- typeof:只能判断该变量是否为原始值,如果值为null或者对象,那么会返回'object'
let s = 'string'
let num = 10
let b = true
let u = undefined
let nu = null
let o = new Object()
console.log(typeof s) // "string"
console.log(typeof num) // "number"
console.log(typeof b) // "boolean"
console.log(typeof u) // "undefined"
console.log(typeof nu) // "object"
console.log(typeof o) // "object"
- instanceof:instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上(原理:因为A instanceof B 可以判断A是不是B的实例,返回一个布尔值,由构造类型判断出数据类型)
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car) // expected output: true
console.log(auto instanceof Object) // expected output: true
console.log([1,2,3] instanceof Array ); // true
console.log(new Date() instanceof Date ); // true
console.log(function f() {} instanceof Function ); // true
- Object.prototype.toString.call
console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
// 封装获取数据类型
function getType(value) {
let type = typeof value;
if (type !== 'object') { // 如果是基本数据类型,直接返回
return type;
}
// 如果是引用数据类型,再进一步判断,正则返回结果
return Object.prototype.toString.call(value).replace(/^\[object (\S+)\]$/, '$1');
}
console.log(getType(123)) // 'number'
console.log(getType('aaa')) // 'string'
console.log(getType(() => {})) // 'function'
console.log(getType([])) // 'Array'
console.log(getType({})) // 'Object'
console.log(getType(null)) // 'Null'
网友评论