面向对象编程

面向对象是一种编程思想,经常被拿来和面向过程比较。

简单点,面向过程关注的重点是动词,是分析出解决问题需要的步骤,然后编写函数实现每个步骤,最 后依次调用函数。而向对象关注的重点是主谓,是把构成问题的事物拆解为各个对象,而拆解出对象的目的也 是为了实现某个步骤, 而是为了描述这个事物在当前问题中的各种行为。

面向对象的特点:

1、封装 让使用对象的人不考虑内部实现,只考虑功能使用把内部的代码保护起来,只留出一些api接口供用户使用。

2、继承 就是为了代码的复用,从父类上继承出一些方法和属性,子类也有自己的一些属性

3、多态 是不同对象作用于同一操作产生不同的效果。多态的思想实际上是把“想做什么”和“谁去做”分开

什么时候适合使用面向对象

可以看出来,在比较复杂的问题面前,或者参与方较多的时候,面向对象的编程思想可以很好的简化问题,并且能够更好的扩展和维护。

Js中的面向对象

对象包含方法和属性

创建对象

1、普通方式

const Player = new Obejct();
Player.color = "white"Player.start = function() {  console.log('start')
}复制代码

工厂模式

function createObject() {  const Player = new Obejct();
  Player.color = "white"
  Player.start = function() {console.log('start')
  }  return Player
}复制代码

2、构造函数/实例

通过 this 添加的属性和 法总是指向当前对象的,所以在实例化的时候,通过this添加的属性和方法都会在内存中复制一份,这样就会造成内存的浪费。

但是这样创建的好处是即使改变了某一个对象的属性或方法,不会影响其他的对象(因为每 个对象都是复制的一份)

function Player(color) {  this.color = color  this.start = function() {console.log(color)
  }
}const player1 = new Player('red')const player2 = new Player('blue')console.log(player1 === player2) // false 内存被创建了多次复制代码

3、原型

通过原型继承的方法并不是自身的,我们要在原型链上一层一层的查找,这样创建的好处是只在内存中创建一次,实例化的对象都会指向这个prototype对象。

function Player(color) {  console.log(color)
}
Player.prototype.start = function() {  console.log(color)
}const player1 = new Player('red')const player2 = new Player('blue')console.log(player1.start === player2.start) // true复制代码

4、静态属性 是绑定在构造函数上的属性方法,需要通过构造函数访问

function Player(color) {  this.color = color  if (!Player.total) {
  	Player.total = 0
  }
  Player.total++
}const player1 = new Player('red')console.log(Player.total)const player2 = new Player('blue')console.log(Player.total)复制代码

原型及原型链

function Player(color) {  console.log(color)
}
Player.prototype = {  start: function() {  	console.log('start')
  },  revert: function() {  	console.log('revert')
  }
}

找Player的原型对象const Player1 = new Player('red')const Player2 = new Player('blue')console.log(Player1.__proto__) // === Player.prototype {start: ƒ, revert: ƒ}console.log(Object.getPrototypeOf(Player1)) // Player.prototype  {start: ƒ, revert: ƒ}Object.getPrototypeOf  获取__proto__console.log(Player.__proto__)  // [Function]复制代码

流程图彻底弄懂JavaScript 面向对象编程_JavaScript

new关键字

  • 一个继承自Player.prototype的新对象Player1被创建
  • Player1.proto指向Player.prototype
  • 将this指向新创建的对象Player1
  • 返回新对象
  • 如果构造函数没有显式返回值,则返回this
  • 如果构造函数有显式返回值,是基本类型, 如 number,string,boolean, 那么还是返回this
  • 如果构造函数有显式返回值,是对象类型, 如{ a: 1 }, 则返回这个对象{ a: 1 }
function proPlayer() {return {a: 1}
}const pro1 = new proPlayer()console.log(pro1) // {a: 1}function proPlayer() {return 123}const pro1 = new proPlayer()console.log(pro1) // proPlayer {}function proPlayer() {return}const pro1 = new proPlayer()console.log(pro1) // proPlayer {}复制代码

实现new

function objectFactory() {  let obj = new Object();  let Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype  let result = Constructor.apply(obj, arguments)  return typeof result === "object" ? result : obj;
}复制代码

原型链

我们都知道当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还 查不到,就去找原型的原型,一直找到最顶层为止。

function Player() {}
Player.prototype.name = 'Tom'let p1 = new Player()
p1.name = 'Davi'console.log(p1.name) // Davidelete p1.nameconsole.log(p1.name) // Tom如果我们在Player.prototype中也找不到name属性的化,就会去Player.prototype.__proto__中寻找,也就是{}delete Player.prototype.nameconsole.log(p1.name)复制代码

继承

1、原型链继承

function Parent() {  this.name = 'parentName'}
Parent.prototype.getName = function() {  console.log(this.name)
}function Child() {}
Child.prototype = new Parent()
Child.prototype.constructor = Childlet child1 = new Child()
child1.getName()复制代码

缺点:

1、如果有属性是引用类型,一旦某个实例修改了这个属性,所有实例都会收到影响

2、创建实例的实例的适合,不能传参

function Parent() {  this.actions = ['parentName', 'play']
}function Child() {}
Child.prototype = new Parent()
Child.prototype.constructor = Childlet child1 = new Child()let child2 = new Child()
child1.actions.pop()console.log(child1.actions) //parentNameconsole.log(child2.actions) //parentName复制代码

2、构造函数

function Parent(name, actions) {  this.actions = actions;  this.name = name;  this.eat = function() {  	console.log(name)
  }
}function Child(id, name, actions) {
  Parent.call(this, name); // 如果想直接传多个参数, 可以Parent.apply(this, 		Array.from(arguments).slice(1));
  this.id = id;
}const child1 = new Child(1, "c1", ["eat"]);const child2 = new Child(2, "c2", ["sing", "jump", "rap"]);console.log(child1.name); // { actions: [ 'eat' ], name: 'c1', id: 1 }console.log(child2.name);console.log(child1.eat === child2.eat)复制代码

缺点:浪费内存

组合继承

function Parent(name, actions) {this.name = name;this.actions = actions;
}
Parent.prototype.eat = function () {console.log(`${this.name} - eat`);
};function Child(id) {
  Parent.apply(this, Array.from(arguments).slice(1));  this.id = id;
}

Child.prototype = new Parent();
Child.prototype.constructor = Child;function Child(id) {
   Parent.apply(this, Array.from(arguments).slice(1));   this.id = id;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;复制代码

缺点: 调用了两次构造函数

寄生式组合继承

function Parent(name, actions) {this.name = name;this.actions = actions;
}
Parent.prototype.eat = function () {console.log(`${this.name} - eat`);
};function Child(id) {
  Parent.apply(this, Array.from(arguments).slice(1));  this.id = id;
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;const child1 = new Child(1, "c1", ["hahahahahhah"]);const child2 = new Child(2, "c2", ["xixixixixixx"]);复制代码