总结对象相关知识

对象的特性

对象类型是一种存储键值对(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. 高级

  • 使用工厂函数创建
  • 使用构造函数创建
  • 使用ES6class创建

具体学习这篇文章:https://blog.csdn.net/qq_45730399/article/details/141086597?spm=1001.2014.3001.5501

属性方法

JavaScript中,对象由属性和方法组成

  • 属性是对象的状态或数据,可以是任何有效的JavaScript,包括基本类型(如字符串、数字、布尔值等)和复杂类型(如对象、数组、函数等)
  • 方法是对象的行为或功能,通常是对象属性的一种特殊类型,其值是函数。方法可以访问和操作对象的其他属性

settergetter

JavaScript 对象中,可以通过定义 gettersetter 方法来控制属性的访问和修改。这些方法是特殊的函数,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

  • 存取属性描述符通过 gettersetter 方法允许在访问或设置属性值时执行特定的逻辑

    • 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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值