面向对象高级

57 阅读4分钟

前提知识www.yuque.com/u28515044/e…

对象创建模式

1. Object构造函数

  • 适用场景:内部函数属性和方法不确定
  • 缺点:语句太多
let son=new Object()
son.name='张三’
son.age=18
son.setName=function(name) {
	this.name=name
}

2. 对象字面量

  • 适用场景:起始时对象内部数据是确定的
  • 缺点:如果创建多个对象, 有重复代码
//对象字面量模式
var p = {
   name: 'Tom',
   age: 12,
   setName: function (name) {
     this.name = name
   }
 }

 var p2 = {  //如果创建多个对象代码很重复
   name: 'Bob',
   age: 13,
   setName: function (name) {
     this.name = name
   }
 }

3. 工厂模式

  • 适用场景: 需要创建多个对象
  • 问题: 对象没有一个具体的类型, 都是Object类型
//返回一个对象的函数===>工厂函数
function createPerson(name, age) { 
 var obj = {
   name: name,
   age: age,
   setName: function (name) {
     this.name = name
   }
 }
 return obj
}

// 创建2个人
var p1 = createPerson('Tom', 12)
var p2 = createPerson('Bob', 13)

// p1/p2是Object类型

function createStudent(name, price) {
 var obj = {
   name: name,
   price: price
 }
 return obj
}
var s = createStudent('张三', 12000)
// s也是Object

4. 构造函数

  • 适用场景: 需要创建多个类型确定的对象,与上方工厂模式有所对比
  • 问题: 每个对象都有相同的数据, 浪费内存
//定义类型
function Person(name, age) {
 this.name = name
 this.age = age
 this.setName = function (name) {
   this.name = name
 }
}
var p1 = new Person('Tom', 12)
p1.setName('Jack')
console.log(p1.name, p1.age)
console.log(p1 instanceof Person)

5. 构造函数+原型(最好的写法)

  • 适用场景: 需要创建多个类型确定的对象
  • 放在原型上可以节省空间(只需要加载一遍方法)
//在构造函数中只初始化一般函数
function Person(name, age) { 
 this.name = name
 this.age = age
}
Person.prototype.setName = function (name) {
 this.name = name
}

var p1 = new Person('Tom', 23)
var p2 = new Person('Jack', 24)
console.log(p1, p2)

构造函数和工厂模式的区别

  1. 创建方式:
    • 构造函数: 使用 new 关键字和一个函数来创建对象。构造函数通常用于定义对象的结构和初始化操作。
    • 工厂模式: 使用一个函数来返回对象的实例。这个函数充当一个工厂,根据传入的参数和逻辑来创建并返回不同的对象。
  1. 返回对象:
    • 构造函数: 不需要显式地返回对象。对象是通过 this 关键字引用构造函数内部创建的。
    • 工厂模式: 显式地使用 return 语句返回一个新创建的对象。
  1. 使用方式:
    • 构造函数: 通常用于创建可实例化的对象,实例可以通过 new 关键字来创建,并且可以使用 instanceof 运算符来检查对象的类型。
    • 工厂模式: 可以更灵活地创建对象,根据参数和逻辑返回不同类型的对象,不需要使用 new 关键字。

继承模式

原型链继承

关键点在于子类型的原型为父类型的实例

步骤:

  1. 创建父类型
  2. 给父类型的原型上添加方法
  3. 创建子类型
  4. 让子类型的原型为父类型的一个实例
  5. 让子类型原型(Sub.prototype)的构造属性(construction)为子类型
  6. 给子类型添加方法
  7. 创建一个子类型,其可以使用父类型上的方法

大致结构图:

//父类型
function Supper() {
	this.supProp = '父亲的原型链'
}
//给父类型的原型上增加一个[showSupperProp]方法
Supper.prototype.showSupperProp = function () {
	console.log(this.supProp)
}

//子类型
function Sub() {
	this.subProp = '儿子的原型链'
}

// 子类型的原型为父类型的一个实例对象
Sub.prototype = new Supper()
// 让子类型的原型的constructor指向子类型
Sub.prototype.constructor = Sub
//给子类型的原型上增加一个[showSubProp]方法
Sub.prototype.showSubProp = function () {
	console.log(this.subProp)
}

var sub = new Sub()

sub.showSupperProp() //父亲的原型链
sub.showSubProp() //儿子的原型链
console.log(sub)  
/**
Sub {subProp: "儿子的原型链"}
subProp: "儿子的原型链"
__proto__: Supper
constructor: ƒ Sub()
showSubProp: ƒ ()
supProp: "父亲的原型链"
__proto__: Object
*/

这里解释一下第18行的代码,为什么要让Sub.prototype.constructor=Sub。当你创建一个函数是,该函数会自动创建一个prototype属性,并且其中包含一个指向函数本身的construction属性;构造函数创建的实例对象的construction通常指向创建该对象的构造函数,因为实例对象通常按照原型链进行查找,继承了原型链上的construction属性。此处代码第十六行,修改Sub.prototype为new Supper( ),即Sub.prototype成为Supper的一个实例对象,则Sub.prototype.construction会沿着原型链查找,继承Supper.prototype.construction,即为Supper。故此时需要将Sub.prototype.constructor修正为Sub。

借用构造函数

在子类型构造函数中通过call( )调用父类型构造函数

function Parents(name,age) {
	this.name=name
  this.age=age
}

function Son(name,age,hobby) {
	Parents.call(this,name,age)
  this.hobby=hobby
}

组合继承

原型链继承+构造函数继承

使用原型链继承方法,构造函数继承属性