js面向对象编程OOP及函数式编程FP区别


Posted in Javascript onJuly 07, 2022

写在前面

浏览下文我觉得还是要有些基础的!下文涉及的知识点太多,基本上每一个拿出来都能写几篇文章,我在写文章的过程中只是做了简单的实现,我只是提供了一个思路,更多的细节还是需要自己去钻研的,文章内容也不少,辛苦,如果有其他的看法或者意见,欢迎指点,最后纸上得来终觉浅,绝知此事要躬行

javscript 中函数和对象的关系

javscript 一切皆为对象,但基本类型之外,函数是对象,对象是由函数创建而来, 从而衍生出我对这两种编程方式的探讨。下面对类型判断和原型做了一个简单的表述,这里不是重点,不做具体的表述,感兴趣的可以自己百度/谷歌。

// 类型判断
// 基本类型
console.log(typeof 1)                               // | ==> number
console.log(typeof '2')                             // | ==> string
console.log(typeof undefined)                       // | ==> undfined
// null 类型判断【特殊】
console.log(typeof null)                            // | ==> object
console.log(Object.prototype.toString.call(null))   // | ==> [object Null]
// 报错【null 不是一个对象】TypeError: Right-hand side of 'instanceof' is not an object
console.log(null instanceof null)                   
console.log(typeof Symbol())                        // | ==> symbol 【ES6 新类型】
console.log(typeof false)                           // | ==> boolean
console.log(typeof BigInt(9007199254740991n))       // | ==> bigint 【新类型】
// 引用类型 - 对象
console.log(typeof (() => {}))                      // | ==> function
console.log((() => {}) instanceof Object)           // true
console.log(typeof [])                              // | ==> object
console.log(typeof {})                              // | ==> object
console.log(typeof (/\./))                          // | ==> object
console.log(typeof new Date())                      // | ==> object
console.log(typeof new String())                    // | ==> object
console.log(typeof new Number())                    // | ==> object
// 原型链
// fn ====> function fn () {}
// Object ====> function Object () {}
// Function ====> function Funtion()
    new fn() - __proto__ --|
      ↑                    ↓
---→ fn ----------- fn.prototype -------- __proto__ -----→ Object.prototype -- __proto__--→ null
      |                                                                  ↑
      |--------- __proto__ ------→ Function.prototype --- __proto__ -----|
                                        ↑
                  Function -------→ __proto__
                                        |
                                      Object

面向对象编程(OOP)

在面向对象编程中最常见的表现形式就是类,提供了面向对象的 3⃣ 大特点和 5⃣️ 大原则,这东西网上特别多,我只做简单的罗列,下面我会对特点进行实现,我的理解: 原则是面向对象编程的规范,而特点是面向对象编程的实现,前提是你已经仔细理解过下面对核心概念。

三大特点

  • 继承
  • 多态
  • 封装

五大原则

  • 单一 【一个类应该有且只有一个去改变它的理由,这意味着一个类应该只有一项工作】
  • 开放封闭 【对象或实体应该对扩展开放,对修改封闭。】
  • 里氏替换 【即对父类的调用同样适用于子类】
  • 依赖倒置 【高层次的模块不应该依赖于低层次的模块】
  • 接口隔离 【不应强迫客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不使用的方法】

继承

继承是面向对象一个特点,可以实现子类调用自己没有的属性方法【父类属性方法】

/** ES6 **/
class Parent {}
class Child extends Parent { constructor () { super() } }
/** ES5 **/
function parent () { this.run () {} }
parent.prototype.eat = function () {}
function child () {}
// 原型式继承
child.prototype = parent.prototype
child.prototype.constructor = child
// 原型链继承
child.prototype = new parent()
child.prototype.constructor = child
// 构造器继承
function boyChild (..arg) { parent.apply(this, arg) }
// 组合继承
function boyChild (..arg) { parent.apply(this, arg) }
boyChild.prototype = new parent()
child.prototype.constructor = child
// 寄生组合继承
function child (..arg) { parent.apply(this, arg) }
// ${1}
(
  function () { 
    function transmit () {};
    transmit.prototype = parent.prototype
    child.prototype = new prototype()
    child.prototype.constructor = child
  }
)()
// ${2}
child.prototype = Object.create(parent.prototype)
// ......
// 总结
// 继承的方式方法多种多样,不外乎,就是通过,某一种方式将不属于自己的属性方法可以调用,沿着原型的方式和拷贝赋值就可以总结出很多种不同的继承方式,每种方式的优缺点,多是考虑,继承的属性方法的完整性和对实例化对象的影响,如实例上方法和原型链上方法是否都可以调用有或者引用传递改变同一原型链问题。
/** 上面为对实例对继承,下面说一说对于接口对继承 **/
// ES6 中并没有提供接口这个概念,但是 Typescript 中对于接口又很好对支持,typescript 是 javascript 对超集,对面向对象提供了非常好对支持
// Typescript 【一时用一时爽,一直用一直爽】
// 很推荐用这个,他能避免很多低级错误,提供类型检查,特别是写过 java 转前端的。
interface parent { run: () => void }
class child implements parent { run () {} }
// 转码后
var child = /** @class */ (function () {
    function child() {
    }
    child.prototype.run = function () { };
    return child;
}());

多态

多态是面向对象一个特点,可以实现子类有不同对表现形态,可以实现同一种表现形式,可以有不同对状态

/** ES6 **/
// ${1} 重写
class Animal {
  eat () { console.log('animal eat') }
}
class Pig extends Animal {
  constructor () { super() }
  eat () { console.log('pig eat grass') }
}
class Tiger extends Animal {
  constructor () { super() }
  eat () { console.log('tiger eat pig') }
}
// ${2} 重载,模拟实现
class Animal {
  eat () { 
    if (typeof arg === '') {
      console.log('操作 one')
    } else if (typeof arg === '') {
      console.log('操作 two')
    } else {
      console.log('操作 three')
    }
  }
}
/** ES5 【提供实现一种】**/
// 原理就是沿着原型链往上找,只要在父类前定义重写这个方法即可
// ${1} 重写
function animal () { this.eat = function () { console.log('Animal eat') } }
function pig () {
  animal.call(this)
  this.eat = function () { console.log('pig eat grass') }
}
function tiger () {
  animal.call(this)
  this.eat = function () { console.log('tiger eat pig') }
}
// ${2} 重载
function animal () {
  eat () { 
    if (typeof arg === '') {
      console.log('操作 one')
    } else if (typeof arg === '') {
      console.log('操作 two')
    } else {
      console.log('操作 three')
    }
  }
}

封装

封装是面向对象一个特点,将属性和方法封装这对象中,可以利用私有或者公有属性,对外提供可以访问的方法或属性

/** ES6 **/
// ES6 没有提供真正的私有方法和属性,有一个还在提案阶段
// 在属性和方法前面加 #
class Animal {
  #height = ''
  #eat () {}
}
// 模拟实现 【提供一种实现】
class Animal {
  constructor () { this.height = '50' }
  get height() { return undefined }
  set height (value) { return undefined }
}
/** ES5 **/
const animal = (function (arg) {
  let height = 50
  function eat () {console.log(height)}
  return { eat }
})([])
/** Typescript **/
class Animal {
  public height: number
  private name: string
  protected color: string
  constructor (height: number, name: string, color: string) {
    this.height = height
    this.name = name
    this.color = color
  }
  private eat ():void { console.log(this.name) }
}

函数编程编程(FP)

函数式编程提倡函数是第一公民【指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值】,纯粹的函数式编程,是纯函数【如果传入的参数相同,就会返回相同的结果,不依赖于外部的数据状态【如下实例】】,函数编程特点

// 纯函数
const add = (one, two) => { return one + two }
// 非纯函数
let two = 1
const add = (one) => { return one + two }
  • 闭包和高阶函数
  • 柯里化
  • 偏函数
  • 组合和管道
  • 函子

闭包和高阶函数

闭包理解 函数内部还有其他函数,可以使父函数数据状态得以保存 高阶函数理解 函数可以通过变量传递给其他函数

// 利用封包实现一个只能调用一次的 map 高阶函数
const map = (fn) => {
  let once = false
  return (arr) => { return once? null: (once = true, arr.map(fn)) }
}
const fn = (item) => item + 10
const arrMap = map(fn)
arrMap([1, 2, 3]) // [11, 12, 13]
arrMap([4, 5, 6]) // null

柯里化

柯里化理解 柯里化是将一个多元函数转换为嵌套一元函数的过程

function curry (fn) {
  return curryN (...arg) {
    if (arguments.length < fn.length) {
      return function () {
        return curryN.call(null, ...arg.concat(...arguments))
      }
    }
    return fn.call(null, ...arguments)
  }
}
const add = curry ((x, y, z) => x + y + z)
console.log(add(2)(3)(4)) // 9

偏函数

偏函数理解 初始化时指定原函数的一些参数并创建一个新函数,这个函数用于接收剩余参数

function proto(fn, ...pagram) {
  return (...args) => {
    args.forEach((item, index) => { if (item && !pagram[index]) pagram[index] = item })
    return fn.apply(null, pagram)
  }
}
let add = proto((x, y) => { console.log(x + y) }, undefined, 10)
add(2) // 12

组合和管道

组合和管道理解 将一个函数的输出作为另一个函数的输入,像流水一样从函数队列从左到右流动或者从右到左流动

// 单个参数,简单组合
const compose = (fn, fnc) => (arg) => fn(fnc(arg))
// 多个参数,借助偏函数实现
function mapArr(arr, fn) { return arr.map(fn) }
function filte (arr, fn) { return arr.filter(fn) }
let map = proto(mapArr, undefined, (item) => { return item + 10 })
let filter  = proto(filte, undefined, (item) => { return item })
let mapFilter = compose(map, filter)
console.log(mapFilter([1, false, 9, 4])) // [11, 19, 14]
// 多个函数组合
const reduce = (arr, fn, value) => {
  let initValue = value? value: arr[0]
  arr.forEach((item) => { initValue += fn(initValue, item) })
  return initValue
}
const compose = (...arg) => (value) => reduce(arg.reverse(), (acc, fn) => fn(acc), value)
let add = compose(() => { return 1 }, () => { return 2 }, () => { return 3 })
add(6) // 12

函子

函子的定义 函子是一个普通对象(在其他语言中,可能是一个类),它实现了 map 函数,在遍历每个对象值的时候生成一个新对象 很抽象,简单来说 函子是一个持有值的容器。嗨难懂,上代码。

  • 如图[网上所盗]

js面向对象编程OOP及函数式编程FP区别

// 实现一个基本定义的函子,满足定义
// 实现 map 方法,在遍历对象的时候生成一个新对象
function container (value) { this.value = value }
container.prototype.of = function (value) { return new container(value) }
container.prototype.map = function(fn) { return new container().of(fn(this.value)) }
new container().of([1, 5, 7, 3]).map((arr) => { return arr.filter((item) => item === 5)})
console.log(
  new container().of([1, 5]).map((arr) => { return arr.filter((item) => item === 5)}).value
) // 5

写在最后

到此面向对象和函数式编程的基本思想就都简单实现了,更多的需要自行深入学习

上面两种编程方式在学习实践的过程中给我提供了很多解决问题和组织代码框架的思维,在很多开源库中也能看见它们实现的影子,当然真正理解这两种编程方式,谈何容易,更多的是要不断的实践和思考总结,慢慢积累

https://github.com/loo41/Blog

以上就是js面向对象编程OOP及函数式编程FP区别的详细内容,更多关于js面向对象OOP函数式FP区别的资料请关注三水点靠木其它相关文章!


Tags in this post...

Javascript 相关文章推荐
js 面向对象的技术创建高级 Web 应用程序
Feb 25 Javascript
JS实现时间格式化的方式汇总
Oct 16 Javascript
Node.js静态文件服务器改进版
Jan 10 Javascript
javascript实现右侧弹出“分享到”窗口效果
Feb 01 Javascript
js HTML5 Canvas绘制转盘抽奖
Sep 13 Javascript
easyui datebox 时间限制,datebox开始时间限制结束时间,datebox截止日期比起始日期大的实现代码
Jan 12 Javascript
Vue 进阶教程之v-model详解
May 06 Javascript
Vue组件通信之Bus的具体使用
Dec 28 Javascript
微信小程序适配iphoneX的实现方法
Sep 18 Javascript
js计算两个时间差 天 时 分 秒 毫秒的代码
May 21 Javascript
javascript canvas检测小球碰撞
Apr 17 Javascript
如何解决jQuery 和其他JS库的冲突
Jun 22 jQuery
类和原型的设计模式之复制与委托差异
JS高级程序设计之class继承重点详解
Jul 07 #Javascript
JS class语法糖的深入剖析
Jul 07 #Javascript
MutationObserver在页面水印实现起到的作用详解
Jul 07 #Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
You might like
解决phpmyadmin中缺少mysqli扩展问题的方法
2013/05/06 PHP
新浪SAE云平台下使用codeigniter的数据库配置
2014/06/12 PHP
PHP实现的oracle分页函数实例
2016/01/25 PHP
Jquery 弹出层插件实现代码
2009/10/24 Javascript
JavaScript执行效率与性能提升方案
2012/12/21 Javascript
jQuery对象数据缓存Cache原理及jQuery.data方法区别介绍
2013/04/07 Javascript
JS添加删除一组文本框并对输入信息加以验证判断其正确性
2013/04/11 Javascript
JavaScript中的数组操作介绍
2014/12/30 Javascript
JavaScript取得键盘按下方向键是哪个的方法
2015/08/04 Javascript
request请求获取参数的实现方法(post和get两种方式)
2016/09/27 Javascript
js时间控件只显示年月
2017/01/08 Javascript
javascript数据结构中栈的应用之符号平衡问题
2017/04/11 Javascript
使用3D引擎threeJS实现星空粒子移动效果
2020/09/13 Javascript
微信小程序scroll-view组件实现滚动动画
2018/01/31 Javascript
基于Vue实现关键词实时搜索高亮显示关键词
2018/07/21 Javascript
微信小程序--特定区域滚动到顶部时固定的方法
2019/04/28 Javascript
[04:31]2016国际邀请赛中国区预选赛妖精采访
2016/06/27 DOTA
布同 Python中文问题解决方法(总结了多位前人经验,初学者必看)
2011/03/13 Python
pyqt4教程之实现windows窗口小示例分享
2014/03/07 Python
python实现在字符串中查找子字符串的方法
2015/07/11 Python
Python使用win32com实现的模拟浏览器功能示例
2017/07/13 Python
浅谈python中的正则表达式(re模块)
2017/10/17 Python
OpenCV搞定腾讯滑块验证码的实现代码
2019/05/18 Python
python实现一行输入多个值和一行输出多个值的例子
2019/07/16 Python
使用pandas读取文件的实现
2019/07/31 Python
Python pandas自定义函数的使用方法示例
2019/11/20 Python
基于python实现生成指定大小txt文档
2020/07/20 Python
Python基于argparse与ConfigParser库进行入参解析与ini parser
2021/02/02 Python
HTML5 video标签(播放器)学习笔记(二):播放控制
2015/04/24 HTML / CSS
艺术系应届生的自我评价
2013/10/19 职场文书
弘扬职业精神演讲稿
2014/03/20 职场文书
学校就业推荐信范文
2014/05/19 职场文书
校园活动策划方案
2014/06/13 职场文书
微信小程序实现拍照和相册选取图片
2021/05/09 Javascript
python的变量和简单数字类型详解
2021/09/15 Python
Spring Cloud OpenFeign模版化客户端
2022/06/25 Java/Android