对象是包括属性与方法的数据类型,JS中大部分类型都是对象,如:String、Boolean、Symbol、Number、BigInt、Object、Array、RegExp、Date、Function等等。
一、对象计算
对象直接参与计算时,系统会根据计算的场景在 string/number/default 间转换。
对象内部自定义 Symbol.toPrimitive、valueOf、toString方法用来处理所有的转换场景。
- [Symbol.toPrimitive] 优先级最高,如果这个方法存在,其他两个方法将不会查找
- 如果没有[Symbol.toPrimitive]方法,则算术运算和条件比较时valueOf的优先级会高一些
- 如果没有[Symbol.toPrimitive]方法,则字符串拼接操作时toString的优先级会高一些
var o = {
/* [Symbol.toPrimitive]: function () {
return 10;
}, */
valueOf: function () {
return 100;
},
toString: function () {
return '[object Object]';
}
};
console.log(o + 1);
console.log(o == 100);
console.log(`字符串拼接${o}`);
注意: Symbol.toPrimiative是一个变量,必须加上[ ]
二、对象解构赋值
1、基本使用
解构原则:看右边,如果是对象则key相同,如果是数组,名称随意,左右类型相同。
//对象解构
var o={id:1000,title:'phone',price:999};
var {id,title,price} = o;
console.log(id,title,price);
//数组解构
var arr = ["10", "100", "1000"];
var [a, b, c] = arr;
console.log(a,b,c);
//解构练习
var obj = { id: 1, child: [1, 2, 3, { tag: 'div' }] }
var {id,child:[,,c,{tag}]} = obj;
console.log(id,c,tag);
2、默认值解构
注:解构时 : 是给原来的key值起别名,= 是给变量赋值默认值
//对象
var obj = { title: '标题' };
var { title: name, url = "http://www.baidu.com" } = obj;
console.log(name, url);
//数组
var arr=[100,1000];
var [a,b,c=1001]=arr;
console.log(a,b,c);
对象的解构赋值,多用于封装函数时,函数的形参太多,这时使用对象传入形参的形式进行解构赋值,可以不用规定形参实参的顺序,且当有利于设置默认值。
//方法1:
function fn({
id = 1,
name = "zhangsan",
age
}) {
console.log(id,name,age);
}
//方法二:
function fn(options) {
let { id = 1, name = "zhangsan", age } = options;
console.log(id, name, age);
}
//方法三:...语法
function fn(options) {
let obj = { ...options, name:"lisi", id:2 };
console.log(obj);
}
fn({ age: 18 });
三、属性管理
1、添加属性
let obj = {id:1,name:"zhangsan"};
obj.age = 18;
console.log(obj);
2、删除属性
使用delete关键字
let obj = {id:1,name:"zhangsan"};
obj.age = 18;
delete obj.name;
console.log(obj);
3、检测属性
属性:公有属性(原型和原型链上的)和私有属性(构造方法中的)
此处的公有私有区别于后端语言中的公有私有
hasOwnProperty 检差指定名称是否是当前对象的私有属性,不检测原型链上继承的属性。
in 关键词可以用来检查公有属性,先检查私有,私有中没有继续检查共有属性,他会在__proto__(原型链)中找
let obj = {id:1,name:"zhangsan"};
console.log(obj.hasOwnProperty('id'));
console.log("id" in obj);
检查当前给定属性是否为公有属性
function isPublic(obj,prop){
return obj.hasOwnProperty(prop)&&prop in obj;
}
console.log(isPublic(obj,name));
4、获取属性名
Object.getOwnPropretyNames 获取对象中所有非Symbol类型的私有属性方法,以数组的形式返回。
等同于 Object.keys
let sym=Symbol();
let obj1={id:1,name:"lisi",[sym]:'唯一值'};
console.log(Object.keys(obj1));
console.log(Object.getOwnPropertyNames(obj1));
Object.getOwnPropertySymbols 只获取Symbol的私有属性方法,以数组的形式返回
let sym=Symbol();
let obj1={id:1,name:"lisi",[sym]:'唯一值'};
console.log(Object.getOwnPropertySymbols(obj1));
结合上述两种方法遍历出对象中所有的私有属性方法
let sym = Symbol();
let obj1 = { id: 1, name: "lisi", [sym]: '唯一值' };
let keys = [...Object.getOwnPropertyNames(obj1), ...Object.getOwnPropertySymbols(obj1)];
keys.forEach(item => {
console.log(item, obj1[item]);
})
5、禁止向对象中添加属性
禁止了向对象中添加新属性,但还是可以对原有的属性进行数据修改。
在严格模式下,禁止给对象添加属性设置后,如果你还添加属性,则报错,后续代码不会执行。
语法:Object.preventExtensions()
// 'use strict'
let sym = Symbol();
let obj1 = { id: 1, name: "lisi", [sym]: '唯一值' };
Object.preventExtensions(obj1);
obj1.a = 10; //严格模式下会报错且后续代码不执行
obj1.id = 10;
console.log(obj1);
console.log(1);
6、冻结对象
冻结对象,相当于把对象定义为一个常量。
严格模式下,如果对已冻结的对象修改或添加属性则报错,后续代码不会执行。
非严格模式下,不会报错,修改或添加属性没有效果。
冻结对象 语法: Object.freeze()
检查当前对象是否为冻结对象 true/false,语法:Object.isFrozen()
// 'use strict'
let sym = Symbol();
let obj1 = { id: 1, name: "lisi", [sym]: '唯一值' };
Object.freeze(obj1);
console.log(Object.isFrozen(obj1)); //true
obj1.id=10; //严格模式下报错,终止后续代码执行
console.log(obj1);
7、修改器和获取器
修改器:set
获取器:get
调用时,不能把它们当做方法,应该当做属性。es6+语法
通过修改器和获取器,可以在对象内部对属性值进行处理。
let obj = {
//私有属性
_id: 1000,
get id() {
//写条件
return this._id > 500 ? '优秀' : this._id;
},
set id(value) {
//写条件
value = isNaN(Number(value)) ? 0 : Number(value);
this._id = value;
}
};
obj.id='300a';
console.log(obj.id);
对象属性私有化:
比如在项目的一些配置文件,不能让别人修改,设置属性的key为Symbol型,把该对象暴露在外面时,无法通过该对象的key直接获取和修改value,只能通过修改器和获取器来修改或者获取,我们在修改器和获取器中添加条件,可以保证数据的合法性。
let _idsy=Symbol('id');
let obj={
[_idsy]:1000,
get id() {
//写条件
return this[_idsy] > 500 ? '优秀' : this._id;
},
set id(value) {
//写条件
value = isNaN(Number(value)) ? 0 : Number(value);
this[_idsy] = value;
}
}
四、复制对象
Object.assign(目标,源1,源2…) 把源1,源2这些对象合并到目标对象上,并且把目标对象返回,目标对象的地址和源1、源2等地址不关联。
var o1 = { a: 1, b: 2 };
var o2 = { c: 3 };
var o3 = { a: 10, d: 4 };
Object.assign(o1, o2, o3);
o2.c = 1000; //引用地址不关联
console.log(o1);
浅拷贝:
方法1:使用Object.assign方法
var o1 = { a: 1, b: 2 };
var o2 = Object.assign({}, o1);
o1.b = 100;
console.log(o2);
方法2:for in 循环遍历
var o1 = { a: 1, b: 2 };
var o2={};
for(let prop in o1){
//是o1的私有属性则复制
if(o1.hasOwnProperty(prop)) o2[prop]=o1[prop];
};
o1.b=1000;
console.log(o2);
方法3:for of 循环
Object.entries() 这个方法能以二维数组的方式返回对象的key和value
var o1 = { a: 1, b: 2 };
var o2={};
for(let [key,value] of Object.entries(o1)){
o2[key] = value;
};
方法4:展开运算法 …语法
var o1 = { a: 1, b: 2 };
var o2={...o1};
console.log(o2);
深拷贝
function deepClone(obj){
//判断obj是不是对象
if(typeof obj !== 'object') return obj;
//判断obj是数组还是对象
let target= 'push' in obj ? [] : {};
for(let [key,value] of Object.entries(obj)){
target[key] = typeof value === 'object' ? deepClone(value) : value;
}
return target;
}
五、Object.create
创建一个空对象,基于传入的对象创建一个新对象(和传入的对象没有引用关系),将空对象的__proto__ 属性指向新创建的对象
var o = {a:1};
var o2= {b:2};
console.log(Object.create(o2));