likes
comments
collection
share

ES6-面向对象编程思想

作者站长头像
站长
· 阅读数 77

概念

面向对象的思想和类紧密相关。

把客观对象抽象成属性数据和对数据的相关操作,进而隐藏内部细节和不相关的信息,把同一个类型的客观对象进行总结,抽象出属性和属性的操作,封装成类。允许类通过继承实现属性和操作的共享。

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

ES5实现面向对象

function Person(name) {
    //构造函数⾥⾯的⽅法和属性
    this.name = name;
    this.getName = function() {
        console.log(this.name);
    };
    this.setName = function(name) {
        this.name = name;
    }
}

let p = new Person("张三");
p.getName();  //张三
p.setName("李四");
p.getName();  //李四
function Person(name) { 
    this.name = name
};
Person.prototype.say = function() { 
    console.log('hello',this.name)
}
 
var per = new Person('章三');
per.say()

ES6实现面对对象

首先你要知道js里面没有类的概念 ,所谓的面向对象都是模拟出来的。类的本质其实还是⼀个函数,我们也可以简单的认为,类就是函数的另外⼀种写法。ES6里边的类实际上都是函数的语法糖。

class Person {
    //类的构造函数,实例化的时候执⾏new的时候执⾏
    constructor(name) {
        this.name = name;
    };
    getName() {
        console.log(this.name);
    };
    setName(name) {
        this.name = name
    }
}

let p = new Preson('张三')
p.getName();  //张三
p.setName('李四');
p.getName();  //李四

面向对象三个基本特征是:封装、继承、多态。

封装

在说封装之先了解一下封装到底是什么?

我们平时所⽤的⽅法和类都是⼀种封装,当我们在项⽬开发中, 遇到⼀段功能的代码在好多地⽅重复使⽤的时候,我们可以把他单独封装成⼀个⽅法,这样在我们需要使⽤的地⽅直接调⽤就可以了。

在面向对象里边,封装通常是指创建一个对象集中保存一个事物的属性与功能。将对象运行所需的资源封装在一段程序中——基本上就是是方法和数据。

如果更进一步,对于封装的理解,可能还有一个层面就是抽离,首先你要清楚在一段代码中你应该抽离那些属性方法,有了这些为基础才能更好的做好封装。封装无非就是其属性和方法封装。

类:封装对象的属性和行为

方法:封装具体逻辑功能

访问封装:访问修饰封装无非就是对其访问权限进行封装

class Employees {     
    constructor(name,age) {         
        this.name = name;         
        this.age = age;     
    }     
    getInfo() {         
        let {name,age} = this;         
        return {name,age};     
    };    
    static seyHi() {         
        console.log("Hi");        
    } 
}
let lisi = new Employees("Aaron",18); 
lisi.seyHi();   //lisi.seyHi is not a function 
lisi.getInfo();  //{name: "Aaron", age: 18} 
Employees.seyHi()  //Hi1234567891011121314151612345678910111213141516

分析:在Employees中抽出的公共属性有name,age,公共方法有getInfo,seyHi,然而getInfo与seyHi所不同的是seyHi使用了static修饰符,改变其为静态方法,seyHi只属于Employees这个类。然而getInfo方法则是属于实例的。这里使用了static对seyHi方法对其进行了访问权限的封装。

Promise.then()  //Promise.then is not a function 
let p1 = new Promise(() => {}) 
p1.then();  //Promise {<pending>} 
Promise.all([1])   //Promise {<resolved>: Array(1)}123123

从上面的代码中可以看出Promise也使用了static对其方法的访问权限进行了封装。

继承

继承是指父亲的成员,孩子无需重复创建就可直接使用。继承实现了代码重用,节约内存。

class Father {
    constructor(name) {
        this.name = name;
    };
    //实例方法,通过实例对象调⽤
    getName() {
        console.log(this.name);
    };
    // 静态⽅法不会被继承,并且是通过类名去调⽤的
    static hitXiaoMing() {
        console.log("打⼩明");
    }
};
class Son extends Father {
    constructor(name, age) {
    //实例化⼦类的时候把⼦类的数据传给⽗类(这里的super必须有,super里的参数是所继承的⽗类实例化所需要的数据)
        super(name);
        this.age = age;
    } 
}
var DaMing = new Father('⼤明');
Father.hitXiaoMing(); //打⼩明
DaMing.getName(); //⼤明
var XiaoMing = new Son('⼩明',15);
XiaoMing.getName() //⼩明

继承可以使得子类具有父类的各种的公有属性和公有方法。而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。

子类继承父类后,子类具有父类属性和方法,然而也同样具备自己所独有的属性和方法,也就是说,子类的功能要比父类多或相同,不会比父类少。

class Employees {     
    constructor(name) {         
        this.name = name;     
    };     
    getName() {         
        console.log(this.name)     
    };    
    static seyHi() {         
        console.log("Hi");        
    }; 
}; 
class Java extends Employees {     
    constructor(name) {         
        super(name);     
    };    
    work() {         
        console.log("做后台工作");
    } 
}
let java = new Java("Aaron"); 
java.getName(); 
java.work(); 
java.seyHi() //java.seyHi is not a function

从上面的例子可以看出继承不会继承父类的静态方法,只会继承父类的公有属性与方法,这一点需要注意。子类继承之后既拥有了getName方法,同样也拥有自己的worker方法。

多态

按字面的意思就是“多种状态”,说白了多态就是相同的事物,一个接口,多种实现,同时在最初的程序设定时,有可能会根据程序需求的不同,而不确定哪个函数实现,通过多态不需要修改源代码,就可以实现一个接口多种解决方案。

多态的具体表现为⽅法重载和⽅法重写。

⽅法重载: 重载是指不同的函数使⽤相同的函数名, 但是函数的参数个数或类型不同。调⽤的时候根据函数的参数来区别不同的函数

⽅法重写:子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。

重写 (也叫覆盖) 是指在派⽣类中重新对基类中的虚函数 (注意是虚函数) 重新实现。即函数名和参数都⼀样, 只是函数的实现体不⼀样。

class Father {
    constructor(name) {
        this.name = name;
    };
    //实例⽅法,通过实例对象调⽤
    getName() {
        console.log(this.name);
    };
    work() {
        console.log('我的⼯作是累死累活,赚钱养家');
    };
    // 静态⽅法不会被继承,并且是通过类名去调⽤的
    static hitXiaoMing() {
        console.log("打⼩明");
    }
};
class Son extends Father {
    constructor(name, age) {
    //实例化⼦类的时候把⼦类的数据传给⽗类(这⾥的super必须有,super⾥的参数是所继承的⽗类实例化所需要的数据)
        super(name);
        this.age = age;
    };
    work() {
        console.log('我的⼯作是好好学习,天天向上。');
    };
}
var DaMing = new Father('⼤明');
DaMing.work() // 我的⼯作是累死累活,赚钱养家。
var XiaoMing = new Son('⼩明',15);
XiaoMing.work(); // 我的⼯作是好好学习,天天向上。
class Employees {     
    constructor(name) {         
        this.name = name;     
    };     
    seyHello() {         
        console.log("Hello");     
    };     
    getName() {         
        console.log(this.name);     
    };
}; 
class Java extends Employees {     
    constructor(name) {         
        super(name);     
    };     
    seyHello(){         
        console.log(`Hello,我的名字是${this.name},我是做Java工作的。`);     
    }; 
}

const employees = new Employees("Aaron"); 
const java = new Java("Leo"); 
employees.seyHello(); //Hello 
java.seyHello(); //Hello,我的名字是Leo,我是做Java工作的。 
employees.getName(); //Aaron 
java.getName() //Leo

重载

JS没有重载的概念。原因是JS的函数只看真实的传参数。重载就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。

class Employees {     
    constructor(arg) {         
        let obj = null;         
        switch(type of arg) {             
            case "string":                   
                obj = new StringEmployees(arg);                   
                break;             
            case "object":                   
                obj = new ObjEmployees(ObjEmployees);                   
                break;             
            case "number":                 
                obj = new NumberEmployees(ObjEmployees);                 
                break;         
        }         
        return obj;     
    } 
}; 
class ObjEmployees {     
    constructor(arg) {         
        console.log("ObjEmployees")     
    } 
}; 
class StringEmployees {     
    constructor(arg) {         
        console.log("StringEmployees")     
    } 
}; 
class NumberEmployees {     
    constructor(arg) {         
        console.log("NumberEmployees")     
   } 
} 

new Employees({});   // ObjEmployees 
new Employees("123456"); // StringEmployees 
new Employees(987654)   // NumberEmployees

因为JavaScript是没有重载的概念的所以要自己编写逻辑完成重载。

在上面的代码中定义了Employees,ObjEmployees,StringEmployees,NumberEmployees类,在实例化Employees的时候在constructor里面进行了判断,根据参数的不同返回不同的对应的类,这样完成了一个简单的类重载。

面向过程和面向对象

// 面向对象事实上是和面向过程来对比的。
// 面向对象和面向过程是两种不同思路的编程思想。

// 经典案例:把大象塞进冰箱
// 面向过程的思路: 经典的编程语言C
// 第一步 打开冰箱门
// 第二步 把大象放进去
// 第三步 关闭冰箱门

// 伪代码
// 打开门
    // 检测冰箱门是否可以打开
    // 启动降温系统
    // 操控冰箱门压力阀
    // 打开冰箱
// 放大象
    // 把大象放进去
    // 检查冰箱内的温度
    // 调成合适的温度
// 关闭冰箱门
    // 看是否能够关紧
    // 关闭后的温度调节
    // 提示已经关门成功
 
// 面向对象的思路 封装一个对象 让对象去做 
// 1、封装对象 =》冰箱
// 冰箱可以打开门 (方法封装)
    // 检测冰箱门是否可以打开
    // 启动降温系统
    // 操控冰箱门压力阀
    // 打开冰箱
 
//冰箱可以关上门 (方法封装)
    // 看是否能够关紧
    // 关闭后的温度调节
    // 提示已经关门成功
//冰箱可以放东西 (方法封装)
    // 把大象放进去
    // 检查冰箱内的温度
    // 调成合适的温度
 
// 2、调用冰箱的开门方法
// 3、调用冰箱的存放的方法
// 4、调用冰箱的关门方法

// 下一次,我不放大象了,我把长颈鹿放进去,我不需要去写冰箱开门关门放东西的方法,只需要直接调用,达到了代码复用的效果。
// 1、调用冰箱的开门方法
// 2、调用冰箱的存放的方法
// 3、调用冰箱的关门方法

总结

1、封装可以隐藏实现细节,使得代码模块化。封装的优势在于只可以在类内部进⾏对属性的操作,外部只能通过类实现的方法进行操作,相当于对类的操作进行了规范。

2、继承可以扩展已存在的代码模块(类),它们的目的都是为了——代码重用。减少了代码的冗余,省略了很多重复代码,开发者可以从⽗类底层定义所有⼦类必须有的属性和⽅法,以达到减少耦合的⽬的。

3、多态就是相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。多态实现了⽅法的个性化, 不同的⼦类根据具体状况可以实现不同的⽅法,光有⽗类定义的⽅法不够灵活, 遇见特殊状况就捉襟见肘了。在编程的是多多运用这个写思想对其编程时很有用的,能够使你的代码达到高复用以及可维护。

转载自:https://juejin.cn/post/7151996502292840456
评论
请登录