前言:
此刻同学们对“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