javascript——面向对象

141 阅读3分钟

什么是面向对象

把所需要完成的功能拆解成几个类,由类创造出实例,具体的交互流程,可以在实例添加相应的属性和方法。

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