js数据类型 /数据类型判断/如何写深拷贝

一、js数据类型有哪些:

主要可以分为两大类:

基本数据类型(值类型): Number、String、Boolean、Undefined、Null、Symbol(es6新增独一无二的值) 和 BigInt(es10新增);

引用数据类型: Object。包含Object、Array、 function、Date、RegExp。

备注: 基本数据类型,又称值类型。

主要区别:

值类型栈存储: 主要针对(Number、String、Boolean)三种数据。直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。

引用类型堆栈存储: 主要针对Object、Array这两种引用数据以及null, 同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
备注:

引用类型存储把值存储在堆内存中,堆内存是从下往上存储。生成唯一内存地址。然后在栈内存中把地址赋值给变量。栈内存是从上往下存储的。之所以如此划分内存主要考虑到特别大的对象进行值传递时的效率问题。

二、数据类型判断:

四种方法

typeof、instanceof、constructor、Object.prototype.toString.call()、jquery.type()

1、typeof

基本数据类型中:Number,String,Boolean,undefined 以及引用数据类型中Function ,可以使用typeof检测数据类型,分别返回对应的数据类型小写字符。
另:用typeof检测构造函数创建的Number,String,Boolean都返回object
基本数据类型中:null 。引用数据类型中的:Array,Object,Date,RegExp。不可以用typeof检测。都会返回小写的object

console.log(
    typeof 100, //"number"
    typeof 'abc', //"string"
    typeof false, //"boolean"
    typeof undefined, //"undefined"
    typeof null, //"object"
    typeof [1,2,3], //"object"
    typeof {a:1,b:2,c:3}, //"object"
    typeof function(){console.log('aaa');}, //"function"
    typeof new Date(), //"object"
    typeof /^[a-zA-Z]{5,20}$/, //"object"
    typeof new Error() //"object"
    typeof new Number(100), //'object'
    typeof new String('abc'),// 'string'
    typeof new Boolean(true),//'boolean'
);
2 . instanceof

除了使用typeof来判断,还可以使用instanceof。instanceof运算符需要指定一个构造函数,或者说指定一个特定的类型,它用来判断这个构造函数的原型是否在给定对象的原型链上。

console.log(
    100 instanceof Number, //false
    'dsfsf' instanceof String, //false
    false instanceof Boolean, //false
    undefined instanceof Object, //false
    null instanceof Object, //false
    [1,2,3] instanceof Array, //true
    {a:1,b:2,c:3} instanceof Object, //true
    function(){console.log('aaa');} instanceof Function, //true
    new Date() instanceof Date, //true
    /^[a-zA-Z]{5,20}$/ instanceof RegExp, //true
    new Error() instanceof Error //true
)

基本数据类型中:Number,String,Boolean。字面量值不可以用instanceof检测,但是构造函数创建的值可以,如下:

var num = new Number(123);  
var str = new String('dsfsf');
var boolean = new Boolean(false);

还需要注意null和undefined都返回了false,这是因为它们的类型就是自己本身,并不是Object创建出来它们,所以返回了false。

3 .constructor

constructor是prototype对象上的属性,指向构造函数。根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的。

如果输出一个类型的实例的constructor,就如下所示:

console.log(new Number(123).constructor)
//ƒ Number() { [native code] }

 可以看到它指向了Number的构造函数,因此,可以使用num.constructor==Number来判断一个变量是不是Number类型的。

var num  = 123;
var str  = 'abcdef';
var bool = true;
var arr  = [1, 2, 3, 4];
var json = {name:'wenzi', age:25};
var func = function(){ console.log('this is function'); }
var und  = undefined;
var nul  = null;
var date = new Date();
var reg  = /^[a-zA-Z]{5,20}$/;
var error= new Error();

function Person(){
  
}
var tom = new Person();

// undefined和null没有constructor属性
console.log(
    tom.constructor==Person,
    num.constructor==Number,
    str.constructor==String,
    bool.constructor==Boolean,
    arr.constructor==Array,
    json.constructor==Object,
    func.constructor==Function,
    date.constructor==Date,
    reg.constructor==RegExp,
    error.constructor==Error
);
//所有结果均为true

除了undefined和null之外,其他类型都可以通过constructor属性来判断类型。

4 . 使用Object.prototype.toString.call()检测对象类型

 可以通过toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为thisArg。
 

var toString = Object.prototype.toString;

toString.call(123); //"[object Number]"
toString.call('abcdef'); //"[object String]"
toString.call(true); //"[object Boolean]"
toString.call([1, 2, 3, 4]); //"[object Array]"
toString.call({name:'wenzi', age:25}); //"[object Object]"
toString.call(function(){ console.log('this is function'); }); //"[object Function]"
toString.call(undefined); //"[object Undefined]"
toString.call(null); //"[object Null]"
toString.call(new Date()); //"[object Date]"
toString.call(/^[a-zA-Z]{5,20}$/); //"[object RegExp]"
toString.call(new Error()); //"[object Error]"

这样可以看到使用Object.prototype.toString.call()的方式来判断一个变量的类型是最准确的方法。

5 .无敌万能的方法:jquery.type()

 如果对象是undefined或null,则返回相应的“undefined”或“null”。

jQuery.type( undefined ) === "undefined"
jQuery.type() === "undefined"
jQuery.type( window.notDefined ) === "undefined"
jQuery.type( null ) === "null"
如果对象有一个内部的[[Class]]和一个浏览器的内置对象的 [[Class]] 相同,我们返回相应的 [[Class]] 名字。 (有关此技术的更多细节。 )
jQuery.type( true ) === "boolean"
jQuery.type( 3 ) === "number"
jQuery.type( "test" ) === "string"
jQuery.type( function(){} ) === "function"
jQuery.type( [] ) === "array"
jQuery.type( new Date() ) === "date"
jQuery.type( new Error() ) === "error" // as of jQuery 1.9
jQuery.type( /test/ ) === "regexp"
其他一切都将返回它的类型“object”
6 . 自己也可以封装一个获取变量准确类型的函数
function gettype(obj) {
  var type = typeof obj;

  if (type !== 'object') {
    return type;
  }
  //如果不是object类型的数据,直接用typeof就能判断出来

  //如果是object类型数据,准确判断类型必须使用Object.prototype.toString.call(obj)的方式才能判断
  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');
}
三、深拷贝
1、浅拷贝与深拷贝

示意图大致如下

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

2、赋值和浅拷贝的区别

赋值:

把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

浅拷贝:

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

3、深拷贝:
(1)递归

这是最基本的,也是都需要会的方法:

/**
 * 深拷贝
 */

const obj1 = {
    age: 20,
    name: 'xxx',
    address: {
        city: 'beijing'
    },
    arr: ['a', 'b', 'c']
}

const obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
obj2.arr[0] = 'a1'
console.log(obj1.address.city)
console.log(obj1.arr[0])

/**
 * 深拷贝
 * @param {Object} obj 要拷贝的对象
 */
function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null ,或者不是对象和数组,直接返回
        return obj
    }

    // 初始化返回结果
    let result
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }

    for (let key in obj) {
        // 保证 key 不是原型的属性
        if (obj.hasOwnProperty(key)) {
            // 递归调用!!!
            result[key] = deepClone(obj[key])
        }
    }

    // 返回结果
    return result
}
(2)JSON.stringify + JSON.parse
// JSON.stringify
// 情况一: 对象内不存在undefined、symbol、function类型的属性时
// const foo = {
//     name: '张三',
//     info: {
//         age: 24
//     }
// }
// const newFoo = JSON.parse(JSON.stringify(foo))
// console.log(foo, newFoo) // { name: '张三', info: { age: 24 } } { name: '张三', info: { age: 24 } }
// foo.info.age = 25
// console.log(foo, newFoo) // { name: '张三', info: { age: 25 } } { name: '张三', info: { age: 24 } }

// 情况二:当对象内存在undefined、symbol、function类型的属性时,在序列化过程中会被忽略。
// 当属性为NaN 和 Infinity 格式的数值及 null 都会被当做 nul
// const foo = {
//   name: "张三",
//   age: undefined,
//   height: 183,
//   gender: Symbol("男"), //
//   say: () => {},
//   aa: NaN,
// };
// const newFoo = JSON.parse(JSON.stringify(foo));
// console.log(foo, newFoo); // {aa: NaN, age: undefined, gender: Symbol(男), height: 183, name: "张三"} { {name: '张三', height: 183, aa: null} }

// JSON.stringify() 第二个参数为可选参数,可以是一个函数或者数组,
// 当是数组时表示需要被转化的属性列表,但undeinfed,symbol,function类型的属性依然会被忽略

// const newFoo = JSON.parse(JSON.stringify(foo, ['name', 'age', 'gender', 'say', 'aa']));
// console.log(newFoo) // {name: '张三', aa: null}
(3)lodash
const _ = require('lodash')
const foo = {
    name: '张三',
    info: {
        age: 24
    }
}
const newFoo = _.cloneDeep(foo);
foo.info.age = 25
console.log(foo, newFoo) // { name: '张三', info: { age: 25 } } { name: '张三', info: { age: 24 } }
(4)Object.assgin
注意:只有当对象中没有嵌套对象时,才可以实现深拷贝
const foo = {
    name: '张三',
    age: 24
}
const newFoo = Object.assign({}, foo)
foo.age = 25
console.log(foo, newFoo) // {name: '张三', age: 25} {name: '张三', age: 24}

// 对象中有内嵌的对象时
const foo = {
    name: '张三',
    info: {
        age: 24
    }
}
const newFoo = Object.assign({}, foo)
foo.info.age = 25
console.log(foo, newFoo) // { name: '张三', info: { age: 25 } } { name: '张三', info: { age: 25 } }
(5)structuredClone
const foo = {
    name: '张三',
    info: {
        age: 24
    }
}
const newFoo = structuredClone(foo) // 
foo.info.age = 25
console.log(foo, newFoo) // { name: '张三', info: { age: 25 } } { name: '张三', info: { age: 24 } }

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值