一、JS数据类型
六种基本数据类型(原始类型):null、undefined、Number、Boolean、String、Symbol
一种引用类型:Object
二、基本数据类型与引用数据类型的区别
(1)存储方式:
基本数据类型以键值对的方式存储在栈中;复杂数据类型)----名存储在栈内存中,值存储在堆内存中,但栈内存会提供一个引用的地址指向堆内存的值。
(2)访问机制。
原始类型的值可以直接访问的,而对于引用数据类型,不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先要得到这个对象在堆内存中的地址,然后按照地址去获得这个对象的值,这就是按引用访问。
(3)两种类型的复制(深拷贝与浅拷贝)
基本数据类型:当b复制a时,栈内存会开辟一个新的内存,然后把值复制到为新变量分配的位置上,改变源数据不会影响到新的变量。
复杂数据类型:复制的是存储在栈中的指针,而这个指针的副本和原指针指向的是堆中的同一个对象。所以复制操作结束后,两个变量实际上引用的是同一个对象,因此,改变其中一个,将影响另一个。
三、Symbol
为什么要引入Symbol??
es5的对象属性名是字符串,容易造成属性名的冲突,如果有一种机制能够保证每个属性的名字都是独一无二的,就可以从根本上防止属性名的冲突。
Symbol功能类似于一种标识唯一性的ID,通常情况下,我们可以通过调用Symbol()函数来创建一个Symbol实例。
let s1=Symbol();
或者可以传入一个可选的字符串参数
let s2=Symbol('another symbol');
由于Symbol是一种基础数据类型,所以当我们使用typeof去检查它的类型的时候,会返回一个属于自己的类型"symbol"。
另外,每个Symbol实例都是唯一的,因此,当你比较两个Symbol实例的时候,将总会返回false。
let s2=Symbol('another symbol'); let s3=Symbol('another symbol'); s2===s3 //false
知识点:
(1)使用new命令会报错,这是因为生成的Symbol是一个原始类型的值,不是对象,所以不能添加属性。
(2)如果传入的参数是对象,会调用toString()方法,再转为字符串;
(3)Symbol函数的参数只是表示对当前Symbol值的描述,因此即使传入的参数相同,Symbol的返回值也是不相等的;
(4)Symbol的值不能与其他的值进行运算,Symbol可以显式地转为字符串、布尔值,但是不能转为数值。
应用场景1:使用Symbol来作为对象属性名(key)
在这之前,我们通常定义或访问对象的属性时都是使用字符串,比如以下代码:
let obj={ name:'anny', 'age':20 } obj['name'] //anny obj['age']或obj.age //20
现在,Symbol可同样用于对象的定义和访问const name=Symbol();
const name=Symbol(); const age=Symbol(); let obj={ [name]:'anny', sex:'famale' } obj[age]=18 console.log(obj[name]); //anny console.log(obj[age]); //18 console.log(obj['sex']); //famale
//注意:Symbol作为属性名时不能使用点运算符
有时候我们想要区分两个属性,其实我们并不在意这两个属性究竟是什么,我们在意的是这两个属性绝对要区分开。例如
这时候,我们不仅想要区分各个形状,因为不同的形状用不同的计算面积的公式。这里使用triangle的名字叫做“Triangle”。而事实上我们不想对triangle特意去取名,我们只想要区分triangle这个形状不同于任何其他形状,这时候就需要用到Symbol。
我们不用去给一个变量附一个字符串的值去区分他和别的变量的值不同。
属性值的遍历
注意:使用Symbol作为对象的属性后,对于属性值的遍历会有问题。Symbol不会被包含在对象自身的属性名集合中
我们通常使用Object.keys()或者for...in来枚举对象的属性值。
console.log(Object.keys(obj));//['sex'] for(let i in obj){ console.log(i); //只输出sex属性 }
由上可知,Symbol类型的key是不能通过Object.keys(obj)或者for...in来枚举的,它没有被包含在对象自身的属性名集合中。所以利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。
也正因为这一特性,当使用JSON.stringify()将对象转成JSON字符串的时候,Symbol属性也会被排除在外。
console.log(JSON.stringify(obj));//{"sex":"famale"}
那么我们怎么访问Symbol属性?
Object.getOwnPropertySymbols
方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值;
Reflect.ownKeys
方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
如果我希望使用同一个Symbol值呢?
let s1 = Symbol.for('foo'); let s2 = Symbol.for('foo'); s1 === s2 // true
(1)Symbol.for()
Symbol.for()与Symbol()两种写法都会生成新的Symbol,他们的区别是,前者会被登记在全局环境中供搜索,后者不会。
Symbol.for()类似于单例模式,首先在全局中搜索有没有以该参数为名称的Symbol值,如果有则返回该Symbol值,否则新建并返回一个以该参数为名称的Symbol值。
(2) Symbol.keyFor()方法
返回一个已创建的Symbol类型值的key,实质是检测该Symbol是否已创建。
var symbol1 = Symbol.for("Alice"); console.log(Symbol.keyFor(symbol1)); // 输出:"Alice" var symbol2 = Symbol("Alice"); console.log(Symbol.keyFor(symbol2)); // 输出:undefined