JS中的面向对象编程

面向对象编程

什么是面向过程编程

面向过程重点是分析出解决问题的步骤,然后编写函数实现每个步骤,最后依次调用函数。

什么是面向对象编程

面向对象编程重点是把构成问题的事物拆解成一个个对象,拆解出对象的目的不是为了解决问题,而是为了描述事物在当前各种问题中的行为。

面向对象编程的特点和优点

特点:

  • 封装性:让使用对象的人不考虑内部实现,只考虑功能的使用,把内部的代码保护起来,只留出一些API供使用方使用,
  • 继承性:为了代码复用性,可以继承父类的一些属性和方法,子类也可以有自己的一些方法。
  • 多态性:是不同对象作用于同一操作产生不同的结果,多态的思想是把“想做什么”和“谁去做”分开。

优点:
在解决复杂问题有多方参与的情况下,面向对象编程的优势就体现出来了,能够更好的简化问题,更好的维护和扩展,

创建对象的方式

1、普通模式

let o = new Object();
复制代码

2、工厂模式

function createPerson(name, age, job){
    const o = new Object();
    
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayname = function() {
        console.log(this.name);
    };
    
    return o;
}

let person = createPerson('vivi', 25, 'Web developer');
复制代码

缺点: 无法识别出创建的对象是什么类型。

3、构造函数模式

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayname = function(){
        console.log(this.name);
    };
}

let person = new Person('vivi', 25, 'Web developer');
复制代码

优点:改变某个对象的属性和方法不会影响其他对象,可以确保实力被标识为特定的类型。

缺点:this挂的属性/方法,都是指向当前对象的,所以在实例化的时候,都会在内存中复制一份,这就造成了一定程度上的内存浪费。

4、原型模式

function Person(){}

Person.prototype.name = 'vivi';
Person.prototype.age = '18';
Person.prototype.sayname = function(){
    console.log(this.name);
};

let person = new Person();
复制代码

优点: 原型上的属性、方法只创建一次,所有的实例共享一份属性、方法。

原型、原型链

牢记:每个实例对象都有一个私有属性__proto__,都会指向它的构造函数的对象属性Prototype,而每个prototype也有自己的原型对象__proto__,层层向上,直到一个对象的原型对象为null,就代表找到顶端了,一条原型链就形成了。

怎么找到原型对象

通过对象的__proto__属性可以找到原型对象。通过构造函数new一个实例,实例的__proto__属性指向了构造函数的prototype, 构造函数的prototype.constructor指向构造函数。

手写一个new的实现过程

new的实现步骤:

1、新建一个对象

2、将对象的__proto__属性指向构造函数的Prototype。

3、执行构造函数,并将this指向新建的对象。

4、如果构造函数的返回值是对象,则返回该返回值对象。

5、返回新建的这个对象。

function newInstance(Fn, ...args) {
    const obj = {};
    
    obj.__proto__ = Fn.prototype;
    
    let result = Fn.call(obj, ...args);

    return  result instanceof Object ? result : obj;
}

复制代码

new返回不同的类型会有哪些表现?

  • 1、如果构造函数没有显示返回值,则返回this。
function Person(name, age) {
    this.name = name;
    this.age = age;
}

const p1 = newInstance(Person, 'vivi', 18);// Person {name: "vivi", age: 18}
复制代码
  • 2、如果构造函数有显示返回值,是基本数据类型,比如String、Number、Boolean等,那么还是返回this。
function Person(name, age) {
    this.name = name;
    this.age = age;
    
    return name;
}

const p2 = newInstance(Person, 'vivi', 18);// Person {name: "vivi", age: 18}
复制代码
  • 3、如果构造函数有显示返回值,是对象类型,那么返回该对象。
function Person(name, age) {
    this.name = name;
    this.age = age;

    // 显示返回值为对象
    return {
        name
    }
}

const p3 = newInstance(Person, 'vivi', 18); // {name: "vivi"}
复制代码

继承

原型链继承

function Parent() {
    this.name = "Father";
}

Parent.prototype.getName = function(){
    console.log(this.name);
}

function Child() {}

Child.prototype = new Parent();

const child1 = new Child();
child1.getName();
复制代码

隐含的问题:

1、一旦有引用类型的属性,某个实例更改了这个属性,那么所有的实例都会受影响。

2、实例无法传参

构造函数继承

function Parent(name, actions) {
    this.name = name;
    this.actions = actions;
}

function Child(id) {
    Parent.apply(this, Array.prototype.slice.call(arguments, 1));
    this.id = id;
}

const c1 = new Child(1, 'coco', ['eat', 'sleep']);
复制代码

隐含问题:

1、属性或者方法如果被继承的话,只能在构造函数内被定义,如果方法在构造函数内定义了,那么每次创建实例都会创建一遍方法,多占用一块内存。

组合式继承

原型链继承实现了基本的继承,但是方法存在Prototype上,子类可以直接调用,但是引用类型的属性会被所有实例共享,并且不能传参。

构造函数继承解决了原型链继承的两个问题,但是构造函数内的方法会被重复创建,导致内存占用过多。

funtion Parent(name, actions) {
    this.name = name;
    this.actions = actions;
}

Parent.prototype.eat = function() {
    console.log(`${this.name} -- eat`);
}

function Child(id, name, actions) {
    this.id = id;
    Parent.apply(this, Array.from(arguments).slice(1));
}

Child.prototype = new Parent();
Child.prototype.constructor = child;

const c1 = new Child(1, 'Chris', ['hhhh']);
复制代码

隐含问题:

调用了两次父的构造函数,做了无意义的操作。

寄生组合式继承

funtion Parent(name, actions) {
    this.name = name;
    this.actions = actions;
}

Parent.prototype.eat = function() {
    console.log(`${this.name} -- eat`);
}

function Child(id, name, actions) {
    this.id = id;
    Parent.apply(this, Array.from(arguments).slice(1));
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = child;

const c1 = new Child(1, 'Chris', ['hhhh']);
复制代码

Class继承

class Parent {
    constructor() {
        this.name = 'Vivi';
    }

    getName() {
        console.log('getName');
    }
}

class Child extends Parent {
    constructor() {
        super();
    }
}

const c1 = new Child();
c1.getName(); // getName
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享