一、面向对象编程
1、什么是面向对象编程
1.1 面向过程编程VS面向对象编程
1、面向过程编程:
面向过程过程的重点是动词,是分析出解决问题需要的步骤,然后后编写函数实现每个步骤,最后依次调用函数
2、面向对象编程:
面向对象关注重点但是主谓,是吧构成问题的事物拆解成各个对象,而拆解出对象的目的也不是为了实现某个步骤,二十为了描述这个事物在当前问题中的各种行为
1.2 面向对象特点
- 继承:就是为了实现代码的复用,从父类上继承出一些属性方法,子类也有自己的一些属性
- 封装:让使用对象的人不考虑内部实现,只考虑功能使用,把内部的代码保护起来,只有一些api供用户使用
- 多态:是不同对象作用域统一操作产生不同的效果。多态的思想实际上是吧“想做什么”和“谁去做”分开
2、JS中的面向对象
2.1 对象包含什么
- 属性
-
2.2 常用内置对象
Object
- Array
- Date
- Function
- RegExp
2.3 创建对象
1、普通方法
通过new Object()
构造函数创建对象,然后后设置属性和方法
工程模式let Player = new Object()
// 再添加属性和方法
Player.name = 'yongzhi'
2、构造函数/实例function createObject () {
const Player = new Object()
Player.name = 'yongzhi'
Player.run = function () {
console.log(this.name + 'run')
}
return Player
}
this添加的属性和方法总是指向当前对象,所以实例化的时候,通过this添加的属性和方法都会在内存中复制一份,这样会造成内存浪费
缺点:会造成内存浪费
优点:即使改变某一个对象和方法,不会影响到其他的对象
3、原型function Player(name){
this.name = name;
this.run = function(){
console.log(this.name+"run")
}
}
const p1 = new Player("p1");
const p2 = new Player("p2");
通过原型继承的方法不是自身的,我们要在原型链上一层一层的查找,这样创建的好处是在内存中创建一次,实例化的对象都会指向这个prototype对象
4、静态属性function Player (name) {
this.name = name
}
player.prototype.run = function () {
console.log(this.name + 'run')
}
const p1 = new Player('p1')
const p2 = new Player('p2')
是绑定在构造函数上的属性和方法,需要通过构造函数访问function Player () {
if(!Player.total) {
Player.total = 0
}
Player.total++
}
二、原型及原型链
1、new
- 创建一个继承自Player.prototype的新对象
- 新对象的属性proto指向prototype
- 将this指向新建的对象
- 返回新对象
- 如果构造函数没有显式返回值,则返回this
- 如果构造函数有显式返回值,是基本类型,那么返回this
- 如果构造函数有显式返回值,是对象,则返回这个对象
function objectFactory () {
const obj = new Object()
let Constructor = [].shift.call(arguments)
obj.__proto__ = Constructor.prototype;
let ret = Constructor.apply(obj,arguments);
return typeof ret === 'object'?ret:obj
}
2、原型链
当读取实例的属性时,如果找不到,就会查找与对象相关链的原型中的属性,如果查找不到,就是取查找原型的原型,一直找到最顶层为止
三、继承
1、原型链继承
1.1 实现
function Parent() {
this.name = 'parent'
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child () {}
Child.prototype = new Parent()
Child.prototype.constructor = Child
var child = new Child()
child.getName()//parent
1.2 缺点
- 如果有属性是引用类型的,一旦某个实例修改了这个属性,所有的实例都会受到影响
- 创建Child实例的时候,不能传参
2、构造函数继承
2.1实现
function Parent (name,actions) {
this.name = name
this.actions = actions
}
function Child (id,name,actions) {
Parent.apply(this,Array.from(arguments).splice(1))
this.id = id
}
const child1 = new Child(1, "c1", ["eat"]);
const child2 = new Child(2, "c2", ["sing", "jump", "rap"]);
console.log(child1.name); // c1
console.log(child2.name); // c2
2.2 缺点
- 属性或方法想被继承的话,只能在构造函数中定义。如果方法在构造函数中定义了,那么每次创建实例都会创建一遍方法,多占一块内存
3、组合继承
3.1 实现
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).splice(1))
this.id = id
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
const child1 = new Child(1, "c1", ["eat"]);
const child2 = new Child(2, "c2", ["run"]);
child1.eat(); // c1 - eat
child2.eat(); // c2 - eat
console.log(child1.eat === child2.eat); // true
3.2 缺点
调用了两次构造函数,重复操作Parent.apply(this,Array.from(arguments).slice(1))
Child.prototype = new Parent();
4、寄生组合继承
4.1 实现
```javascript function Parent(name,action){ 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”, [“eat”]); const child2 = new Child(2, “c2”, [“run”]);
```