什么是面向对象
把所需要完成的功能拆解成几个类,由类创造出实例,具体的交互流程,可以在实例添加相应的属性和方法。
js是基于面向对象构建出来的。比如windows对象,就有很多属性和方法。
创建对象
工厂模式
可以创建多个相似对象,但是没办法识别对象,因为所创造出的所有对象的原型都是Object。
function people(name,age){
let obj = new Object();
obj.name = name;
obj.age = age;
obj.sayName = function(){
return this.name;
}
return obj;
}
let tom = people('Tom',12);
let lili = people('Lili',12);
console.log(tom.constructor);//Object
console.log(lili.constructor);//Object
构造函数模式
解决了无法识别对象的问题,但是没生成一个实例就会创建新的方法。
function People(name,age){
this.name = name;
this.age = age;
this.sayName = function(){retrun this.name;}
}
let tom = new People('TOM',12);
console.log(tom.constructor);//People
原型模式
通过在原型上增加属性和方法就可以让所有实例对象共享。
function People(name,age){
this.name = name;
this.age = age;
}
People.prototype.sayName = function(){return this.name}
let tom = new People('TOM',12);
console.log(tom.sayName());//TOM
封装
低耦合高内聚
多态
分为重载和重写。
重载是方法名相同,根据传入参数的类型和个数不同执行不同的逻辑。
重写就是方法名相同,其他都不同,子类可以重写父类的方法。
继承
继承就是子类继承父类的属性和方法,同时子类还可以有自己的属性和方法。
原型继承
把子类的原型指向父类的一个实例对象。
缺点:子类的实例对象都有一个相同的父类实例对象。父类的属性和方法是共享的。
//父类的构造函数
function Parent(){
this.name = 'Tom';
this.age = 12;
this.arr = [1,2,4];
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
//子类的构造函数
function Child(grade){
this.grade = grade;
}
//子类构造函数的原型指向父类的一个实例对象
Child.prototype = new Parent();
let child1 = new Child(1);
let child2 = new Child(2);
console.log(child1.grade);//1
child1.sayName();//Tom
console.log(child2.grade);//2
child2.sayName();//Tom
child1.arr.push(11);
console.log(child1.arr, child2.arr);//[1,2,4,11] [1,2,4,11]
组合继承
使用构造函数和原型的方法。
子类的实例对象的父类实例对象都是不同的,所以父类的属性都隔离开了,而且可以向父类的构造函数传参。
但是这种方法使父类的构造函数执行了两次
第一次是原型指向的时候,new Parent(),这个时候会生成__proto__的那个父类对象,可以看到age name都是undefined。
第二次是执行new Child时会执行Parent.call(),这个函数会返回一个父类对象,所以Child对象里就会有name,age,arr。
//父类的构造函数
function Parent(name,age){
this.name = name;
this.age = age;
this.arr = [1,2,4];
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
//子类的构造函数
function Child(grade,name,age){
Parent.call(this,name,age);
this.grade = grade;
}
//子类构造函数的原型指向父类的一个实例对象
Child.prototype = new Parent();
let child1 = new Child(1,'TOM',12);
let child2 = new Child(2,'LILI',13);
console.log(child1.grade);//1
child1.sayName();//TOM
console.log(child2.grade);//2
child2.sayName();//LILI
child1.arr.push(11);
console.log(child1.arr, child2.arr);//[1,2,4,11] [1,2,4]
寄生组合继承
解决了组合继承重复属性的问题。
使子类构造函数的prototype指向父类的prototype。这样就不会重复生成相同的父类属性。
注意修改constructor的指向。
//父类的构造函数
function Parent(name,age){
this.name = name;
this.age = age;
this.arr = [1,2,4];
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
//子类的构造函数
function Child(grade,name,age){
Parent.call(this,name,age);//获得父类实例的所有属性
this.grade = grade;
}
//子类构造函数的原型指向父类构造函数的原型
Child.prototype = Parent.prototype;
Child.prototype.constructor = Child;
let child1 = new Child(1,'TOM',12);
let child2 = new Child(2,'LILI',13);
console.log(child1.grade);//1
child1.sayName();//TOM
console.log(child2.grade);//2
child2.sayName();//LILI
child1.arr.push(11);
console.log(child1.arr, child2.arr);//[1,2,4,11] [1,2,4]
ES6的extend继承
ES6的extend其实是寄生组合继承的一个语法糖。
class Parents{
constructor(name){
this.name = name;
this.arr = [1,2,3];
}
say(){
console.log(this.name);
}
}
class Child extends Parents{
constructor(name,age){
super(name);//调用父类构造函数
this.age = age;
}
}
let child1 = new Child('Tom',12);
child1.arr.push(11);//[1,2,3,11]
child1.say();//Tom