面向对象是一种编程思想,经常被拿来和面向过程比较。
简单点,面向过程关注的重点是动词,是分析出解决问题需要的步骤,然后编写函数实现每个步骤,最 后依次调用函数。而向对象关注的重点是主谓,是把构成问题的事物拆解为各个对象,而拆解出对象的目的也 是为了实现某个步骤, 而是为了描述这个事物在当前问题中的各种行为。
面向对象的特点:
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]复制代码
流程图
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"]);复制代码