JS对象类
类
// 定义类
class Clazz {}
const Clazz = class {};
// 创建对象
new Clazz();
// 检查是否为某个类的实例
c instanceof Clazz;
属性
// 添加属性
class Clazz {
name; // 实例属性
static type; // 静态属性
}
// 访问实例属性
const c = new Clazz();
c.name;
// 访问静态属性
Clazz.type;
方法
// 添加方法
class Clazz {
fn = function() {} // 方式一
fx() {} // 方式二
static fun() {} // 静态方法
}
// 调用实例方法
const c = new Clazz();
c.fn();
c.fx();
// 调用静态类方法
Clazz.fun();
构造函数
// 添加构造函数
class Clazz {
constructor() {} // 无参构造函数
constructor(x, y) {} // 有参数构造函数
}
面向对象三大特性:封装/多态/继承
封装(安全性)
// 封装示例一
class Clazz {
#name; // 使用#标识这是一个私有属性
getName() {
return this.#name;
}
setName(name) {
this.#name = name;
}
}
// 调用 getter/setter 方法
const c = new Clazz();
c.getName(); // 调用getter
c.setName('x'); // 调用setter
// 封装示例二
class Clazz {
#name; // 使用#标识这是一个私有属性
get name() { // 使用get标识这是一个getter方法
return this.#name;
}
set name(name) { // 使用set标识这是一个setter方法
this.#name = name;
}
}
// 调用 getter/setter 方法
const c = new Clazz();
c.name; // 调用getter
c.name = 'x'; // 调用setter
多态(灵活性)
在JS中任何数据都可以作为参数传递
class Cat {
name;
}
class Dog {
name;
}
function fn(obj) {
console.log(obj.name);
}
const cat = new Cat();
const dog = new Dog();
fn(cat);
fn(dog);
继承(扩展性)
// 父类
class Animal {
constructor(name) {
this.name = name;
}
say() {}
}
// 子类 继承父类
class Cat extends Animal {
constructor(name, sound) { // 重写构造函数
super(name); // 必须先调用父类构造函数
this.sound = sound;
}
say() { console.log('meow') }; // 重写父类方法
}
class Dog extends Animal {
superSay() { super.say(); } // 调用父类的方法
say() { console.log('wong') }; // 重写父类方法
}
// 调用
const cat = new Cat('c');
const dog = new Dog('d');
cat.name; // 'c'
dog.name; // 'd'
JS对象结构
对象自身
class Clazz {
constructor(age) {
this.age = age; // 1.构造函数中使用this添加属性
}
name; // 2.实例属性
fn = function() {} // 3.x = y 形式添加的方法属性
}
const c = new Clazz(20);
c.gender = 'f'; // 4.通过对象添加属性
// 上面添加的属性或方法会直接加到对象的结构中
console.log(c); // Clazz {name: undefined, age: 20, gender: 'f', fn: ƒ}
原型对象/原型链
在对象中有一个指向原型对象的属性__proto__,其中存储原型对象的属性方法;
在访问对象属性或方法时,先寻找对象自身,再从原型对象中寻找;
同样地,原型对象也有其原型对象,这样就构成一条原型链;
原型链根据对象结构复杂程度决定链长度,直到Object对象的原型。
class Clazz {
constructor() {} // 构造函数就是原型对象中的方法
fn() {} // 这种形式的方法会添加到原型对象中
}
const c = new Clazz();
// 访问原型对象
console.log(c.__proto__); // {constructor: ƒ, fn: ƒ}
console.log(Object.getPrototypeOf(c)); // {constructor: ƒ, fn: ƒ}
// 通过类访问实例的原型对象
console.log(Clazz.prototype); // {constructor: ƒ, fn: ƒ}
class Animal {}
class Cat extends Animal {}
const c = new Cat();
console.log(c.__proto__); // Animal {constructor: ƒ}
console.log(c.__proto__.__proto__); // {constructor: ƒ}
console.log(c.__proto__.__proto__.__proto__); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(c.__proto__.__proto__.__proto__.__proto__); // null
同类型的对象的原型对象指向同一个
class Clazz {}
const c1 = new Clazz();
const c2 = new Clazz();
console.log(c1.__proto__ === c2.__proto__); // true
原型/原型链区别
- prototype(原型):
- prototype 是函数对象特有的属性,每个函数对象都有一个 prototype 属性,它指向一个对象。
- 当你使用 new 关键字创建一个函数的实例时,该实例的
__proto__属性会指向构造函数的 prototype 属性。 - prototype 主要用于实现基于原型的继承。你可以向 prototype 对象添加属性和方法,这些属性和方法将被该函数创建的所有实例共享。
- proto(原型链):
__proto__是每个对象都有的属性,它指向该对象的原型。- 通过
__proto__属性,JavaScript 实现了对象之间的原型链,这使得对象可以继承其他对象的属性和方法。 - 通过
__proto__属性,你可以访问和修改对象的原型链。
修改原型
// 通过一个实例对象修改原型
class Clazz {}
const c = new Clazz();
c.__proto__.field = ''; // 添加原型属性
c.__proto__.fn = ()=> {}; // 添加原型方法
// 通过类修改原型对象
class Clazz {}
Clazz.prototype.fn = ()=>{};
查找对象属性
通过hasOwnProperty()或Object.hasOwn()可以检查对象自身属性。
class Clazz {
name;
say() {}
}
const c = new Clazz();
console.log("name" in c); // true
console.log("say" in c); // true
console.log(c.hasOwnProperty("say")); // false
console.log(Object.hasOwn(c, "say")); // false
JS对象复制
浅拷贝
// 数组复制
const array = [{}];
const array2 = array.slice();
console.log(array === array2); // false
console.log(array[0] === array2[0]); // true
// 数组复制
const array = [{},{}];
const array2 = [array[0], array[1]];
console.log(array === array2); // false
console.log(array[0] === array2[0]); // true
// 使用展开运算符实现数组复制
const array = [{},{}];
const array2 = [...array]; // 相当于 [array[0], array[1]]
console.log(array === array2); // false
console.log(array[0] === array2[0]); // true
// 对象复制
const obj = {};
const obj2 = Object.assign({}, obj);
console.log(obj === obj2); // false
// 使用展开运算符实现对象复制
const obj = {name: ''};
const obj2 = {...obj}; // 将对象属性展开到新对象{}中
console.log(obj === obj2); // false
深拷贝
const array = [{}];
const array2 = structuredClone(array);
console.log(array === array2); // false
console.log(array[0] === array2[0]); // false