JavaScript数据类型
数据类型分为两种:
基本数据类型:Undefined
、Null
、Booblean
、Number
、String
、Symbol
引用数据类型:Object
、Array
、Date
、Function
、RegExp
、···
注意点1:
- Undefined表示声明变量未初始化
- Null表示一个空对象指针,空对象的引用,typeof(Null) = 'object'。
注意点2:Number
- 浮点数最高精度是17位小数点,0.1 + 0.2 = 0.30000000000000004
- NaN表示本来要返回数值的操作未返回数值的情况,Number(undefined) = NaN。NaN与任何值都不相等包括自己本身,任何关于NaN的操作都会返回NaN
- isNaN:判断一个数是否不能被转换成数值,isNaN(true) = false --> Number(true) = 1
数值转换
由三个函数可以把非数值转换成数值。Number()、parseInt()、parseFloat()
Number转换的规制:
- 1、如果是Booble值,true为1,false为0
- 2、Undefined返回NaN,Null返回0
- 3、Number('hello') = NaN,空字符串会返回0,数值字符串前面的0会清除,
parseInt转换的规制:
- 1、忽略字符串前面的第一个非空格字符,第一个字符不是数字字符或者负号,都会返回NaN
- 2、接收两个参数,第二个参数表示进制 var num = parseInt("10", 2) // 按二进制解析
!操作符
- 1、如果操作是一个对象,返回false
- 2、如果操作是一个空字符串,返回false ---> !"" = false
- 3、如果操作是0返回true,其他返回false ---> !0 = true、!1 = false
- 4、如操作时null,NaN,Undefined,返回true ---> !NaN = !Null = !Undefined = true
类型判断
千万不要使用typeof来判断对象和数组,因为这种类型都会返回object。
typeOf()是判断基本类型的Boolean,Number,symbol, undefined, String, null返回object。对于引用类型:除function,都返回object。
Object.prototype.toString.call({}) // [object Object]
Object.prototype.toString.call([]) // [object Array]
Array.isArray([]) // true
Array.isArray({}) // false
一些判断函数汇总:
- 1、hasOwnProperty() 返回布尔值,检测一个属性是否在对象本身,不检测原型链继承的属性
- 2、typeof() 基本类型的判断,null会返回NaN,除function其他的引用数据类型都会返回object
- 3、instacneof() 返回一个布尔值,判断属性是否在另外一个对象的原型链上
- 4、isProperty() 方法检测一个对象是否存在另一个对象的原型链上。
数据类型存储方式
基本数据类型:基本类型值在内存中占据固定大小,保存在栈内存中
引用数据类型:引用类型的值是对象,保存在堆内存中,然后在栈内存存储一个对象的变量标识符以及对象在堆内存中的存储地址,所以当访问一个对象的时候,先访问栈中它的地址,然后按照这个地址去堆中找到它的实际内容。对引用数据类型的操作都是操作对象的引用而不是实际的对象。
扩展:
JS的内存可以分为堆和栈
- 栈:由系统自动分配,自动回收,效率高,但容量小
- 堆:手动分配内存,并且需要手动销毁(垃圾自动回收机制),效率不如栈,但容量大
对于基本类型的复制,相互不干扰。引用类型的复制,改变值都会受到到影响
let foo = 1
let bar = foo
let foo = 233 // 修改foo变量的值并不会影响bar变量的值
console.log(foo) // -> 233
console.log(bar) // -> 1
let foo = {
name: 'leeper',
age: 20
}
let bar = foo
foo.age = 19 // 改变foo变量的值会影响bar变量的值
console.log(foo) // -> {name: 'leeper', age: 19}
console.log(bar) // -> {name: 'leeper', age: 19}
深浅拷贝
关于深浅拷贝也是面试经常遇到的问题,当遇到这类问题,我们不妨先从上面的知识开始说起。 对于基本类型的复制就是在栈内存中创建一个新的副本,而引用类型的复制则是复制了栈内的指针,两个对象最终都指向同一个对象。
- 浅拷贝:是指复制对象的时候,只对第一层键值对拷贝,只复制了对对象的引用,堆内存数据是共用的,彼此之间
相互影响
- 深拷贝:复制对象的时候,通过递归的方式把所有的属性和值都复制一遍,等同于在堆内存中复制了一套一模一样的,彼此之间
互不影响
浅拷贝
浅拷贝的一些方法:Array.slice()、Array.concat()、object.assign()
var obj = {
a: 1
}
var myObj = Object.assign({}, obj)
console.log(myObj) // {a: 1}
obj.a = 2
console.log(myObj) // {a: 1} 不会相互影响
问题,浅拷贝Array.slice、Array.concat、object.assign拷贝出来的值不会相互影响,是深拷贝吗?
对第二层的拷贝改变值还是相互影响的,举例子:
let a = [[1, 2], 3, 4];
let b = a.concat();
console.log(a === b); // -> false
a[0][0] = 0;
console.log(a); // -> [[0, 2], 3, 4]
console.log(b); // -> [[0, 2], 3, 4]
深拷贝
简单的复制一个对象值改了,另一个对象也会影响。如果想要复制一个复杂数据类型却不想影响原对象,此时就需要用到深拷贝。
let obj = {a: 1}
let obj2 = obj;
obj2.a = 2;
console.log(obj) // {a: 2}
深拷贝的一些方法:JSON.parse()和JSON.stringify()
- 1、JSON.stringify():把一个js对象序列化为一个JSON字符串
- 2、JSON.parse():把JSON字符串反序列化为一个js对象
let obj = {
name: 'leeper',
age: 20,
friend: {
name: 'lee',
age: 19
}
};
let copyObj = JSON.parse(JSON.stringify(obj))
obj.name = 'vc'
obj.friend.name = 'Jerry';
console.log(copyObj.name) // leeper
console.log(copyObj.friend.name) // lee
综上,JSON.parse()和JSON.stringify()是完全的深拷贝,拷贝出来的值是互相不影响。
var obj = {
a: 1,
b: 2,
c: undefined,
sum: function() { return a + b; }
};
var obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2);
//Object {a: 1, b: 2}
手写一个深拷贝非常的重要,下面这段代码希望大家都能理解并默写
function DeepCopy(target, source) {
if (!source || typeof source !== 'object') return
for (var key in source) {
if (source.hasOwnProperty(key)) { // 排除原型链上的属性
if (source[key] && typeof source[key] === 'object') { // 判断下层是否是object
target[key] = Array.isArray(source[key]) ? [] : {} // 判断是否是数组
DeepCopy(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!