JS多态封装继承

更新日期: 2019-11-04阅读: 2.1k标签: 封装

前言

JS是一种基于对象的语言,在JS中几乎所有的东西都可以看成是一个对象,但是JS中的对象模型和大多数面向对象语言的对象模型不太一样,因此理解JS中面向对象思想十分重要,接下来本篇文章将从多态、封装、继承三个基本特征来理解JS的面向对象思想


多态

含义

同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果,也就是说,给不同的对象发送同一个消息时,这些对象会根据这个消息分别给出不同的反馈。
举个例子:假设家里养了一只猫和一只狗,两只宠物都要吃饭,但是吃的东西不太一样,根据主人的吃饭命令,猫要吃鱼,狗要吃肉,这就包含了多态的思想在里面,用JS代码来看就是:

let petEat = function (pet) {
  pet.eat()
} 
let Dog = function () {}
Dog.prototype.eat = function () {
  console.log('吃肉')
}
let Cat = function () {}
Cat.prototype.eat = function () {
  console.log('吃鱼')
}

petEat(new Dog())
petEat(new Cat())

上面这段代码展示的就是对象的多态性,由于JS是一门动态类型语言,变量类型在运行时是可变的,因此一个JS对象既可以是Dog类型的对象也可以是Cat类型的对象,JS对象多态性是与生俱来的,而在静态类型语言中,编译时会进行类型匹配检查,如果想要一个对象既表示Dog类型又表示Cat类型在编译的时候就会报错,当然也会有解决办法,一般会通过继承来实现向上转型,这里感兴趣的可以去对比一下静态语言的对象多态性。

作用

多态的作用是通过把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支语句,举个例子:还是上面宠物吃饭的问题,如果在没有使用对象的多态性之前代码可能是这样是的:

let petEat = function (pet) {
  if (pet instanceof Dog) {
    console.log('吃肉')
  } else if (pet instanceof Cat) {
    console.log('吃鱼')
  }
}
let Dog = function () {}
let Cat = function () {}
petEat(new Dog())
petEat(new Cat())

通过条件语句来判断宠物的类型决定吃什么,当家里再养金鱼,就需要再加一个条件分支,随着新增的宠物越来越多,条件语句的分支就会越来越多,按照上面多态的写法,就只需要新增对象和方法就行,解决了条件分支语句的问题


封装

封装的目的是将信息隐藏,一般来说封装包括封装数据、封装实现,接下来就逐一来看:

封装数据

由于JS的变量定义没有private、protected、public等关键字来提供权限访问,因此只能依赖作用域来实现封装特性,来看例子

var package = (function () {
  var inner = 'test'
  return {
    getInner: function () {
      return inner
    }
  }
})()
console.log(package.getInner()) // test
console.log(package.inner) // undefined

封装实现

封装实现即隐藏实现细节、设计细节,封装使得对象内部的变化对其他对象而言是不可见的,对象对它自己的行为负责,其他对象或者用户都不关心它的内部实现,封装使得对象之间的耦合变松散,对象之间只通过暴露的api接口来通信。
封装实现最常见的就是jquery、Zepto、Lodash这类JS封装库中,用户在使用的时候并不关心其内部实现,只要它们提供了正确的功能即可


继承

继承指的是可以让某个类型的对象获得另一个类型的对象的属性和方法,JS中实现继承的方式有多种,接下来就看看JS实现继承的方式

构造函数绑定

这种实现继承的方式很简单,就是使用call或者apply方法将父对象的构造函数绑定在子对象上,举个例子:

function Pet (name) {
  this.type = '宠物'
  this.getName = function () {
    console.log(name)
  }
}
function Cat (name) {
  Pet.call(this, name)
  this.name = name
}
let cat = new Cat('毛球')
console.log(cat.type) // 宠物
cat.getName() // 毛球

通过调用父构造函数的call方法实现了继承,但是这种实现有一个问题,父类的方法是定义在构造函数内部的,对子类是不可见的

原型继承

原型继承的本质就是找到一个对象作为原型并克隆它。这句话怎么理解,举个例子:

function Pet (name) {
  this.name = name
}
Pet.prototype.getName = function () {
  return this.name
}
let p = new Pet('毛球')
console.log(p.name) // 毛球
console.log(p.getName()) // 毛球
console.log(Object.getPrototypeOf(p) === Pet.prototype) // true

上面这段代码中p对象实际上就是通过Pet.prototype的克隆和一些额外操作得来的,有了上面的代码基础,接下来来看一个简单的原型继承代码:

let pet = {name: '毛球'}
let Cat = function () {}
Cat.prototype = pet
let c = new Cat()
console.log(c.name) // 毛球

来分析一下这段引擎做了哪几件事:

  • 首先遍历c中的所有属性,但是没有找到name属性
  • 查找name属性的请求被委托给对象c的构造器原型即Cat.prototype,Cat.prototype是指向pet的
  • 在pet对象中找到name属性,并返回它的值

上面的代码实现原型继承看起来有点绕,实际上在es5提供了Obejct.create()方法来实现原型继承,举个例子:

function Pet (name) {
  this.name = name
}
Pet.prototype.getName = function () {
  return this.name
}
let c = Object.create(new Pet('毛球'))
console.log(c.name) // 毛球
console.log(c.getName()) // 毛球

组合继承

组合继承即使用原型链实现对原型属性和方法的继承,通过构造函数实现对实例属性的继承,举个例子:

function Pet (name) {
  this.name = name
}
Pet.prototype.getName = function () {
  return this.name
}
function Cat (name) {
  Pet.call(this, name)
}
Cat.prototype = new Pet()
let c = new Cat('毛球')
console.log(c.name) // 毛球
console.log(c.getName()) // 毛球


总结

本篇文章主要介绍了JS面向对象编程思想的多态封装继承的特性,这里只做了浅析,想要深挖还需要更深入的学习,希望看完本篇文章对理解JS面向对象编程思想有所帮助


链接: https://www.fly63.com/article/detial/6265

原生js实现ajax请求封装,类似于jquery的操作

ajax可以实现不阻塞用户,达到无刷新更新部分页面的效果,它的异步请求都会首先考虑的是jquery,但是前提得加载它那核心包,如何使用原生的js来实现这一功能呢?

通过2行js代码实现DOM属性的封装以及交换两个变量的值

JavaScript两行代码实现DOM属性的封装以及交换两个变量的值。编程的时候不要忘记思考,否则你就只是一个完成工作的机器。所以,如果你现在的工作只是让你疯狂做业务而不给你思考和学习的时间,别犹豫,换一个。

浅谈Vue v-model实现原理,如何封装,以及封装方法

vue的v-model是一个十分强大的指令,它可以自动让input里的值自动和你设定的值进行绑定,它是如何实现的呢?其实v-model只不过是一个语法糖而已,真正的实现靠的还是:(1) v-bind:绑定响应式数据,(2) 触发 input 事件 并传递数据 (重点)

如何去设计一个组件封装_前端组件化设计思路

目前前端三大框架(Vue.js, Angular.js, React.js)都在引领着前端的组件化开发方向,组件化的前端开发方式的确为业务实现带来了前所未有的方便,其实组件化开发早已经具有(YUI),但如何封装一个优秀的组件,可能并不是每位前端开发者都能够做好的。

fetch请求二次封装

JS 中使用 fetch 更加高效地进行网络请求,由于fetch 的请求方式同 $ajax 和 axios 都不太一样,为了方便请求过程,进行Fetch网络封装类:方式一传统使用回调函数的方式、方式二借助ES6中的promise的方式封装。

关于那些变化万千,开箱即用的 Promise 高度封装方法

在日常开发中,我们少不了使用 Promise,而透过封装抽象方法,可以避免造轮子,写出更加优雅的代码。例如:将任意函数,化为具有异步能力的函数、改装成具有并发上千万请求的函数、使用 Web Worker 来使用不同线程、暂停若干秒再继续

微信和支付宝中的一些常用方法封装

最近做了同一个样子的小程序的支付宝版本,现在如果想想是开发两次的话心里应该是很难受的。去年11月12月份左右开发了两个微信小程序是一个在超市买商品的,一个用户版本,一个商户版本的。我们团队看到了uniapp这个东西

JS防抖函数、节流函数工具类封装

当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次, 如果设定时间到来之前,又触发了事件,就重新开始延时。也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于既定时间

JQuery组件封装之return this.each(function () {});

这个时候就要说each了,还是之前的,倘若页面上有N个class为div的元素呢,即:this.length>1,这里each就必然要上场了,且每个对象都要返回,所以此段代码无疑是最方便的写法了

原生js实现ajax的封装

ajax 的全称是Asynchronous JavaScript and XML,简单理解下:ajax就是异步的js和服务端的数据;GET请求的数据会附在URL之后, 以 ? 分割URL和传输数据, 参数之间以 & 相连

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!