龙空技术网

JS面向对象三大特性(内含ES5、ES6实现)

尚硅谷教育 75

前言:

此刻同学们对“js如何实现函数重载操作”可能比较关注,看官们都想要学习一些“js如何实现函数重载操作”的相关内容。那么小编也在网络上网罗了一些关于“js如何实现函数重载操作””的相关知识,希望看官们能喜欢,各位老铁们一起来学习一下吧!

所谓面向对象编程是一种程序设计思想。它将各种复杂的关系,抽象为一个个对象,然后通过对象之间的分工与合作实现编程目标。它有一个标志,即类的概念,通过类可以创建任意多个具有相同属性和方法的对象。

JS面向对象和Java一样有三大特征,都是封装、继承和多态,只不过类的概念在ES6之后才出现。这里我主要跟大家谈一谈三大特性的理解。

一、封装

封装的本质是把抽象出来的属性和对方法组合在一起,只对外暴露指定的接口,且属性值被保护在内部, 只有通过接口进行修改和读取,不用考虑内部如何实现。函数就是最基础的代码封装形式,面向对象思想中的封装仍以函数为基础,但提供了更高级的封装形式。

优势

良好的封装能够减少耦合(代码与界面);

内部的方法实现机制是可以自由修改的,并且不会向外呈现,外部只需传入必要的参数即可;

类具有清晰的对外接口(确定内容的访问属性,是公有还是私有;字段是只读、只写或者是可读可写等)。

ES5实现

我们先来回顾一下以往代码封装的形式:

// 普通对象(命名空间)形式的封装

let person = {

name: "Tom",

setName: function (name) {

this.name = this.name;

},

getName() {

console.log(this.name);

},

};

person.setName("Jack");

person.getName();

以往以普通对象(命名空间)形式封装的代码只是单纯把一系列的变量或函数组合到一起,所有的数据变量都被用来共享(使用 this访问)。

ES6实现

ES6新增了class来实现类的封装:

class Person {

constructor(name,age){

this.name = name;

this.age = age;

}

getInfo(){

let {name,age} = this;

return {name,age};

}

static sayHi(){ //这里使用了static对sayHi方法对其进行了访问权限的封装。

console.log("Hi");

}

}

let Tom= new Employees("Tom",18);

Tom.sayHi(); // Tom.sayHi is not a function

Tom.getInfo(); // {name: "Tom", age: 18}

Person.sayHi(); // Hi

在Person中抽出的公共属性有name,age,公共方法有getInfo,sayHi,然而getInfo与sayHi所不同的是sayHi使用了static修饰符,改变其为静态方法,sayHi只属于Person这个类。然而getInfo方法则是属于实例的。

二、继承

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

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

优势

继承使得所有的子类的公共部分都放在了父类中,使得代码得到了共享。提高了代码的重用性,避免了重复书写;

继承可使得修改或扩展继承而来的实现都较为容易;

使类与类之间产生联系,为多态提供了前提。

缺点

继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;

降低了代码的灵活性。子类必须拥有父类的属性和方法,让子类多了一些冗余的属性;

增强了类之间的耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能造成大段的代码需要重构。

ES5实现

在ES5中我们主要采用原型链+构造函数组合的方式来实现继承:

// 父类型

function Person(name, age) {

this.name = name;

this.age = age;

}

Person.prototype.fn = function () {

console.log(1);

};

Person.prototype.sayHello = function () {

console.log(`我叫${this.name}, 今年${this.age}岁`);

};

// 子类型

function Student(name, age, score) {

// 借用父类型的构造函数

Person.call(this, name, age);

this.score = score;

}

// 让子类的原型为父类的实例

Student.prototype = new Person();

// 让原型对象的构造器为子类型

Student.prototype.constructor = Student;

const Tom = new Student("Tom", 23, 95);

Tom.sayHello(); // 我叫Tom, 今年23岁

Tom.fn(); // 1

通过父类构造继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。

ES6实现

ES6提供了extends关键字实现继承:

// 父类

class Person2 {

constructor(name, age) {

this.name = name;

this.age = age;

}

fn() {

console.log(1)

}

sayHello() {

console.log(`我叫${this.name}, 今年${this.age}岁`);

}

static sayHi () {

console.log('Hi')

}

}

// 子类

class Teacher extends Person2 {

constructor(name, age, course) {

super(name, age);

this.course = course;

}

// 重写父类的方法

sayHello() {

console.log(

`我叫${this.name}, 今年${this.age}岁, 教${this.course}`

);

}

}

const Jack = new Teacher("Jack", 34, "English");

Jack.sayHello(); // 我叫Jack, 今年34岁

Jack.fn(); // 1

Jack.sayHi(); // Jack.sayHi is not a function(报错)

从上面的例子可以看出继承不会继承父类的静态方法,只会继承父类的公有属性与方法。这一点需要注意。

三、多态

从字面意义上来看,多态即多种状态。说白了就是相同的事物,调用其相同的方法,参数也相同时,作用在不同的对象表现的行为却不同。它主要表现在方法的重载和重写上。

优势

提高了代码的维护性(继承保证)。

提高了代码的可扩展性(有多态保证)。

优点:多态实现了方法的个性化,不同的子类根据具体状况可以实现不同的方法,光有父类定义的方法不够灵活,遇见特殊状况就捉襟见肘了

重载

重载是有多个同名函数,但参数列表不同,调用时根据参数的多少动态匹配函数执行。

但JS中不存在真正意义上的重载,因为JS中的同名函数,后者会覆盖前者。JS中的重载指的是同一个方法,根据传参不同(可以是参数类型或参数个数不同),实现出不同的效果。因此JS就需要开发者自己编写逻辑完成重载。

这里我们举一个参数类型不同的例子:

class Person {

constructor(arg) {

let obj = null;

switch (typeof arg) {

case "string":

console.log(arg);

obj = new StringPerson(arg);

break;

case "object":

console.log(arg);

obj = new ObjPerson(arg);

break;

case "number":

console.log(arg);

obj = new NumberPerson(arg);

break;

}

return obj;

}

}

class ObjPerson {

constructor(arg) {

console.log(arg, "ObjPerson");

}

}

class StringPerson {

constructor(arg) {

console.log(arg, "StringPerson");

}

}

class NumberPerson {

constructor(arg) {

console.log(arg, "NumberPerson");

}

}

new Person({ type: "ObjPerson" }); // {type: 'ObjPerson'} 'ObjPerson'

new Person("12345"); // 12345 StringPerson

new Person(12345); // 12345 'NumberPerson'

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

重写

重写是在子类中定义与父类同名的方法从而覆盖父类方法。

在上面继承的案例中,我们就可以重新定义sayHello方法实现重写:

class Person2 {

constructor(name, age) {

this.name = name;

this.age = age;

}

fn() {

console.log(1)

}

sayHello() {

console.log(`我叫${this.name}, 今年${this.age}岁`);

}

}

// 子类

class Teacher extends Person2 {

constructor(name, age, course) {

super(name, age);

this.course = course;

}

// 重写父类的方法

sayHello() {

console.log(

`我叫${this.name}, 今年${this.age}岁, 教${this.course}`

);

}

}

const Jack = new Teacher("Jack", 34, "English");

Jack.sayHello(); // 我叫Jack, 今年34岁, 教English

Jack.fn();// 1

通过上面的代码可以看出Teacher继承了Person2,然而子类与父类中都存在sayHello方法,为了满足不同的需求子类继承父类之后重写了sayHello方法,所以在调用的时候会得到重写后的结果,同时子类还拥有父类的fn方法。

四、总结

封装可以实现隐藏代码细节,使得代码模块化;

继承可以对已存在的类进行扩展,从而实现代码复用;

多态即调用相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。多态分为两种,一种是行为多态与对象的多态。

在工作中使用面向对象思想可以提高我们代码的复用性以及维护性,希望这篇文章对大家有所帮助。

标签: #js如何实现函数重载操作 #js函数对象参数有哪些 #js面向对象 #js参数object