简介
任何事物都可以看作一个对象,用属性描述它的特征,用方法描述它的行为,如下方代码所示:
let obj = {
name: "朝阳",
sayHi: function () {
console.log("你好!");
},
};
typeOf obj // object
变量 obj 被赋值了一个对象,它有属性 name,属性值为 朝阳
,方法为 sayHi,具有在控制台打印 你好!
的行为。
对象的储存原理
对象的值保存在堆内存中,变量 obj 内存储的是对象的引用(对象在堆内存中存储的地址)
对象的内部属性和内部方法
https://blog.csdn.net/weixin_41192489/article/details/141022779
对象的 getter 和 setter
https://blog.csdn.net/weixin_43845090/article/details/117563999
常用的对象操作
创建对象
1. 通过对象字面量创建【常用】
原型链为: Object.prototype 》 对象
// 创建空对象:自身无任何属性和方法的对象 (因空对象继承自 Object ,所以还具有一些继承来的方法和属性)
let obj1 = {};
// 创建自带属性和方法的对象
let obj2 = {
// 属性
name: "朝阳",
// 方法
sayHi: function () {
console.log("你好!");
},
};
ES6 的简写方式
- 对象名和属性名相同时,
name:name
可简写为name
- 方法的
:function
可省略不写
let name = "朝阳";
let obj2 = {
name,
sayHi() {
console.log("你好!");
},
};
ES6 开始支持动态属性和方法
即用变量作为对象的属性和方法名
let type = "book";
let obj = {
[type + "Name"]: "《朝阳传》",
[type + "Sale"]: function () {
console.log("免费领取啦!");
},
};
2. 通过构造函数创建 new
- 通过内置的构造器函数创建
原型链为: Object.prototype 》 对象
// Object 是 JS 内置的构造函数,若无参数,则创建一个空对象,效果同 {}
let obj = new Object();
new Object() 可以传入参数,参数可以是任何类型,如字符串、数字、对象等。当传入不同类型的参数时,Object() 构造函数会根据参数的类型返回相应的基本包装类型的实例(如 String、Number 等)。
- 通过自定义的构造函数创建
原型链为: Object.prototype 》 构造函数.prototype 》 对象
function Person(name) {
// 用 new 创建对象时,此处的 this 指向新创建的对象实例
this.name = name;
}
let me = new Person("朝阳");
console.log(me.name); // 控制台打印 "朝阳"
通过构造器函数创建对象的好处:可以用同一个构造器创建不同的对象实例
3. 通过 Object.create() 创建
以一个现有对象作为原型,创建一个新对象
原型链为: 现有对象的原型 》 现有对象 》 新对象
其底层实现为
Object.create = function create(proto) {
function F(){}
F.prototype = proto
return new F()
}
Object.create() 的第一个参数为原型,第二个参数(可选)为属性对象,使用方法如下:
原型链:null 》新对象
let newObj1 = Object.create(null) // 以 null 为原型创建对象, 该对象无任何属性,不会继承Object的任何东西
Object.getPrototypeOf(newObj1) // 结果为 null
newObj1.__proto__ // 因无任何属性,所以值为 undefined
原型链:Object.prototype 》新对象
Object.create(Object.prototype) // 以 Object.prototype 为原型创建对象, 与用字面量 {} 创建对象,效果相同
原型链:Object.prototype 》{} 》新对象
Object.create({}) // 以 {} 为原型创建对象
原型链:Object.prototype 》Person.prototype 》p1 》新对象
function Person(name) {
this.name = name;
}
let p1 = new Person("朝阳");
let newObj3 = Object.create(p1);
第二个参数的使用范例:
let newObj4 = Object.create(Object.prototype, {
name: {
writable: true,
value: "朝阳",
},
age: {
writable: true,
value: 35,
},
});
其效果与字面量创建对象类似
let newObj4 = {
name:"朝阳",
age:35
}
4. 通过类创建
原型链为: Object.prototype 》 类.prototype 》 对象
(类的本质上构造函数)
class Car {
constructor(model, year) {
this.model = model;
this.year = year;
}
}
const fiesta = new Car('Fiesta', '2010');
5. 通过工厂函数创建
原型链为: 由工厂函数返回的对象决定
function fac(name){
return { name:name }
}
fac('朝阳')
访问属性
// 属性为字符串时,用 .
obj.name // 得到属性值 '朝阳'
let type = 'hobby'
// 属性为变量时,用 []
obj[type]
- 访问对象中不存在的属性,返回 undefined
在对象的方法内部访问该对象的属性,可以使用this,此时 this 就是当前对象。
let me = {
name: '朝阳',
sayName: function() {
return this.name;
}
};
获取对象所有的属性 Object.keys()
let obj = {
a: 1,
b: 2
};
let keys = Object.keys(obj) // ['a', 'b']
获取对象所有的属性值 Object.values()
let obj = {
a: 1,
b: 2
};
let values = Object.values(obj) // [1, 2]
添加属性
// 属性为字符串时,用 .
obj.age = 35
let type = 'hobby'
// 属性为变量时,用 []
obj[type] = '编程'
删除属性
// 属性为字符串时,用 .
delete obj.age
let type = 'hobby'
// 属性为变量时,用 []
delete obj[type]
遍历对象 for in
for (let key in obj) {
console.log("属性名:" + key );
console.log("属性值:" + obj[key ]);
}
合并对象 Object.assign
let a = { gender: "male"}
Object.assign(a, { age: 25 })
console.log(a) // {"age":25, "gender":"male"}
- 第一个参数是发起合并的对象
- 之后的参数是被合并的对象
- 可以一次合并多个对象
let obj = { a: 1 };
Object.assign(obj, { b: 2 }, { c: 3 });
console.log(obj); // { a: 1, b: 2, c: 3 }
- Object.assign() 的返回值为合并后的对象
- Object.assign() 有副作用(会改变原对象)
拷贝对象
浅拷贝
let obj1 = {num:1}
let obj2 = obj1
此时变量 obj2 内存的是对象 {num:1}
的地址,变量 obj1 内存的也是对象 {num:1}
的地址,当变量改变时,obj1 和 obj2 都会改变。
深拷贝
为了避免变量后期改变时,对拷贝后的对象产生影响,经常需要进行深拷贝。
// 简易深拷贝 -- 存在缺陷,但对大部分简单的对象适用
let obj2 = JSON.parse(JSON.stringify(obj1));
更多深拷贝的方法见
https://blog.csdn.net/weixin_41192489/article/details/119633624
【实战】判断变量是否为空对象
if(JSON.stringify(obj)==="{}"){
// 是空对象
}
【实战】对比对象的差异
推荐使用 js 库 jiff
https://github.com/cujojs/iiff