1、JS数据类型:
原始类型:存放在栈中,占内存小,读取快
- boolean
- null
- undefined
- number
- string
- symbol
引用类型:可以动态分配内存,可以存大数据
- object --- 引用类型
栈:栈中存放的引用类型其实是该引用类型在堆中的地址
10 |
'abc' |
true |
0xabba |
0xabbb |
堆:堆中存放的才是真正的引用类型内容;当引用类型中嵌套引用类型时,还是存放下一层引用类型的地址
0xabba | {a: 1, b: 2} |
0xabbb | {name: 0xabbd, age: 27} |
0xabbc | [1, 2, 3, 4] |
0xabbd | ['kimi', 'bob'] |
Tips:(1)not defined属于未定义,undefined属于定义未赋值。typeof都返回undefined.
(2)js中的string是不可变的,无论你在 string 类型上调用何种方法,都是创建并返回一个新的字符串(并销毁原来的字符串)
var a = 'aaa'; c = a.toUpperCase(); c === a; // false
2、字面量:
- 字符串字面量
- 数组字面量
- 对象字面量
- 函数字面量
eg:对象字面量 - 创建对象的一种简单容易阅读的方法。
var obj = { a: 'aaa', // a是属性,'aaa'是属性值 b: 'bbb', c: 'ccc' } obj.a; // "aaa" obj['a']; // "aaa"
eg:函数字面量:由4部分组成。
- 关键词 function
- 函数名,但是可有可无。
- 包含在括号内的参数,当然参数也是可有可无的,括号不能少。
- 是一组包裹在大括号的语句块,也就是函数要执行的具体代码,当然不写代码也没问题,{} 是必须要的。
function() {}; // Uncaught SyntaxError: Function statements require a function name (function() {}); // ƒ () {} var fun = function() {}; fun; // ƒ () {} var fun = {fn: function () {}}; fun; // { fn: f() } var fun = function test() {}; fun; // ƒ test() {} var fn = function test1() { console.log(test1); }; console.log(fn); // ƒ test1() { console.log(test1); } fn(); // ƒ test1() { console.log(test1); } console.log(test1); // Uncaught ReferenceError: test1 is not defined
3、toString()、String()、new String():
- toString():返回string类型(string返回该字符串的一个副本),null和undefined没有这个方法;
- String():返回string类型,能将任何类型的值转换成字符串,在不确定转换的值是不是null和undefined时可以使用;
- + ' ':返回string类型
- new String():返回引用类型
var abc = 'abc',
str = abc.toString(),
str1 = String(abc),
str2 = new String(abc);
//判断下面输出
str === abc // true str1 === abc // true str2 === abc // false typeof str // string typeof str1 // string typeof str2 // object String(null); // "null" String(undefined); // "undefined" null + ''; // "null" undefined + ''; // "undefined"
Tips:
- 'abc'.toString()或abc.length可以直接调用的原因:只要引用字符串的属性或方法,JavaScript就会将字符串值通过new String(s)的方式转换成对象,这个对象继承了字符串的方法,并用来处理属性的作用。一旦属性引用结束,这个新创建的对象就会销毁(其实在实现上并不一定创建和销毁这个临时对象,然而整个过程看起来就是这样的)
类似代码 var a1 = new String('abc'); var a2 = a1.substring(0); a1 = null; console.log(a2); // abc
- 存取字符串,数字或布尔值的属性时创建的临时对象称作包装对象.
同字符串一样,数字和布尔值也具有各自的方法:通过Number和Boolean构造函数创建一个临时对象,这些方法的调用均来自这个临时对象;
null和undefined没有包装对象:访问它们的属性会造成一个错误
var aa = "test"; aa.len = 5; aa.len; // undefined
由于包装对象使用完毕会自己销毁,所以添加的属性也读取不到。
- 2.toString()会报错的原因:解释器将.判断成了小数点,真正解释成了(2.)toString(),因此报错。可以改成:
2..toString(); // "2" (2).toString(); // "2" 2 .toString(); // 加个空格 // "2" 2.0.toString(); // "2"
4、深浅拷贝:
- 浅拷贝:直接赋值,改变其中一个变量的值,另外一个也跟着改变
let obj1 = {name: '小明', age: 15}; let obj2 = obj1; obj2.name = '小红'; console.log(obj1); // {name: "小红", age: 15} console.log(obj2); // {name: "小红", age: 15}
- 一层拷贝:只会拷贝所有对象属性值到心得对象中。如果还是对象则拷贝地址
let a = {id: 1}; let b = {...a, c: 2}; console.log(b); // {id: 1, c: 2} console.log(a); // {id: 1} let b = Object.assign({}, a); let b = a.concat(); let b = a.splice('-'); ...
- 深拷贝:
- let b = JSON.parse(JSON.stringify(obj)) :可以满足开发过程中90%的深拷贝
let a = { q: function() { console.log(1); }, w: null, e: undefined, d: Symbol('abc') } let b = JSON.parse(JSON.stringify(a)); b; // {w: null}
缺点:对函数、undefined、Symbol序列化后会丢失
2. 递归遍历:如果有特殊情况(有函数、undefined、Symbol),需要递归遍历进行深拷贝。
3.MessageChannel:API允许我们创建一个新的消息通道,并通过它的两个MessagePort属性发送数据
和webwork相似。数据是深拷贝且不会丢失undefined,循环引用也可以。但是对函数和Symbol还是不行。
// 有undefined + 循环引用 let obj = { a: 1, b: { c: 2, d: 3 }, f: undefined } obj.c = obj.b; obj.e = obj.a; obj.b.c = obj.c; function deepCopy(obj) { return new Promise((resolve) => { const { port1, port2 } = new MessageChannel(); port2.onmessage = ev => resolve(ev.data); port1.postMessage(obj); }); } deepCopy(obj).then((copy) => { // 请记住"MessageChannel"是异步的这个前提!! let copyObj = copy; console.log(copyObj, obj); console.log(copyObj == obj); });
Test:
function test(person) { person.age = 26; person = { name: 'yyy', age: 30 } return person; } const p1 = { name: 'abc', age: 25 } const p2 = test(p1); p1; // {name: 'abc', age: 25} p2; // {name: 'yyy', age: 30} p2在调用test方法时,传入p1为参数,p1的age属性被重写。 后面代码person相当于是重新创建了一个person,返回的是新创建的
5、typeof:
- 使用typeof是可以准确判断基本数据类型的
typeof false; // boolean typeof null; // object typeof undefined; // undefined typeof 2; // number typeof NaN; // number typeof Infinity; // number typeof '2'; // string typeof Symbol('22'); // symbol typeof Date; // funtion typeof [2,2,3]; // object typeof {a:1}; // object
- typeof null ----> object,这是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。
- 对象、数组、函数都是引用类型,要较准确的判断各个引用类型就需要使用instanceof。
null instanceof Object; // false
6、准确类型的判断方法:
- 引用类型的类型校验用 instanceof
let Person = function() {}; let p1 = new Person(); p1 instanceof Person; // true let str = 'hello'; str instanceof String; // false str.__proto__ // String // str字面量有__proto__属性是因为转换成包装对象。但是本身是个基本类型,使用不了instanceof typeof str; // string let str1 = new String('hello'); str1 instanceof String; // true
- 原始类型不能直接通过instanceof判断,需要通过Symbol.hasInstance加层判断
class PrimitiverString { static [Symbol.hasInstance](x) { return typeof x === 'string'; } } console.log('hello' instanceof PrimitiverString); // true
- Object.prototype.toString.call()
Object.prototype.toString.call(2); // "[object Number]" Object.prototype.toString.call('aa'); // "[object String]" Object.prototype.toString.call(true); // "[object Boolean]" Object.prototype.toString.call(Symbol('aaa')); // "[object Symbol]" Object.prototype.toString.call(new Date()); // "[object Date]" Object.prototype.toString.call(new RegExp()); // "[object RegExp]"
7、Object.prototype.toString.call()、toString()、toString.call()
- 所有的对象都会继承Object.prototype.toString这个方法。而在每个子类都会改写这个方法。
- Array、Function的原型上都已经改写了这个方法。
- 每个对象上调用toString方法时会先调用自身的toString方法,如果找不到会沿着原型链网上找,如果一直没找到最终会找到Object.prototype.toString这个方法。
具体看每个对象调用toString方法的结果:
-
- 对象 object
var obj = {a: 1}; obj.toString(); // "[object Object]" Object.prototype.toString.call(obj); // "[object Object]"
Tips:
Object.prototype.toString()在toString方法被调用时,会执行下面步骤
-
-
获取this对象的[[Class]]属性的值(怎么看这个的取值??)
-
计算出三个字符串"[object", 第一步的操作结果Result[1], 以及"]"连接后的新字符串
-
返回第二部操作结果Result(2)。
-
[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性。在规范中,[[Class]]是这么定义的:
内部属性 描述[[Class]]一个字符串值,表明了该对象的类型。即:
1、获取对象的类名(对象类型)。2、然后将[object、获取的类名]组合并返回
Object.prototype.toString.call({}); // "[object Object]" Object.prototype.toString.call([]); // "[object Array]" Object.prototype.toString.call(function() {}); // "[object Function]" Object.prototype.toString.call(''); // "[object String]" Object.prototype.toString.call(1); // "[object Number]" Object.prototype.toString.call(true); // "[object Boolean]" Object.prototype.toString.call(null); // "[object Null]" Object.prototype.toString.call(undefined); // "[object Undefined]" // toString()方法能识别以下类型是因为引擎给它们设置好了 toStringTag 标签 Object.prototype.toString.call(); // "[object Undefined]" 相当于Object.prototype.toString.call(undefined) Object.prototype.toString.call(new Date()); // "[objectDate]" Object.prototype.toString.call(/at/); // "[object RegExp]"
-
- 自己创建的类不能识别,toString()找不到toStringTag属性时只好返回默认的Object标签
class ValidatorClass {} Object.prototype.toString.call(new ValidatorClass()); // "[object Object]" // 可以加上 toStringTag 属性让它识别 class ValidatorClass { get [Symbol.toStringTag]() { return 'Validator'; } } Object.prototype.toString.call(new ValidatorClass()); // "[object Validator]"
用Object.prototype.toString.call()判断类型可简写为toString.call()来判断
但是要注意如果已经定义了toString函数就不可以!
function toString() { console.log('qqq'); } toString(); // 'qqq' toString.call({}); // 'qqq' toString.call([]); // 'qqq'
8、toString、valueOf:
基本类型的valueOf会返回自身的原始类型,而Array、Function、Object则返回自身,Date返回时间戳
// object var obj = {a: 1}; obj.valueOf(); // {a: 1} obj.toString(); // "[object Object]" // array var arr = [1, 2]; arr.valueOf(); // [1, 2] arr.toString(); // "1, 2" // function var fun = function() {}; fun.valueOf(); // ƒ () {} fun.toString(); // "function() {}" // date var date = new Date(); date.valueOf(); // 1572592931967 date.toString(); // "Fri Nov 01 2019 15:22:01 GMT+0800 (中国标准时间)" // string var str = 'abc'; str.valueOf(); // "abc" str.toString(); // "abc" // number var num = 222; num.valueOf(); // 222 num.toString(); // "222" // boolean var bool = true; bool.valueOf(); // true bool.toString(); // "true" // null & undefined 无法调用,报错 // regExp var reg = /cat/g; reg.valueOf(); // /cat/g reg.toString(); // "/cat/g" // window var win = window; win.valueOf(); // window win.toString(); // "[object Window]" // error var err = new Error('abc'); err.valueOf(); // Error: abc at <anonymous>:1:11 err.toString(); // "Error: abc"