本文最后更新于 2024-03-22T23:32:26+00:00
                  
                  
                
              
            
            
              
                
                面向某某编程
面向对象编程
面向对象编程:Object-Oriented Programming,程序的主要组织单位是对象。
在 JS 中的对象定义为:无序属性的集合,其属性可以为基本值、对象或函数
行话:单个物体的抽象
面向过程编程
在面向过程编程中,程序的主要组织单位是函数。函数接收输入(参数),经过一系列处理,产生输出(返回值)。面向过程编程通常将问题分解为一系列的步骤,每个步骤由一个函数实现
函数式编程
鼓励使用纯函数(Pure Functions),即对于相同的输入,始终产生相同的输出,并且没有副作用(没有改变外部状态的行为)
面向对象编程
对象
基础概念
对象由一组属性组成,每个属性都包括一个键(字符串或 Symbol)和一个值(任意数据类型)。
属性的键是唯一的,不同属性之间用逗号分隔。属性的值可以是任何数据类型,包括基本数据类型和其他对象。
对象创建
Object
1 2 3 4 5 6
   | const obj = new Object() obj.name = 'xx' obj.age = '23' obj.sayAge = function() {   console.log(this.age) }
 
  | 
 
字面量
1 2 3 4 5 6 7
   | const obj = {   name: 'xx',   age: '23',   sayAge(){     console.log(this.age)   } }
 
  | 
 
Object.create()
创建一个空对象,该对象的原型(即__proto__属性)指向传入的参数对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | const obj = Object.create({})
 
  Object.create = (_obj) => {   if(typeof _obj !== 'object'){     return {}   }
       function F() {}      F.prototype = _obj      return new F() }
 
  | 
 
对象存储
对象的存储为:对象的内容是存储在堆中,变量在栈中存储对象的引用
构造函数
用来创建对象的特殊函数,通常以大写字母开头。
使用 new 关键字调用构造函数可以创建新对象,并将构造函数内部的属性和方法添加到新对象上。
1 2 3 4 5 6 7
   | function Person (age){   this.age = age }
  const my = new Person(29)
  console.log(my.age) 
 
  | 
 
new 做的事情
- 在堆里面建一个新的空对象
 
- 将这个新对象的
__proto__指向构造函数的prototype,以便实例可以继承构造函数原型上的属性和方法。–可用Object.setPrototypeOf(obj, prototype) 
- 执行构造函数,其中 this 关键字指向新创建的空对象上,这样构造函数内部的代码可以操作这个新对象。–可用
call() 
- 如果构造函数没有显式返回一个对象,那么会返回这个新对象的引用地址
 
工厂模式
在工厂模式中,不直接调用构造函数来创建对象,而是使用一个工厂函数(或者方法)来创建对象。
这种模式封装了对象的创建过程(不让外部感知),使得代码更具灵活性和可维护性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | function Person (...args) {            const _isClass = this instanceof Person
    if(!_isClass) return new Person(...args)
    this.name = args[0]   this.age = args[1] }
  const myself1 = Person('zhangsan', 58) console.log(myself1) 
  const myself2 = new Person('lisi', 69) console.log(myself2) 
 
  | 
 
Person 函数既可以被当作构造函数使用(通过 new 关键字调用),也可以被当作工厂函数使用(直接调用)
单例模式
是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点以访问该实例。
常用于:路由、全局状态等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | function Person (...args) {     if(typeof Person.instance === 'object'){     return Person.instance   }
       this.name = args[0]   this.age = args[1]
       Person.instance = this }
  const myself1 = new Person('xxx', 12) const myself2 = new Person('zzz', 21)
  console.log(myself1 === myself2) 
 
  | 
 
原型与原型链
原型
对象的原型指的就是__proto__属性,但它不是标准的 JavaScript API,不建议直接使用
原型链
当在对象上面找不到属性时,就会通过__proto__属性一层层往上找,这就是原型链。
最终找不到就返回 undefined
1 2 3 4 5 6 7 8 9 10
   | function Parent(name){   this.name = name } Parent.prototype.sayName = function(){   console.log(`my name is ${this.name}`) }
  const obj = new Parent('张三')
  obj.getName() 
 
  | 
 
继承
通过构造函数的原型对象来实现实例的继承
普通继承
将父类实例构赋值给子造函数的原型对象
1 2 3 4 5 6 7 8 9
   | function Parent(...args){   this.address = '成都'   this.name = '张三'   this.like = ['钓鱼', '洗碗']   this.args0 = args[0] } Parent.prototype.sayName = function(){   console.log(`my name is ${this.name}`) }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13
   | function Child(){}
 
  Child.prototype = new Parent() Child.prototype.constructor = Child 
 
  const child1 = new Child() const child2 = new Child()
  child1.sayName()  child2.like.push('喝酒')  child1.like  
 
  | 
 
优点
- 完成基础的继承功能:子类实例将会完全继承父类实例的属性、原型对象
 
缺点
- 父构造函数的调用不支持传参
 
- 子类实例的原型对象是共享的,那如果直接改原型对象的值后,影响所有的子类实例
 
构造函数继承(经典继承)
在子构造函数中调用父构造函数来实现继承
1 2 3 4 5 6 7 8 9
   | function Parent(...args){   this.address = '成都'   this.name = '张三'   this.like = ['钓鱼', '洗碗']   this.args0 = args[0] } Parent.prototype.sayName = function(){   console.log(`my name is ${this.name}`) }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13
   | function Child(...args){      Parent.call(this, ...args) }
  const child1 = new Child() const child2 = new Child()
  child2.like.push('喝酒') child2.like   child1.like  
  child1.sayName() 
 
  | 
 
优点
- 父构造函数将支持传递参数
 
- 子类实例的原型不会共享,避免了原型继承中的共享问题。
 
缺点
- 子类实例将无法继承父构造函数的
prototype属性 
组合继承
结合普通继承与经典继承,完全弥补这两个继承的缺点
1 2 3 4 5 6 7 8 9
   | function Parent(...args){   this.address = '成都'   this.name = '张三'   this.like = ['钓鱼', '洗碗']   this.args0 = args[0] } Parent.prototype.sayName = function(){   console.log(`my name is ${this.name}`) }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | function Child(...args){      Parent.call(this, ...args) }
 
  Child.prototype = new Parent() Child.prototype.constuctor = Child 
 
 
  const child1 = new Child() const child2 = new Child()
  child2.like.push('喝酒') child2.like   child1.like  
  child1.sayName() 
 
  | 
 
优点
- 子类实例将能继承父构造函数的
prototype属性 
缺点
- 会调用两次父构造函数
Parent.call(...) 
Child.prototype = new Parent() 
 
- 原型对象上多了不必要的属性
- 因为
Child.prototype = new Parent();这行代码会创建一个父类的实例,所以子类的原型对象上会多出一些不必要的属性,尽管它们在子类的构造函数中被覆盖了。 
 
寄生组合继承
基于组合继承,解决两次调用父类构造函数问题。
1 2 3 4 5 6 7 8 9
   | function Parent(...args){   this.address = '成都'   this.name = '张三'   this.like = ['钓鱼', '洗碗']   this.args0 = args[0] } Parent.prototype.sayName = function(){   console.log(`my name is ${this.name}`) }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | function Child(...args){      Parent.call(this, ...args) }
 
  Child.prototype = Object.create(Parent.prototype) Child.prototype.constuctor = Child 
 
  const child1 = new Child() const child2 = new Child()
  child2.like.push('喝酒') child2.like   child1.like  
  child1.sayName() 
 
  | 
 
寄生组合继承其实就是ES6的 class Child extends Parent的ES5代码,对标的是super()
多重继承
指的是一个类(或对象)同时继承了多个父类(或对象),从而可以拥有多个父类的属性和方法
1 2 3 4 5 6 7 8 9
   | function Parent1(...args){   this.address1 = '成都'   this.name1 = '张三'   this.like1 = ['钓鱼', '洗碗']   this.args10 = args[0] } Parent1.prototype.sayName = function(){   console.log(`my name is ${this.name}`) }
 
  | 
 
1 2 3 4 5 6 7 8 9
   | function Parent2(...args){   this.address2 = '上海'   this.name2 = '李四'   this.like2 = ['游泳']   this.args20 = args[0] } Parent2.prototype.sayLike = function(){   console.log(`my like is ${this.like}`) }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | function Child(...args){      Parent1.call(this, ...args)
 
       Parent2.call(this, ...args) }
 
  Child.prototype = Object.create(Object.assign(Parent1.prototype, Parent2.prototype))
  Child.prototype.constuctor = Child 
  const child1 = new Child() const child2 = new Child()
  child2.like.push('喝酒') child2.like   child1.like  
  child1.sayName() 
 
  | 
 
其他补充知识
in、hasOwnProperty、instanceof
- in:检查属性是否在对象上(自身以及原型链上)
- “name” in my // true || false
 
 
- hasOwnProperty:检查属性是否在对象上(仅自身不涉及原型链上)
- my.hasOwnProperty(‘name’) // true || false
 
 
- instanceof:检查对象是否属于某个构造函数的实例
- my instanceof Object // true || false
 
- 实现原理:检查对象的原型链上是否包含构造函数的 prototype 属性,即判断
实例.__proto__ === 构造函数.prototype 
 
对象分类
对象分为 2 类:实例对象、函数对象
实例对象:通过 [new 构造函数()] 生成的
函数对象:通过 [new Function()] 生成的
- 每个对象(包含函数)都有
__proto__属性,其指向等于其构造函数的prototype指向,实例.__proto__ === 构造函数.prototype 
- 每个函数都有 
prototype 属性,指向一个普通对象,该对象具有__proto__、constructor属性
__proto__ 指向等于其构造函数(Object)的prototype指向 
constructor指向函数本身 
 
图解:实例、构造函数、Function、Object、null 的关系

1 2 3 4 5 6
   | person.__proto__===Person.prototype  Person.__proto__===Function.prototype  Function.__proto__===Function.prototype  Object.__proto__===Function.prototype  Object.__proto__.__proto__===Object.prototype  Function.__proto__===Object.prototype 
 
  | 
 
Object 与 Function 的关系
Function 与 Object 的__proto__都指向同一个原型对象(Function.prototype)
1 2 3 4 5
   | Object.__proto__ === Function.__proto__ === Function.prototype  Object instanceof Function  Function instanceof Function  Function instanceof Object  Object instanceof Object 
 
  | 
 
更改对象的原型
- 粗暴(不推荐):
obj.__proto__===newObj; 
- 优雅(推荐):
Object.setPrototypeOf(obj, newObj)等价于操作 1 
- 到位:
const obj = Object.create(newObj)等价于两步const obj = {}; obj.__proto__=newObj; 
面试题
手写 new
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   |  function Person(age){   this.age = age } const my = myNew(Person, 29)
  function myNew = function (context, ...args) {    }
 
 
  function myNew = function (context, ...args) {   
       const obj = {};
       Object.setPrototypeOf(obj, context.prototype);    
       const res = context.apply(obj, args);
       return res instanceof Object ? res : obj; }
 
  | 
 
基础判断题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | function Person(name, age) {   this.name = name;   this.age = age; }
  const person = new Person("xh", 29);
  console.log(person.__proto__ === Person.prototype) console.log(person.constructor === Person.constuctor) console.log(Person.constructor === Function) console.log(person.constructor === Person) console.log(Person === Person.prototype.constructor)
  console.log(person.__proto__.constructor === Object) console.log(Person.prototype.__proto__ === Object.prototype) console.log(person.__proto__.__proto__.constructor === Object) console.log(person.__proto__.__proto__.__proto__ === null) console.log(Function.constructor === Object) console.log(Object.constructor === Function)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  |