这篇博客主要包括数据类型、深拷贝&浅拷贝、数据类型的转换、toString()方法判断数据、相等与全等的区别。如有错误,欢迎指正。
因为上次金山面试问了巨多这一块的内容,所以做个总结,零碎的知识点很多。这篇本来是上周先发在掘金的,但是掘金的排版器没有思否用着顺手,以后还是在这里更。。。(唉出场顺序太重要了,我爱思否)
数据类型
null是对象类型还是基本数据类
1.基本数据类型有 number,string,boolean,null,undefined,symbol(ES6新增的),也称原始数据类型。
2.JS有原始值类型和引用值类型
2.1 原始值类型里面存储的都是值,原始值类型是无法改变的。
2.2 引用值类型指的是由多个值构成的对象。
let str = "hello";
str[0] = "z"; //无法修改
一般情况下声明的变量是对象类型的话,会在栈内存中存放着引用地址,该地址会指向堆内存中的内容。
3.JS内存分为堆内存和栈内存,栈内存保存的都是有固定大小、空间的内容。堆内存中存储的都是不固定的内容。
- 字符串是个特例,字符串具有不变性,字符串存储在栈内存中。
4.二者区别
4.1 原始值类型保存在栈内存中,可以按值直接访问。
值与值之间是独立存在的,修改一个变量不会影响到其他变量。
var a = 10;
var b = a; //将a的值赋给b
b = 20;
console.log(a); // 10
console.log(b); // 20
① 基本数据类型赋值过程:
4.2 引用值类型存放在堆内存中,按照引用访问。
- 每新建一个新的对象,就会在堆内存中开辟一块新的空间
-
如果两个变量保存的是同一个对象引用,当通过一个变量修改属性时,另外一个也会受到影响。
var obj = new Object(); obj.name = "孙悟空"; var obj2 = obj; obj2.name = "猪八戒"; console.log(obj.name); //猪八戒 console.log(obj2.name); //猪八戒
② 引用数据类型赋值过程:
5.如果两个对象一模一样,但也还是两个对象
- 当比较两个基本数据类型的值时,就是比较数值
- 比较两个引用数据类型时,比较的是对象的内存地址,对象相同,但地址不同,会返回false
6.null不是引用数据类型,是基本数据类型。
深拷贝、浅拷贝
1.深拷贝: 修改新变量的值不会影响原有变量的值,默认情况下基本数据类型都是深拷贝
let num1 = 123;
let num2 = num1;
num2 = 666; // 修改新变量的值
console.log(num1); //123
console.log(num2); /666
2.浅拷贝:修改新变量的值会影响原有的变量的值,默认情况下引用类型都是浅拷贝。
class Person{
name = "jjj";
age = 23;
}
let p1 = new Person();
let p2 = p1;
p2.name = "wdhj"; //修改p2的值影响了p1的值
console.log(p2.name); //wdhj
console.log(p1.name); //wdhj
如何实现引用数据类型的深拷贝(上)
当引用数据类型中保存的都是基本数据类型时
以下三种方法并不能实现真正意义上的深拷贝,因为对象中保存的都是基本数据类型
1.方法一(不好)
浅拷贝的原因:多个变量指向了同一块存储空间,想要实现深拷贝,可以让每个变量分别指向自己的存储空间
class Person{
name = "mss";
age = 18;
}
let p1 = new Person();
let p2 = new Person();
//因为p1里面的属性都是基本数据类型,所以可以取出p1的name赋值给p2
p2.name = p1.name;
p2.age = p1.age;
p2.name = "mhf";
p2.age = 20;
console.log(p1.name); //mss
console.log(p2.name); //mhf
2.方法二(不好)
利用循环来遍历每个属性
class Person{
name = "mss";
age = 18;
}
let p1 = new Person();
let p2 = new Person();
for(let key in p1){ //利用遍历来赋值每个属性
p2[key] = p1[key];
}
p2.name = "mhf";
console.log(p1.name);//mss
console.log(p2.name);//mhf
3.方法三(重点)
Object的assign()方法接收两个参数,代表含义是将p1中的所有属性拷贝到p2中
class Person{
name = "mss";
age = 18;
}
let p1 = new Person();
let p2 = new Person();
Object.assign(p2,p1);
p2.name = "mhf";
console.log(p1.name);//mss
console.log(p2.name);//mhf
如何实现引用数据类型的深拷贝(下)
当引用数据类型中保存的都是引用数据类型时
1.方法一:JSON.parse(JSON.stringify(obj))
2.方法二:手写深拷贝
数据类型转换
其他类型 ---> 数值类型
1.Number(常量或者变量)
1.1 注意:
- Number()无法转换“数字+字母”组合,会转换为NaN,
- undefined也转换为NaN
let str = Number('123');
console.log(str); //123
console.log(typeof str); //number
1.2 转换规则
- 纯数字字符串 --> 数字
- 字符串中有非数字内容 --> NaN
- undefined --> NaN
- 空字符串/空格 --> 0
- false --> 0
- Null --> 0
- true --> 1
2.数学运算符中的“+”、“-”
2.1 注意:
- “+”、“-”都可以将其他类型转换为数值类型,但是“-”会改变数值的正负性
- “+”、“-”底层上调用了Number()函数
- “+”、“-”无法转换“数字+字母”组合
- 任何值做“-”、“*”、“/”运算都会自动转换为Number类型
let str = "123";
let num = +str;
console.log(num); //123
console.log(typeof num); //number
3.parseInt(字符串)和parseFloat(字符串)
3.1 注意:
- parseInt()和parseFloat()都会从左至右提取数值,一旦遇到非数值就会停止,返回NaN
- parseInt()和parseFloat()都会将传入的数据先转为string类型,再操作
let res = true;
// let res = "true";
let num = parseInt(res); //把true当做字符串处理,没有遇到数字所以返回NaN
console.log(num); //NaN
其他类型 ---> 字符串类型
1.toString()
Number、Boolean类型可调用toString()转换为String类型
格式:变量名称.toString()
注意:
- toString()是对拷贝的数据进行转换,因此不会影响原有的数据
- 不能使用常量直接调用toString()方法
- null和undefined不可以调用toString()方法
let value = 123;
let str = value.toString();
console.log(str); //123
console.log(typeof str); //string
console.log((3).toString()); //影响原有的数据
2.String()方法
格式: String(常量or变量)
注意:
- String(变量or常量)可以转换常量,因为是根据传入的值重新生成一个新的值,并不会修改原有的值
- 常量可以直接使用String()方法
- String()可以用来转换null,undefined
let res = String(3);
console.log(res); //3
console.log(typeof res); //string
let str = String(undefined);
console.log(str); //undefined
console.log(typeof str); //string
3.常量/变量 + “”
利用 常量/变量 加上一个空字符串来转换,本质上是调用了String()函数, 不会修改原有的值
let value = 123;
let str = value + '';
console.log(str); //123
console.log(typeof str); //string
其他类型 ---> 布尔类型
Boolean(变量or常量)
注意:
- 只要字符串中有内容就会转换为true,只要字符串的没有内容才会转换false
- NaN、0会转换为false
- undefined、null会转换为false
总结:
空字符串/0/NaN/undefined/null会转换成false
toString方法判断数据类型
1.undefined
和null
没有toString()
方法
console.log(undefined.toString()); //报错
console.log(null.toString()); //报错
2.Number
、Boolean
、String
等包装对象的toString()
方法;本地对象的toString()
方法
console.log(Number.toString()); //function Number() { [native code] }
console.log(Boolean.toString()); //function Boolean() { [native code] }
console.log(String.toString()); //function String() { [native code] }
console.log(Function.toString()); //function Function() { [native code] }
console.log(Array.toString()); //function Array() { [native code] }
console.log(Object.toString()); //function Object() { [native code] }
console.log(RegExp.toString()); //function RegExp() { [native code] }
console.log(Date.toString()); //function Date() { [native code] }
3.整数直接跟上.toString()
形式会报错,提示无效标记,因为整数后的点会被识别为小数点
- 解决:
①用括号将整数括起来
② 或者使用两个..
③ 如果仅仅是数据转换,可以利用String()
方法
console.log(3.toString()); //报错
console.log((3).toString()); //3
console.log(3..toString()); //3
let res = String(3);
console.log(res); //3
4.Object.prototype.toString()
来进行类型识别,返回代表该对象的[object Type]字符串表示
- 注意:虽然每个对象都继承了
toString()
方法,但是不能直接使用,因为对象有可能直接重写了此方法,必须使用call()或者apply()调用
console.log(Object.prototype.toString.call("abc")); //[object String]
console.log(Object.prototype.toString.call(1)); //[object Number]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call(null)); //[object Null]
console.log(Object.prototype.toString.call(undefined)); //[object Undefined]
console.log(Object.prototype.toString.call({})); //[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(/\s/)); //[object RegExp]
console.log(Object.prototype.toString.call(new Date));//[object Date]
function Person(){}
console.log(Object.prototype.toString.call(new Person));//[object Object]
5.除了类型识别之外,还可以进行其他识别,例如识别arguments或DOM元素
(function(){
console.log(Object.prototype.toString.call(arguments));//[object Arguments]
})();
console.log(Object.prototype.toString.call(document));//[object HTMLDocument]
6.数组Array类型调用toString()
方法,返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串
console.log([1, 2, 3, 4].toString()); //1,2,3,4
console.log([].toString()); // 输出空
这时我们可以用这个思路来将多维数组转为一维数组,注意数组中的每一项变为字符串了
let arr = [1,[2,[3,4],5],6];
// console.log(arr.join(",").split(","));
console.log(arr.toString().split(","));
"==" VS "==="比较数据
"=="相等运算符
1.两个值类型相同
如果两个值类型相同,再进行三个等号(===)的比较, 与严格相等运算符完全一样。
2.两个值类型不同
在比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。类型转换规则如下:
(1) 原始类型值比较
原始类型的数据会转换成数值类型再进行比较
① 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值; false转换为0,而true转换为1console.log(true == 1); //true
② 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值console.log('123' == 123);//true
(2) 对象与原始类型值比较
如果一个操作数是对象,另一个操作数不是,则调用对象的toString()方法,用得到的基本类型值按照前面的规则进行比较
console.log([] == 0); //true
console.log([].toString()); //空
console.log({} == 0); //false
console.log( {}.toString()); //[object Object]
(3) undefined和null的比较
undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为true。
console.log(null == undefined); //true
console.log(undefined == 0); //false
console.log(null == 0); //false
console.log(undefined == undefined); //true
console.log(null == null); //true
(4) NaN的比较
如果有一个操作数是NaN,则相等操作符返回 false; 两个操作数都是NaN,相等操作符也返回 false, NaN不与任何数相等, NaN不等于NaN
console.log(NaN == 0); //false
console.log(NaN == NaN); //false
(5) 两个对象的比较
如果两个操作数都是对象,则比较它们是不是同一个对象。
① 如果两个操作数都指向同一个对象,则相等操作符返回 true;否则, 返回false
② 对于引用类型的数据,比较的是地址
console.log([] == []); //false
console.log({} == {}); //false
let a = [];
let b = a;
console.log(a == b); //true
3.多益笔试题
console.log('' == 0); //true
空串转为数字为0
console.log([] == 0); //true
[]调用toString()返回空串,再转为数字为0
console.log({} == 0); //false
{}调用toString()返回[object Object],再转为数字为NaN
4.很绕的一题
(刚刚本来写了很详细的解析,没保存,这次不想再写了。)
关键在于:
!可将变量转换成boolean类型,null、undefined、NaN以及空字符串('')取反都为true,其余都为false。
{}调用toString()返回[object Object],再转为数字为NaN
console.log([] == ![]); //true
console.log(![]); //false
相当于
console.log([] == 0); //true
console.log({} == !{}); //false
console.log(!{}); //false
相当于
console.log({} == 0); //false
"==="全等运算符
(相比起来,全等运算符就没有那么多条条框框了,相等运算符整理到吐血)
(1)如果类型不同,就一定不相等
(2)如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)
(3)如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。
(4)如果两个值都是true,或者两个值都是false,那么相等
(5)如果两个值都引用同一个对象或是函数,那么相等,否则不相等
(6)如果两个值都是null,或者两个值都是undefined,那么相等
显示转换 VS 隐式转换
显式转换的数据类型
1.将非数值转换为数值类型的函数
-
Number()
:Number(mix),可以用于任何数据类型,该函数先将mix的数据类型转换为number类型,然后再将mix的值转换为数值。
若mix的值能直接转换成数字,则直接显示。若不能则显示0或NaN. -
parseInt()
:将字符串转换为数值,不遵循四舍五入。这里的string必须是数字类型的开头字符串,一直遍历到非数值的那个字符才停止。若不是数字开头,则会显示NaN. -
parseFloat()
:将string转换为浮点数。从数字位开始看,直到非数字位结束,用法与parseInt(string)一致。
2.将其它类型的数据转换为字符串类型的函数
-
String(mix)
:将mix转换成字符串类型。该函数可以将任何数据类型的值转换为字符串。 -
toString()
:demo.toString()
将demo转换成字符串类型。demo不能是null undefined
3.将值转换成布尔值类型Boolean()
方法把undefined、null、-0、+0、NaN、''(空字符串)六个值的转化为false,其他的值全部为true。
隐式的数据类型转换
隐式类型的转换是系统进行运算时自动进行的,但是调用的方法都是显式类型转换的方法。
1.递增和递减操作符
不仅适用于整数,还可以用于字符串、布尔值、浮点数值和对象。
即先将变量通过Number()
转换成number的数据类型,然后再进行递增、递减操作。
2.正负号
不仅适用于整数,还可以用于字符串、布尔值、浮点数值和对象。
将变量通过Number()
转换成number的数据类型。
3.isNaN(变量)
执行过程为:即先将变量通过Number()
转换,再进行isNaN()
判断 。
4.加号
加法有两个作用。
- 如果没有运算过程中没有字符串时,就将变量通过`Number()转换为number类型后,再进行运算。
- 如果有字符串的话,加号两边起的就是字符串连接作用。
5.- * / % (减号,乘号,除号,取余)
运算时把数据转换成number类型后,再进行运算。
6.&& || ! (与或非运算)
将运算符两边的值转换成通过Boolean()
函数转换成布尔类型,然后再进行运算。
不同的是:&& || 返回的是比较后自身的原值,而 !运算返回的是布尔值.
7.< > <= >= == != 比较运算符
当数字和字符串比较大小时,会隐示将字符串转换成number类型进行比较。
而当字符串和字符串比较大小时,则比较的是ascii码的大小。最后返回的则是布尔值
几道很适合放这里的题
1.
var str = 1 + 2 + 'abc';
console.log(str); //3abc
2.
var a;
var b = a * 0;
//console.log(b); //NaN
//console.log(b === b); //false
if(b === b){
console.log(b * 2 + "2" - 0 +4)
}else{
console.log(!b * 2 + "2" - 0 +4)
} //26
3.
var a = 1;
//console.log(a); //1
var b = a * 0;
//console.log(b); //0
if(b === b){
console.log(b * 2 + "2" -0 + 4)
}else{
console.log(!b * 2 + "2" - 0 +4)
} //6
4.涉及精度问题
var a = 1.0 - 0.9;
if(a == 0.1){
console.log(true);
}
else{
console.log(false);
} // false
var b = 0.8 - 0.7;
if(a == b){
console.log(true);
}else{
console.log(false);
} // false
本文参考链接:
toString()方法的讨论参考到了这篇:
https://www.cnblogs.com/xiaoh...
==&===的比较参考了这两篇:
http://javascript.ruanyifeng.com/grammar/operator.html#toc6
https://blog.csdn.net/magic_xiang/article/details/83686224
参考图片来自:
博主:cc_ccc
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。