对象也称为“散列、映射、字典、关联数组",与C、C++、Java等强类型语言不同,JavaScript是松散类型语言,对象不只有固定数量的属性,无需提前定义属性名字。
JavaScript一个设计缺陷:对象的属性名必须是字符串,采用其他数据类型作为key,JavaScript会做一件蠢事——将变量转换为string
后面语言发展中为了弥补这个缺陷出现了symbol、map、weakMap等语法
访问对象属性的语法:点、括号
let a={},b={key:'b'},c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]);// 456
console.log(a);// {[object Object]: 456}
console.log(b);// {key: "b"}
let a={},b={key:'b'},c={key:'c'};
a.b=123;
a.c=456;
console.log(a.b);// 123
console.log(a);// {b: 123, c: 456}
console.log(b);// {key: "b"}
注意:点语法or中括号都是在定义object的键,定义object的属性可以采用数值、字符串、符号为键,key会被隐式转换为string,[]语法会先计算再转换。
- 但是map可以采用JavaScript所有类型为键,map内部采用sameValueZero比较(类似===)来匹配键
let a={},b='b1';
a[b]=123;
console.log(a); // {b1: 123}
b='b2'
console.log(a); // {b1: 123}
const m =new Map();
m.set(b,123);
console.log(m); // {b2 => 123}
b='b3'
console.log(m); // {b2 => 123}
const obj={s:'s1'}
m.set(obj,123);
console.log(m); // Map {"b2" => 123, {s: "s1"} => 123}
console.log(m); // Map {"b2" => 123, {s: "s2"} => 123}
- 点语法是首选,静态。属性名是通过标识符来表示,标识符必须直接写在JavaScript程序中,不是一种数据类型,因此不能被程序操作
- 中括号动态,属性名是通过字符串来表示的,是一种JavaScript数据类型,因此可以在程序运行期间修改和创建。提供了更灵活的语法,在程序运行之前不知道属性名的情况
注意:是设置键那一刻静态的string
let addr='';
for(let i=0;i<4;i++){
addr+=customer[`address${i}`]+"\n";
}
在括号中使用属性名的字符串形式,即必须是一个计算结果为字符串的表达式
利用方括号语法实现去重
原理:对象的属性key不能重复
function noRepeat(arr){
var i=-1,
obj={},
res=[];
while(++i<arr.length){
obj[arr[i]]||res.push(arr[i]);
obj[arr[i]]=true;
}
return res;
}
console.log(noRepeat([2,2,4,3,4,3]));
下列情况中采用中括号
- 通过变量访问属性
- 属性名中包含可能会导致语法错误的字符,e.g:空格
- 属性名中包含关键字/保留字
- 以symbol为键的属性
Symbol作为对象属性
隐藏即特殊的方法访问:symbol、weakMap实现私有变量
注意理解: [symbol]是[计算属性]的真子集
// 另一个代码层级
let user = {
id: "John"
};
// 当前代码层级
let id = Symbol("id");
user[id] = 1;
console.log(( user[id] )); // 1,可以使用 Symbol 作为键来访问数据
console.log(( user.id )); // John ,可以使用 Symbol 作为键来访问数据
Symbol值通过Symbol函数生成 Symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。 如果我们要在对象字面量 {...} 中使用 Symbol,则需要使用方括号把它括起来。
var s=Symbol("hh");
let user = { // 属于另一个代码
id: "John",
[s]:"dd"
};
console.log(typeof(( user[s] ))); // string
console.log(typeof(s)); // Symbol
user[s]="ss";
console.log(( user.s )); // undefined
console.log(( user[s] )); // ss
console.log(( user.id )); // John
BOM使用symbol:
weakMap 关联对象属性
js垃圾回收程序中对待“弱映射”中键的方式
- key必须是object或者继承自object,不能为空
- 保证只有通过键对象的引用才能取得值
- 一旦指针key发生变化(不被引用),key对应的属性就会被回收
隐藏即特殊的方法访问:symbol、weakMap实现私有变量 weakMap的优越性在于,关联的方式不受对象属性限制,即使对象被冻结也可以实现关联一个秘密属性
var Pclass = (function(){
const aa = new WeakMap();
const mt = new WeakMap();
class Pclass {
constructor(){
this.b = 'b这是公有变量';
aa.set(this, '私有变量aa')
mt.set(this, function(){
console.log('私有方法mt')
})
}//实现了constructor
getA(){
console.log(aa.get(this));
}
getM(){
console.log(mt.get(this));
}
}
return Pclass
}())
let pc = new Pclass();
console.log(pc) // Pclass {b: "b这是公有变量"}
for (const key in pc){
console.log(key);//b
}
应用:缓存计算属性
基于一个对象执行某些耗时操作之后得出一个计算值,后续考虑到效率,会缓存这个计算值。 如果用Map建立对象和该值的连接,就会阻止对象的回收,那么采用WeapMap
使用一个私有的Symbol属性也可以实现该效果。
const m =new WeakMap();
let obj={s:'s1'}
m.set(obj,123);
console.log(m); // Map { {s: "s1"} => 123}
obj.s='s2';
console.log(m); // Map { {s: "s2"} => 123}
obj={};
console.log(m); // Map { {s: "s2"} => 123}
obj=null;
console.log(m); // Map {}
console.log(m); // Map {}
应用:关联元数据
WeakMap实例不会阻碍垃圾回收,所以特别适合关联元数据。节点变化之后,垃圾回收程序立刻释放其内存。
const wm=new WeakMap();
const loginButton=document.querySelector('#login');//id选择器
wm.set(loginButton,{disabled:true});//给节点关联一些元数据
应用:真正的私有变量
对象实例为键,私有成员的字典为值
const wm=new WeakMap();
class User{
construction(id){
this.idP=Symbol('id');//
this.setId(id);
}
setPrivate(property,value){
const privateMember=wm.get(this)||{};//查看vm中是否存在User实例对应私有变量
privateMember[property]=value;
wm.set(this,privateMember);
}
getPrivate(property){
return wm.get(this)[property];
}
setId(id){
this.setPrivate(this.idP,id);
}
getId(){
return this.getPrivate(this.idP);
}
}
const user=new User(123);
alert(user.getId());//123
user.setId(456);
alert(user.getId());//456
//并不是真正的私有
alert(wm.get(user)[user.idP]);//456
外部代码只要拿到对象实例的引用和弱映射,就可以取得私有变量。为了避免这种访问可以用一个闭包把weakMap包装起来,这样就可把弱映射与外界完全隔离开。
const User=(()=>{
const wm=new WeakMap();
class User{
construction(id){
this.idP=Symbol('id');//
this.setId(id);
}
setPrivate(property,value){
const privateMember=wm.get(this)||{};//查看vm中是否存在User实例对应私有变量
privateMember[property]=value;
wm.set(this,privateMember);
}
getPrivate(property){
return wm.get(this)[property];
}
setId(id){
this.setPrivate(this.idP,id);
}
getId(){
return this.getPrivate(this.idP);
}
}
})();
const user=new User(123);
alert(user.getId());//123
user.setId(456);
alert(user.getId());//456
//拿不到弱映射中的键,也就无法取得弱映射中对应的值。闭包私有变量
WeakSet 弱集合
给对象打标签,对象被删除自动回收。
const disabledE=new WeakSet();
const loginButton=document.querySelector('#login');//id选择器
disabledE.add(loginButton);//通过加入对应集合,打上标签
```# 条件式属性访问的语法
ES2020新增
针对访问undefined、null的属性报错
```javascript
let a={b:null};
a.b?.c.d;//不确定b是否存在
a.b?.[s];
结果是undefined,不报错
补充条件式调用
function square(x,log){
if(log){
log(x);
}
//替换为
// log?.(x);
return x*x;
}
条件式属性访问的语法
ES2020新增 针对访问undefined、null的属性报错
let a={b:null};
a.b?.c.d;//不确定b是否存在
a.b?.[s];
结果是undefined,不报错
补充条件式调用
function square(x,log){
if(log){
log(x);
}
//替换为
// log?.(x);
return x*x;
}