一、数据类型
▪ 原始类型**
number、boolean、null、underfined、string、symbol
(存放于栈中,占内存小,读取快),也就是说他们的值是直接存储在变量访问的位置,值是不可变的,改变只是“指针指向的改变”,比较是值得比较
1.null、underfined
①null
▪ nulll代表空值。typeOf null为Object是js的一个bug.
▪ null类型被看做空对象指针,只有一个值,即null值,所以在用typeof操作符去检测null类型的值得时候,结果是object类型;
▪ 如果你定义了一个变量,但是想在以后把这个对象当做一个对象来使用,最好将该对象初始化为null值
②underfined
▪ underfined代表未定义。underfined和not defined区别,前者代表定义了但没有赋值,后者表示没有定义。相同点,typeOf都返回underfined.
▪ 调用函数时,应该提供的参数没有提供,该参数等于undefined;
▪ 对象没有赋值的属性,该属性的值为undefined;
▪ 函数没有返回值,默认返回undefined。
2.字面量、String()、new String()
▪ 字面量就是没有用标识符封装起来的量,是“值”的原始状态; 如const int A = 1; // A 是常量,1 是字面量
▪ String和字面量的方式都可以返回基本类型
▪ new String()返回 object
'aa' instanceof String //false
new String(a) instanceof String //true
let a=123;
String(a) //"123"
console.log(String(null)) // 输出:null
console.log(String(underfined)) // 输出:underfined
new String(a) //String {"123"}
new String(a)=='123' //true
new String(a)==='123' //false
Q1、为什么基本类型’kk’ 可以调用length或者toString()方法和属性呢?
在这里,JavaScript会将字符串的值调用new String()方法转成对象,这个对象继承了字符串的方法,并用来处理属性。一旦属性引用结束,这个临时包装对象就会销毁。
包装对象,即存取字符串,数字或布尔值的属性时临时创建的对象
null和underfined没有包装对象
/*
基本包装对象 1、String 2、number 3、Boobear
*/
var str = 'dwkdwqudu2i';
var str2 = str.charAt(0);
cconsole.log(str2);
/* 在后台的执行过程 */
var str = new String('dwkdwqudu2i');
var str2 = str.charAt(0);
str = null;//把原来的str清空为null
console.log(str2);
/*
设置原型就可以不会销毁 ,但是会污染所有的String
*/
String.porototype.name = 12;
console.log(str.name);//12
Q2、为什么2.toString()会报错?
前面的. 既可以理解为小数点,也可以理解为方法的调用。在这里,2.toString()在解释器看来其实是(2.)toString(),解决方法如下:
2..toString();
(2).toString() //加个括号
2 .toString() //加个空格
2.0.toString()
3.基本数据类型不可以添加属性和方法
let user = 'zhangsan'
user.age = 18
user.method = function () {
console.log('12345')
}
console.log(user.age) // 输出:undefined
console.log(user.method) // 输出:undefined
▪ 引用类型**
object (在JS中除了基本数据类型以外的都是对象)
(**可以动态分配内存,可以存大数据**),是保存在堆(heap)中的对象,
也就是说,存储在变量处的值是一个指针,该指针地址->指向存储对象的内存处;值是可变的,比较是引用的比较
//obj1赋值给obj2,实际上这个堆内存对象在栈内存的引用地址复制了一份给了obj2,
//但是实际上共同指向了同一个堆内存对象,所以修改obj2其实就是修改那个对象,所以通过obj1访问也能访问的到
let obj1 = new Object();
let obj2 = obj1;
obj2.name = "我有名字了";
console.log(obj1.name); // 我有名字了
二、深浅拷贝
1.浅拷贝
一层拷贝,只会拷贝所有的属性值到新的对象中,如果还是对象则拷贝地址
▪ Object.assign();
▪ Array.slice();
▪ Array.concat();
▪ […array]
2.深拷贝
▪ 序列化
JSON.stringify - >将对象序列化为字符串
JSON.parse - >从字符串反序列化对象
JSON.parse(JSON.stringify(a))
缺点:对函数、underfined和symbol序列化后会丢失
▪ 递归遍历
90%的情况都可以用浅拷贝和序列化实现,如果有特殊情况需要递归遍历。
▪ MessageChannel 消息通道
MessageChannel API允许我们创建一个新的消息通道,并通过他的两个 MessagePort 属性发送数据。
和webworker类似。数据是深拷贝,不会丢失 underfined,循环引用也可以。但是对函数和symbol还是不行
三、类型判断
1.typeof 操作符
主要用于判断数据是不是基本数据类型,但是无法判断出具体引用类型
▪ “undefined” ——如果这个值未定义;
▪ “boolean” ——如果这个值是布尔值;
▪ “string” ——如果这个值是字符串;
▪ “number” ——如果这个值是数值;
▪ “object” ——如果这个值是对象或 null ;
▪ “function” ——如果这个值是函数。
2.instanceof 操作符
用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上
这里主要的目的是用来检测引用类型
种变量判断可以检测出9种(Number、String、Boolean、Symbol(ES6)、Object 、Array 、Date 、RegExp 、Function 类型),undefined和null 不被检测为object,因为js中没有这种全局类型
//==对于number,string,boolean这三种类型,只有通过构造函数定义==,这样定义才能检测出,比如:
let num =new Number(1);
let str = new String('abc');
let bool = new Boolean(true);·
//下面这样定义是检测不出来的
let num = 1;
let str = 'abc';
let bool = true;
3.Object.prototype.toString.call()
对象的一个原生原型扩展函数,用来精确的区分数据类型
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(new Map()) //"[object Map]"
Object.prototype.toString.call(function* (){}) //"[object GeneratorFunction]"
Object.prototype.toString.call(Promise.resolve()) //"[object Promise]"
let arr2=[1,'2',[1,2],null,{'a':1}];
Array.prototype.toString.call(arr2) //1,2,1,2,,[object Object]"
Function.prototype.toString.call(function(){}) //"function (){}"
Function.prototype.toString.call({}) //Uncaught TypeError: Function.prototype.toString requires that 'this' be a Function
总结:除了Object和Array两种情况,其他类型都不支持非自身实例,通过this绑定调用该Object子类原型对象上的toString()方法,说明重写toString()方法时,限定了调用方法对象的类型,非自身对象实例不可调用。
4.toString、valueOf
①基本类型的valueOf会返回自身的原始类型,而Array、Object和Function都会返回自身,Date返回时间戳
②null和underfined没有相应的构造函数,所以无法调用toString()方法。只是基本数据类型,不能访问任何属性和方法。
let obj={a:1,b:[1,2]};
let fun=function(){};
let arr=[1,'2',null,'underfined'];
obj.valueOf(); //{a: 1, b: Array(2)}
obj.toString(); //"[object Object]"
arr.valueOf(); //[1, "2", null, "underfined"]
arr.toString(); //"1,2,,underfined"
fun.valueOf(); //function(){}
fun.toString(); //"native code"
new Date().toString() //"Tue Nov 12 2019 17:18:50 GMT+0800 (中国标准时间)"
new Date().valueOf() //1573550374068
window.toString() //"[object Window]"
let errors=new Error('kkk');
errors.valueOf() //Error: kkk
errors.toString() "Error: kkk"
//注:{}.toString() 不加()会被当成代码块,报错
{a:1}.toString() //报错 Uncaught SyntaxError: Unexpected token .
({a:1}).toString() //"[object Object]"