总结对象相关知识
目录
- 对象的特性
- 定义对象
- 1. 简单
- 2. 高级
- 属性方法
- `setter` 和 `getter`
- 属性描述符
- 内存
- 原型
- `Object`方法
- 1. `Object.create(proto, [propertiesObject])`
- 2. `Object.keys(obj)`
- 3. `Object.values(obj)`
- 4. `Object.entries(obj)`
- 5. `Object.fromEntries(iterable)`
- 6. `Object.assign(target, ...sources)`
- 7. `Object.freeze(obj)`
- 8. `Object.seal(obj)`
- 9. `Object.getOwnPropertyDescriptor(obj, prop)`
- 10. `Object.getOwnPropertyDescriptors(obj)`
- 11. `Object.setPrototypeOf(obj, prototype)`
- 12. `Object.getPrototypeOf(obj)`
- 13. `Object.defineProperty(obj, prop, descriptor)`
- 14. `Object.defineProperties(obj, props)`
- `Proxy / Reflect`
- 对象的操作
- 简写
- 访问
- 修改
- 添加
- 删除
- 解构
- 判断
- 遍历
- 比较
- 拷贝
- 面向对象
- 封装
- 继承
- 多态
对象的特性
对象类型是一种存储键值对(key-value
)的复杂的数据类型,在JavaScript
中,对象由属性和方法(在对象中的函数称为方法)组成
其中key
是字符串(也叫做属性名, ES6
后可以是Symbol
类型)
其中value
是值可以是任意类型
定义对象
1. 简单
- 使用字面量:
const person = { name: "John", age: 30 }
- 使用
new Object()
:
const person = new Object(); person.name = "John";
- 使用
Object.create()
:
const person = Object.create({name: "John"}); person.age = 30
2. 高级
- 使用工厂函数创建
- 使用构造函数创建
- 使用
ES6
的class
创建
具体学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/141086597?spm=1001.2014.3001.5501
属性方法
在JavaScript
中,对象由属性和方法组成
- 属性是对象的状态或数据,可以是任何有效的
JavaScript
值,包括基本类型(如字符串、数字、布尔值等)和复杂类型(如对象、数组、函数等) - 方法是对象的行为或功能,通常是对象属性的一种特殊类型,其值是函数。方法可以访问和操作对象的其他属性
setter
和 getter
在 JavaScript
对象中,可以通过定义 getter
和 setter
方法来控制属性的访问和修改。这些方法是特殊的函数,getter
用于获取属性值,setter
用于设置属性值。通过它们开发者可以在属性被访问或修改时执行一些额外的逻辑
-
使用字面量定义
const person = { _age: 0, // Getter get age() { return this._age; }, // Setter - 校验值 set age(value) { if (value < 0) { console.log("年龄不能为负数!"); } else { this._age = value; } } }; person.age = -5; // 输出: 年龄不能为负数! console.log(person.age); // 输出: 0 person.age = 30; console.log(person.age); // 输出: 30
-
使用
Object.defineProperty
定义const person = { firstName: "John", lastName: "Doe" }; Object.defineProperty(person, 'fullName', { // 定义 getter get: function() { return `${this.firstName} ${this.lastName}`; }, // 定义 setter set: function(name) { const parts = name.split(" "); this.firstName = parts[0]; this.lastName = parts[1]; } }); console.log(person.fullName); // => "John Doe" person.fullName = "Jane Smith"; console.log(person.fullName); // => "Jane Smith"
属性描述符
每个属性和方法都有一个属性描述符,它描述了该属性或方法的特性。
-
使用
Object.defineProperty
方法定义 -
使用
Object.defineProperties()
方法直接在一个对象上定义 多个 新的属性或修改现有属性,并且返回该对象 -
使用
Object.getOwnPropertyDescriptor
方法检查属性描述符
属性描述符包括两种:
注意: 在定义对象属性时,不能同时定义 存取属性(accessor properties
)和 数据属性(data properties
),两者是互斥的
-
数据属性描述符用于描述对象的普通属性
-
value
:属性的值,默认为undefined
-
writable
:属性的值是否可以被修改,使用对象字面量创建属性时默认为true
,使用Object.defineProperty
方法创建属性时默认为false
-
-
存取属性描述符通过
getter
和setter
方法允许在访问或设置属性值时执行特定的逻辑-
get
:获取属性值时会执行的函数,默认为undefined
-
set
:设置属性值时会执行的函数,默认为undefined
-
-
既是数据属性描述符又是存取属性描述符:
-
enumerable
:属性是否可通过for...in
循环或Object.keys
枚举,使用对象字面量创建属性时默认为true
,使用Object.defineProperty
方法创建属性时默认为false
-
configurable
:表示属性描述符是否可以被修改或删除,使用对象字面量创建属性时默认为true
,使用Object.defineProperty
方法创建属性时默认为false
-
/* 字面量创建属性 */
var obj = {
name: "obj",
age: 18,
height: 188,
};
delete obj.height; // 可删
console.log(obj); // {name: 'obj', age: 18}
obj.age = 28; // 可写
console.log(obj.age); // 28
console.log(Object.keys(obj)); // 可枚举 ['name', 'age']
/* 默认为false */
Object.defineProperty(obj, "school", {
value: "清华",
});
delete obj.school; // 不报错但删除不成功
obj.school = "北大"; // 不报错但修改不成功
console.log(Object.keys(obj)); // 不可枚举 ['name', 'age']
console.log(obj); // {name: 'obj', age: 28, school: '清华'}
/* 数据属性描述符 */
Object.defineProperty(obj, "address", {
value: "北京",
writable: true,
enumerable: true,
configurable: true,
});
console.log(Object.getOwnPropertyDescriptor(obj, "address")); // {value: '北京', writable: true, enumerable: true, configurable: true}
console.log(obj.address); // 北京
obj.address = "上海";
console.log(obj.address); // 上海
console.log(Object.keys(obj)); // ['name', 'age', 'address']
delete obj.address;
console.log(Object.keys(obj)); // ['name', 'age']
/* 存取属性描述符 */
var address = "深圳";
Object.defineProperty(obj, "address", {
enumerable: true,
configurable: true,
set(value) {
console.log("set");
address = value; // 不写会默认为undefined
},
get() {
console.log("get");
return address; // 不写会默认为undefined
},
});
obj.address = "香港"; // 会执行set
console.log(obj.address); // 会执行get 香港
/* 给多个属性创建描述符 */
Object.defineProperties(obj, {
sex: {
enumerable: true,
value: "女",
},
birthday: {
get() {
return "2000-06-08";
},
},
});
console.log(Object.getOwnPropertyDescriptors(obj)); // {......, birthday: {set: undefined, enumerable: false, configurable: false, get: ƒ } }
console.log(Object.keys(obj)); // ['name', 'age', 'address', 'sex']
console.log(obj.birthday); // 2000-06-08
内存
对象类型占据的空间是在堆内存中分配的,它的保存方式是在变量中保存对象的引用,也被称为引用类型,具体学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/141055268?spm=1001.2014.3001.5501
原型
具体学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/141104727?spm=1001.2014.3001.5501
JavaScript
当中每个对象都有一个特殊的内置属性 [[prototype]]
,这个特殊的对象可以指向另外一个对象
不管如何创建只要是对象都会有这样的一个内置属性,那么这个对象有什么用呢?
-
当我们通过引用对象的属性
key
来获取一个value
时,它会触发[[Get]]
的操作 -
首先检查该对象是否有对应的属性,如果有的话就使用它
-
如果对象中没有该属性,那么会访问对象
[[prototype]]
内置属性指向的对象里的属性
如何获取原型?
-
方式一:通过对象的
__proto__
属性可以获取到(但是这个是早期浏览器自己添加的,虽然大多数现代浏览器都支持__proto__
,但它不是标准属性,应该尽量避免直接使用) -
方式二:通过
Object.getPrototypeOf
方法获取
Object
方法
Object
构造函数提供了一些静态方法用于操作和管理对象
1. Object.create(proto, [propertiesObject])
-
创建一个新对象,使用现有对象作为新创建对象的原型
-
proto
:新创建对象的原型对象。如果传入null
,则创建的对象将没有原型 -
propertiesObject
(可选):为新对象定义属性,其形式与Object.defineProperties()
相同
var obj = Object.create({name: 'johan', age: 23}) // obj 继承了属性name 和 age
var obj2 = Object.create(null) // obj2 不继承任何属性和方法
var obj3 = Object.create(Object.prototype) // 与 {} 和 new Object() 一个意思
var obj4 = Object.create({}, {
property1: {
value: true,
writable: true
}
}) // 第二个参数与 Object.defineProperties() 一致
2. Object.keys(obj)
返回一个包含对象自身可枚举属性名的数组
var person = {
name: "nihao",
age: 18,
};
console.log(Object.keys(person)); // ['name', 'age']
3. Object.values(obj)
返回一个包含对象自身可枚举属性值的数组
var person = {
name: "nihao",
age: 18,
};
console.log(Object.values(person)); // ['nihao', 18]
4. Object.entries(obj)
返回一个包含对象自身可枚举属性的键值对数组
var person = {
name: "nihao",
age: 18,
};
console.log(Object.entries(person)); // [['name', 'nihao'], ['age', 18]]
5. Object.fromEntries(iterable)
它的作用是与 Object.entries()
相反,Object.entries()
把对象转换为键值对数组,而 Object.fromEntries()
则将键值对数组还原为对象
-
iterable:一个可迭代的对象,其中每个元素是一个含有两个元素的数组,表示键值对(如
[[key1, value1], [key2, value2], ...]
)。 -
返回值:一个新对象,其中包含了从给定的键值对创建的属性
用法:
- 将键值对数组转换为对象
- 将
Map
转换为对象 - 用
Object.entries()
和Object.fromEntries()
配合使用 - 过滤对象属性: 可以通过
Object.entries()
转换成数组后进行操作,再用Object.fromEntries()
转换回对象
const entries = [['name', 'Alice'], ['age', 25], ['city', 'New York']];
const obj = Object.fromEntries(entries);
console.log(obj);
// 输出: { name: 'Alice', age: 25, city: 'New York' }
const map = new Map([['name', 'Bob'], ['age', 30], ['city', 'San Francisco']]);
const objFromMap = Object.fromEntries(map);
console.log(objFromMap);
// 输出: { name: 'Bob', age: 30, city: 'San Francisco' }
const person = { name: 'Charlie', age: 28 };
const entries = Object.entries(person); // 将对象转换为键值对数组
const newObj = Object.fromEntries(entries); // 再将键值对数组转换回对象
console.log(newObj);
// 输出: { name: 'Charlie', age: 28 }
const person = { name: 'David', age: 35, city: 'Miami' };
const filteredObj = Object.fromEntries(
Object.entries(person).filter(([key, value]) => key !== 'age')
);
console.log(filteredObj);
// 输出: { name: 'David', city: 'Miami' }
6. Object.assign(target, ...sources)
将一个或多个源对象的所有可枚举属性复制到目标对象
-
浅拷贝:
Object.assign()
进行的是浅拷贝,即拷贝的是属性的引用,如果属性值是对象,则拷贝的仅仅是对象的引用,而不是深拷贝。 -
枚举属性:只会拷贝可枚举的属性(
enumerable
),不可枚举的属性不会被拷贝。 -
覆盖属性:如果目标对象中已有相同属性,源对象的属性将覆盖目标对象的原属性。
-
继承属性不拷贝:只会拷贝对象自身的属性,继承的属性不会被拷贝。
-
不可拷贝
Symbol
:无法拷贝Symbol
属性。
var baz1 = {
name: "baz1",
age: 18,
};
const s = Symbol("s");
var baz2 = {
name: "bar2",
height: 188,
[s]: "symbol",
};
Object.defineProperty(baz2, "school", {
value: "清华",
enumerable: false,
});
// 这里会打印Symbol是因为谷歌有一个特殊行为,它会显示对象的所有属性,包括 `Symbol` 属性,尽管这些属性在遍历时(如 `Object.keys()` 或 `for...in`)是不可枚举的
console.log(Object.assign(baz1, baz2)); // {name: 'bar2', age: 18, height: 188, Symbol(s): 'symbol'}
console.log(Object.keys(baz1)); // ['name', 'age', 'height']
7. Object.freeze(obj)
冻结一个对象,冻结的对象不能添加新属性,不能删除现有属性,也不能修改现有属性的值
const obj = {
name: "小小",
age: 18,
height: 188,
};
Object.freeze(obj);
delete obj.name;
obj.name = "obj";
console.log(obj.name); // 小小
8. Object.seal(obj)
密封一个对象,密封的对象不能添加新属性或删除现有属性,但可以修改现有属性的值
const obj = {
name: "小小",
age: 18,
};
Object.seal(obj);
obj.height = 188;
console.log(obj.height); // undefined
delete obj.age;
console.log(obj.age); // 18
obj.name = "obj";
console.log(obj.name); // obj
9. Object.getOwnPropertyDescriptor(obj, prop)
返回一个对象,获取对象的属性描述符
const obj = {
name: "小小",
age: 18,
};
console.log(Object.getOwnPropertyDescriptor(obj, "name")); // {value: '小小', writable: true, enumerable: true, configurable: true}
Object.defineProperty(obj, "age", {
enumerable: false,
configurable: false,
});
console.log(Object.getOwnPropertyDescriptor(obj, "age")); // {value: 18, writable: true, enumerable: false, configurable: false}
10. Object.getOwnPropertyDescriptors(obj)
获取其所有自身属性描述符的对象,如果没有属性,则可能为空对象
const obj = {
name: "小小",
age: 18,
};
console.log(Object.getOwnPropertyDescriptors(obj));
/* log: {
name: {value: '小小', writable: true, enumerable: true, configurable: true},
age: {value: 18, writable: true, enumerable: true, configurable: true}
}
*/
const obj1 = {};
console.log(Object.getOwnPropertyDescriptors(obj1)); // {}
11. Object.setPrototypeOf(obj, prototype)
设置对象的原型(内部 [[Prototype]]
属性)
const obj = {
name: "obj",
age: 18,
};
const baz = {
name: "baz",
};
// baz 对象的原型变成了 obj,即 baz 继承了 obj
Object.setPrototypeOf(baz, obj);
console.log(baz.age); // 18
12. Object.getPrototypeOf(obj)
返回指定对象的原型(内部 [[Prototype]]
属性的值)
const obj = {
name: "obj",
age: 18,
};
const baz = {
name: "baz",
};
// baz 对象的原型变成了 obj,即 baz 继承了 obj
Object.setPrototypeOf(baz, obj);
console.log(Object.getPrototypeOf(baz)); // {name: 'obj', age: 18}
13. Object.defineProperty(obj, prop, descriptor)
在对象上定义一个新属性或修改现有属性,并返回该对象
const obj = {
name: "obj",
age: 18,
};
Object.defineProperty(obj, "address", {
value: "北京",
writable: true,
enumerable: true,
configurable: true,
});
console.log(Object.getOwnPropertyDescriptor(obj, "address")); // {value: '北京', writable: true, enumerable: true, configurable: true}
console.log(obj.address); // 北京
obj.address = "上海";
console.log(obj.address); // 上海
console.log(Object.keys(obj)); // ['name', 'age', 'address']
14. Object.defineProperties(obj, props)
在对象上定义多个属性
const obj = {
name: "obj",
age: 18,
};
Object.defineProperties(obj, {
sex: {
enumerable: true,
value: "女",
},
birthday: {
get() {
return "2000-06-08";
},
},
});
console.log(obj.sex) // 女
console.log(obj.birthday) // 2000-06-08
Proxy / Reflect
具体学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/142111056?spm=1001.2014.3001.5501
对象的操作
简写
const name = "哈哈哈";
const age = 18;
const height = 188;
const key = age + height;
const baz = {
// 属性简写
name,
age,
height,
[key]: "hello",
// 方法简写
running() {
console.log("running");
},
["address" + key]() {},
};
baz.running(); // running
console.log(baz);
// {206: 'hello', name: '哈哈哈', age: 18, height: 188, running: ƒ, address206: ƒ}
访问
console.log(obj.name);
// 使用点操作符
console.log(obj["age"]);
// 使用方括号操作符
修改
obj.age = 26;
obj["age"] = 26;
添加
obj.job = "Engineer";
obj["job"] = "Engineer";
删除
delete obj.job;
delete obj["job"];
解构
- 按
Key
赋值,没有顺序 - 可以重命名
- 可以有默认值
- 解构一个,剩余内容放对象
const bar = {
type: "student",
name: "哈哈",
age: 18,
address: "北京",
school: "清华",
score: undefined,
};
const { name } = bar;
const { age, address } = bar;
const { school: reschool } = bar;
const { size = 6 } = bar;
const { score: rescore = 800 } = bar;
const { type, ...other } = bar;
console.log(type, other); // student {name: '哈哈', age: 18, address: '北京', school: '清华', score: undefined}
console.log(name, age, address, rescore, size); // 哈哈 18 北京 800 6
判断
-
in
判断某个属性是否在某个对象或者对象的原型上console.log("name" in obj); // true
-
obj.hasOwnProperty(prop)
判断对象自身(不包括原型链上的属性)是否具有特定属性console.log(obj.hasOwnProperty("age")); // true
-
instanceof
它通常用于判断一个对象是否是某个构造函数的实例- 检测对象的原型链中是否存在构造函数的
prototype
属性 instanceof
运算符会沿着对象的原型链逐层检查,直到找到与构造函数的prototype
属性相同的原型- 如果找到了,则返回
true
,如果到达原型链的末尾仍然没有找到,则返回false
。 object instanceof constructor
- 检测对象的原型链中是否存在构造函数的
-
isPrototypeOf
用于检查一个对象是否存在于另一个对象的原型链上。与instanceof
类似,它可以用于确定对象之间的继承关系,但它是从原型对象的角度出发的-
isPrototypeOf
方法检查的是对象的原型链,查对象是否继承自某个构造函数的原型 -
在一些复杂的继承结构中,
isPrototypeOf
可以帮助确认对象的实际类型 -
prototypeObj.isPrototypeOf(object)
-
class Man {}
class Son extends Man {}
var m = new Man();
var s = new Son();
const bar = {
type: "student",
name: "哈哈",
age: 18,
address: "北京",
school: "清华",
score: undefined,
};
console.log("name" in bar); // true
console.log(bar.hasOwnProperty("age")); // true
console.log(bar instanceof Man, m instanceof Man); // false true
console.log(Man.prototype.isPrototypeOf(s)); // true
遍历
对象是不可迭代的
-
使用
for...in
语句遍历对象的所有可枚举属性var person = { name: 'nihao', age: 18 } for (var key in person) { if (person.hasOwnProperty(key)) { console.log(key + ": " + person[key]); } }
-
使用
Object.keys(obj)
、Object.values(obj)
、Object.entries(obj)
var person = { name: "nihao", age: 18, }; console.log(Object.keys(person)); // ['name', 'age'] console.log(Object.values(person)); // ['nihao', 18] console.log(Object.entries(person)); // [['name', 'nihao'], ['age', 18]] console.log(Object.ownKeys(person)); // [['name', 'nihao'], ['age', 18]] Object.keys(person).forEach((f) => { console.log(f + ": " + person[f]); // name: nihao age: 18 });
比较
在JavaScript
中,对象的比较主要有以下几种方式:
- 引用比较:这是默认的比较方式,两个对象被认为相等,仅当它们引用同一个对象时
const obj1 = { name: 'Alice' }; const obj2 = { name: 'Alice' }; console.log(obj1 === obj2); // 输出: false console.log(obj1 === obj1); // 输出: true
- 浅比较:比较两个对象的每个直接属性,属性值必须相等
function shallowEqual(obj1, obj2) { const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); if (keys1.length !== keys2.length) { return false; } return keys1.every(key => obj1[key] === obj2[key]); } const obj1 = { name: 'Alice', age: 25 }; const obj2 = { name: 'Alice', age: 25 }; const obj3 = { name: 'Alice', age: 30 }; console.log(shallowEqual(obj1, obj2)); // 输出: true console.log(shallowEqual(obj1, obj3)); // 输出: false
- 深比较:递归比较两个对象的所有属性,确保所有嵌套属性都相等
function deepEqual(obj1, obj2) { if (obj1 === obj2) { return true; } if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) { return false; } const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); if (keys1.length !== keys2.length) { return false; } for (let key of keys1) { if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) { return false; } } return true; } const obj1 = { name: 'Alice', details: { age: 25, city: 'Won' } }; const obj2 = { name: 'Alice', details: { age: 25, city: 'Won' } }; const obj3 = { name: 'Alice', details: { age: 30, city: 'Won' } }; console.log(deepEqual(obj1, obj2)); // 输出: true console.log(deepEqual(obj1, obj3)); // 输出: false
拷贝
具体学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/142286176?spm=1001.2014.3001.5501
面向对象
面向对象编程(OOP
)有三个主要特性,它们是封装、继承和多态。这些特性使得代码更模块化、更易维护,并能更好地模拟现实世界中的对象和关系
封装
封装是将数据(属性)和操作数据的方法(方法)结合在一起,并对外界隐藏内部的实现细节。通过封装,类的内部状态只能通过公开的方法访问或修改,从而保护了对象的完整性和安全性
继承
具体学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/141156347?spm=1001.2014.3001.5501
多态
什么是多态?
-
维基百科对多态的定义:多态(
polymorphism
)指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型 -
总结:不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现
那么从上面的定义来看,JavaScript
是一定存在多态的,而且到处都是多态
// 为不同数据类型的实体提供统一的接口
function add(num1, num2) {
return num1 + num2;
}
console.log(add(10, 20)); // 30
console.log(add("我是", "add")); // 我是add
// 一个单一的符号来表示多个不同的类型
let type = 123;
console.log(type); // 123
type = {
name: "type",
};
type.name = "name";
console.log(type); // {name: 'name'}
type = "type";
console.log(type); // type
type = ["t", "y", "p", "e"];
console.log(type);
多态要看我们的理解,如果按照维基百科的定义来理解,因为JavaScript
的灵活性,它到处都是多态。但我们也要理解其他严格意义的面向对象语言中, 多态的是存在如下条件的:
- 必须有继承(实现接口):子类通过继承父类或实现接口来共享某些行为
- 必须有父类引用指向子类对象:这使得即使我们使用父类类型的引用来操作对象,实际调用的依然是子类的方法(运行时多态)
class Animal {
running() {
console.log("动物 Running");
}
}
class Cat extends Animal {
running() {
console.log("Cat Runing");
}
}
class Dog extends Animal {
running() {
console.log("Dog Running");
}
}
// 参数animal必须是动物,只是js没法作强类型,animal:Animal
function handleAction(animal) {
animal.running();
}
const cat = new Cat();
const dog = new Dog();
handleAction(cat);
handleAction(dog);