08.初识面向对象~ES6继承(127~135)

60 阅读5分钟

面向对象

image.png


创建对象方式

工厂函数

image.png

下面是一个示例工厂函数,用于创建Person对象:

function createPerson(name, age) {
  return {
    name: name,
    age: age,
    sayHello: function() {
      console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
    }
  };
}

在上述示例中,createPerson是一个工厂函数,它接受nameage作为参数,并返回一个包含nameagesayHello方法的对象。

我们可以使用工厂函数来创建多个不同的Person对象:

const person1 = createPerson('John', 25);
const person2 = createPerson('Jane', 30);

person1.sayHello(); // 输出:Hello, my name is John and I'm 25 years old.
person2.sayHello(); // 输出:Hello, my name is Jane and I'm 30 years old.

自定义构造函数

image.png

构造函数注意问题

首字母大写

image.png


不写return

image.png


最好不当普数

image.png


this指向

image.png


面向对象的原型

原型链

image.png


共享内存

在JavaScript中,面向对象编程是通过原型(prototype)来实现的。每个对象都有一个原型,原型是一个包含属性和方法的对象。当我们创建一个新对象时,它会继承原型对象的属性和方法。

JavaScript中的原型继承是基于原型链的概念。每个对象都有一个隐藏的属性__proto__,指向它的原型对象。当我们访问一个对象的属性或方法时,如果对象本身没有这个属性或方法,JavaScript会沿着原型链向上查找,直到找到该属性或方法或到达原型链的末尾(即Object.prototype)。

使用原型,我们可以创建一个构造函数,并通过该构造函数创建新的对象。构造函数可以定义对象的初始属性和方法,而这些属性和方法会被所有通过该构造函数创建的对象共享。我们可以将共享的属性和方法定义在构造函数的原型对象上,这样所有的对象都可以通过原型链访问到这些属性和方法。

例如,我们可以定义一个名为Person的构造函数,并将共享的属性和方法定义在Person.prototype上:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function() {
  console.log("Hello, my name is " + this.name);
};

然后,我们可以使用该构造函数创建新的对象:

var person1 = new Person("Alice", 25);
var person2 = new Person("Bob", 30);

person1.sayHello();  // 输出 "Hello, my name is Alice"
person2.sayHello();  // 输出 "Hello, my name is Bob"

在这个例子中,Person.prototype定义了一个sayHello方法,所有通过Person构造函数创建的对象都可以访问到这个方法。

image.png


ES6-class

构造器函数

image.png

image.png


class Animal {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  speak() {
    console.log(`我的名字是${this.name},我${this.age}岁了。`);
  }
}

// 创建一个Animal类的实例
const cat = new Animal("咪咪", 3);
cat.speak(); // 输出:我的名字是咪咪,我3岁了。

在这个示例中,Animal类有一个构造函数方法,在创建类的实例时被调用。它设置了nameage属性。类还定义了一个speak方法,用于在控制台输出动物的信息。


在ES6中,我们可以使用class语法来定义构造函数。构造函数在class中使用constructor关键字定义,用于初始化对象的属性。例如:

class Animal {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  speak() {
    console.log(`我的名字是${this.name},我${this.age}岁了。`);
  }
}

// 创建一个Animal类的实例
const cat = new Animal("咪咪", 3);
cat.speak(); // 输出:我的名字是咪咪,我3岁了。

面向对象继承

父类

image.png

子类

image.png


apply和call的区别

image.png

.call().apply()是JavaScript中的两个函数方法,它们用于调用函数并指定函数内部的this值和参数。

.call().apply()的主要区别在于传递参数的方式:

.call()方法接受一个指定的this值,后面可以传递多个参数,每个参数都是直接传递给函数的。

.apply()方法也接受一个指定的this值,但是它只接受两个参数:第一个参数是要绑定给函数的this值,第二个参数是一个数组或类似数组的对象,其中的每个元素都会作为单独的参数传递给函数。

以下是一个示例来说明.call().apply()的区别:

function greet(message) {
  console.log(`${message}, ${this.name}!`);
}

const person = {
  name: "John"
};

// 使用.call()方法调用函数,并传递参数
greet.call(person, "Hello"); // Output: Hello, John!

// 使用.apply()方法调用函数,并传递参数
greet.apply(person, ["Hi"]); // Output: Hi, John!

在上面的示例中,greet函数用于打招呼,并输出一个消息和一个名字。使用.call().apply()方法,我们可以将person对象作为函数的this值传递进去,并传递一个消息作为参数。

.call()方法使用逗号分隔的参数列表,而.apply()方法使用一个数组来传递参数。

总结来说,.call().apply()的区别在于传递参数的方式,.call()使用逗号分隔的参数列表,而.apply()使用一个数组。


原型继承

以下是一个使用原型链继承的示例:

// 父类
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(`我是${this.name}`);
}

// 子类
function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

// 设置子类的原型为父类的实例
Dog.prototype = Object.create(Animal.prototype);

Dog.prototype.bark = function() {
  console.log("汪汪汪!");
}

// 创建一个Dog对象
const dog = new Dog("旺财", "柴犬");
dog.speak(); // 输出:我是旺财
dog.bark(); // 输出:汪汪汪!

在这个示例中,Animal是父类,Dog是子类。通过使用call方法,我们可以在子类的构造函数中调用父类的构造函数以继承其属性。然后,通过将子类的原型设置为父类的实例,子类将继承父类的方法。


组合继承

组合继承是一种常用的继承模式,它结合了原型链继承和构造函数继承的优点,以实现对象的属性和方法的继承。

在组合继承中,首先通过构造函数继承来继承父类的属性,然后通过将子类的原型设置为父类的实例来继承父类的方法。

以下是一个使用组合继承的示例:

// 父类
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(`我是${this.name}`);
}

// 子类
function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

// 设置子类的原型为父类的实例
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  console.log("汪汪汪!");
}

// 创建一个Dog对象
const dog = new Dog("旺财", "柴犬");
dog.speak(); // 输出:我是旺财
dog.bark(); // 输出:汪汪汪!

在这个示例中,Animal是父类,Dog是子类。通过使用构造函数继承,我们可以在子类的构造函数中调用父类的构造函数来继承父类的属性

然后,通过将子类的原型设置为父类的实例,子类将继承父类的方法。

最后,我们需要修正子类的constructor属性,将其指向子类本身。


ES6继承

extends原型继承

父类

image.png

子类

image.png


继承覆盖

以下是一个使用ES6继承覆盖的示例:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`我是${this.name}`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    console.log(`我是${this.name},我是一只${this.breed}`);
  }
}

// 创建一个Dog对象
const dog = new Dog("旺财", "柴犬");
dog.speak(); // 输出:我是旺财,我是一只柴犬

在这个示例中,Animal是父类,Dog是子类。子类Dog覆盖了父类Animalspeak方法。

在子类中定义的方法会覆盖父类中同名的方法。这意味着当我们调用子类对象的speak方法时,将执行子类中定义的speak方法,而不是父类中的speak方法。


super关键字

super是在ES6中引入的关键字,用于在子类中调用父类的构造函数和方法。

在子类的构造函数中,可以使用super()来调用父类的构造函数,从而继承父类的属性。这是实现继承的一部分。

在子类的方法中,可以使用super关键字来调用父类中的同名方法,从而继承父类的方法并在其基础上进行扩展。

以下是一个使用super关键字的示例:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`我是${this.name}`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    super.speak(); // 调用父类的speak方法
    console.log(`我是一只${this.breed}`);
  }
}

// 创建一个Dog对象
const dog = new Dog("旺财", "柴犬");
dog.speak(); // 输出:我是旺财,我是一只柴犬

在这个示例中,Animal是父类,Dog是子类。子类Dog通过super(name)调用父类的构造函数来继承父类的属性。

在子类的speak方法中,使用super.speak()调用父类的speak方法,然后在其基础上进行扩展。


总结

继承很好用

前途如何呢?

QQ图片20231115210510.jpg