JavaScript基础(三)

80 阅读4分钟

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