一、面向对象编程
1、什么是面向对象编程
面向对象是一种编程思想,经常被拿来和面向过程比较
1.1 面向过程编程 VS 面向对象编程
1、面向过程编程:
面向过程关注的重点是动词,
是分析出解决问题需要的步骤,
然后编写函数实现每个步骤,
最后依次调用函数
2、面向对象编程:
面向对象关注重点是主谓,
是把构成问题的事物拆解成各个对象,
而拆解出对象的目的也不是为了实现某个步骤,
而是为了描述这个事物在当前问题中的各种行为
1.2 面向对象特点
1、继承
就是为了实现代码的复用,从父类上继承出一些方法属性,子类也有自己的一些属性
2、封装
让使用对象的人不考虑内部实现,只考虑功能使用,把内部的代码保护起来,
只留一些api供用户使用
3、多态
是不同对象作用于同一操作产生不同的效果。
多态的思想实际上是把“想做什么”和“谁去做”分开
2、JS中的面向对象
2.1 对象包含什么
属性
方法
2.2 常用内置对象
- Object
- Array
- Date
- Function
- RegExp
2.3 创建对象
1、普通方法
通过new Object()构造函数创建对象,然后再设置属性和方法
new Object()
let Player= new Object();
// 再添加属性和方法给
objectPlayer.name = 'Tom';工厂模式
function createObject(){
const Player = new Object();
Player.name='Tom';
Player.run = function(){
console.log(this.name+"run")
}
return Player;
}2、构造函数/实例
this添加的属性和方法总是指向当前对象,所以实例化的时候,
通过this添加的属性和方法都会在内存中赋值一份,这样会造成内存浪费
缺点:
这样会造成内存浪费
好处:
即使改变某一个对象的属性和方法,不会影响到其他的对象
function Player(name){
this.name = name;
this.run = function(){
console.log(this.name+"run")
}
}
const p1 = new Player("p1");
const p2 = new Player("p2");3、原型
通过原型继承的方法不是自身的,我们要在原型链上一层层的查找,
这样创建的好处是指在内存中创建一次,实例化的对象都会指向这个prototype对象
function Player(name){
this.name = name;
}
Player.prototype.run = function(){
console.log(name+"run")
}
const p1= new Player("p1");
const p2= new Player("p2");4、静态属性
是绑定在构造函数上的属性方法,需要通过构造函数访问
function Player(name){
if(!Player.total){
Player.total = 0;
}
Player.total++;
}二、原型及原型链
1、 new
1、创建一个继承自Player.prototype的新对象
2、新对象的属性__proto__指向Player.prototype
3、将this指向新建的对象
4、返回新对象
- 4.1如果构造函数没有显式返回值,则返回this
- 4.2如果构造函数有显式返回值,是基本类型,那么返回this
- 4.3如果过构造函数有显式返回值,是对象,则返回这个对象
function objectFactory(){
let 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();// parent1.2 缺点
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).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); // c1
console.log(child2.name); // c22.2 缺点
1、属性或者方法想被继承的话,只能在构造函数中定义。
如果方法在构造函数内定义了,那么每次创建实例都会创建一遍方法,多占一块内存
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).slice(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); // true3.2 缺点
1、调用了两次构造函数,组了重复的操作
Parent.apply(this,Array.from(arguments).slice(1))
Child.prototype = new Parent();4、寄生组合继承
4.1 实现
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"]);