共计 3336 个字符,预计需要花费 9 分钟才能阅读完成。
面向对象三大特性就是封装继承和多态,简单理解,对于猫这种动物,它本身就是一个封装好的类,你只需要供它吃喝(输入),它就能表现猫的行为(输出),同时它继承了动物所具有的习性(吃东西等~),而不同的猫因为所处环境或者习性的不同,可能会有不同的表现和行为,这就是多态。
封装
把客观事物封装成抽象的类,隐藏属性和方法的实现细节,仅对外公开接口。
① 在 ES6 之前,没有 class 这个概念,借由原型对象和构造函数来实现
function Cat(name, food) {
this.name = name // 公有属性
this.food = food
}
Cat.prototype.say = function() { // 公有方法
console.log(this.name + "likes eating" + this.food)
}
Cat.see = function() {console.log('这是静态方法,无需实例化可调用')
}
var cat = new Cat("Lazier","mouse")
cat.say() // 实例共享原型属性和方法
② ES6 的 class
class Cat{constructor(name, food){
this.name = name
this.food = food
}
static see() {console.log('这是静态方法,无需实例化可调用')
}
say(){console.log(this.name+"likes eating" + this.food)
}
}
var cat = new Cat("Lazier","mouse")
cat.say()
以上 class 的基本实现原理如下 ↓
var Cat = function(){function Cat(name, food){
this.name = name
this.food = food
}
// 执行挂载函数,创建类
createClass(Cat,[{key:"say",value:function(){console.log(this.name+"likes eating" + this.food)
}}],[{key:"see",value:function(){console.log('这是静态方法,无需实例化可调用')}])
}
// 定义对象属性
let defineProperties = function(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i]
Object.defineProperty(target, descriptor.key, descriptor)
}
}
// 挂载函数,将静态或动态方法分别挂载到 Cat 和 Cat 的 prototype 上
var createClass = function({return function(Constructor,protoProps,staticProps){if(protoProps){ // 原型方法
defineProperties(Constructor.prototype,protoProps)
}
if(staticProps){ // 静态方法
defineProperties(Constructor,staticProps)
}
}
})
了解面向对象的公有、私有、静态属性和方法可以看下面这篇文章的总结
js 面向对象之公有、私有、静态属性和方法详解
继承
子类可以使用父类的所有功能,并且对这些功能进行扩展。继承的过程,就是从一般到特殊的过程。
js 实现继承有多种方式
原型链继承
// 将子类的 prototype 指向父类的实例
function Parent(){}
function Son(){}
Son.prototype = new Parent()
// * 把 Son 的原型对象的 constructor 指向 Son,解决类型判断问题
Son.prototype.constructor = Son
借助构造函数继承(使用 call 和 apply 实现继承)
function Parent(){}
function Son(){
// 将父类函数中的 this,强行绑定为子类的 this
// 可传参
Parent.call(this, arguments);
}
组合继承
// 原型属性方法由原型链实现继承,实例属性方法由借用构造函数实现继承
// 这样,在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性
function Parent(name){this.name = name}
function Son(name, age){
// 继承父类的实例属性方法,之后再添加自己的实例属性方法
Parent.call(this, name);
this.age = age
}
Son.prototype = new Parent()
// 重写 Son 的原型对象
Son.prototype.constructor = Son
var demo = new Son("jacksonzhou", 23)
寄生式组合继承 – 现在最常用的继承方法
// 获得父类原型属性方法的副本,解决组合继承的属性重复问题
function inheritPrototype(son, parent) {var prototype = object(parent.prototype)
prototype.constructor = son
son.prototype = prototype
}
function Parent(name){this.name = name}
function Son(name, age){Parent.call(this, name)
this.age = age
}
Son.prototype = inheritPrototype(Son, Parent)
var demo = new Son("jacksonzhou", 23)
多态
同一操作用在不同对象上,可以产生不同的解释和不同的执行结果
var makeSound=function(animal){animal.sound()
}
// 声明狗的构造函数
var Dog=function(){}
Dog.prototype.sound=function(){console.log('汪汪汪')
}
// 声明猫的构造函数
var Cat=function(){}
Cat.prototype.sound=function(){console.log('喵喵喵')
}
// 分别调用他们的叫法
makeSound(new Dog())
makeSound(new Cat())
// 非多态写法
var makeSound=function(animal){if(animal instanceof Dog){console.log('汪汪汪')
}else if(animal instanceof Cat){console.log('喵喵喵')
}
}
var Dog=function(){}
var Cat=function(){}
// 分别调用他们的叫法
makeSound(new Dog())
makeSound(new Cat())
// 很明显,后续有其他动物加入都要去修改 makeSound 函数,很不优雅!这里要介绍下方法重载
方法重载是让类以统一的方式处理不同类型数据的一种手段。表现为多个同名函数同时存在,但具有不同的参数个数或类型。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这也是一种多态性。其实 js 本身并没有这个概念,但我们可以通过操作参数的类数组 arguments,根据该类数组的长度以及其元素的类型来选择不同的实现,来模拟实现函数重载效果
// js 的函数参数相当灵活~ 可理解成一个动态的类数组
// 不加参数,调用时有传入参数也不会报错
function countCat(){if(arguments.length==1){console.log(` 这是一只猫,${arguments[0]}`)
}
else if(arguments.length==2){console.log(` 这是两只猫,${arguments[0]}和 ${arguments[1]}`)
}
else{console.log("没猫了~")
}
}
countCat()
countCat("Tom")
countCat("Tom","Mary")
正文完
发表至: javascript
2019-08-22