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 相关文章推荐
javascript 动态数据下的锚点错位问题解决方法
Dec 24 Javascript
jquery对表单操作2
Apr 06 Javascript
js控制当再次点击按钮时的间隔时间
Jun 03 Javascript
基于JavaScript实现右键菜单和拖拽功能
Nov 28 Javascript
jQuery中页面返回顶部的方法总结
Dec 30 Javascript
jQuery插件zTree实现清空选中第一个节点所有子节点的方法
Mar 08 Javascript
浅谈如何使用 webpack 优化资源
Oct 20 Javascript
node文字生成图片的示例代码
Oct 26 Javascript
简单明了区分escape、encodeURI和encodeURIComponent
May 26 Javascript
微信小程序Echarts图表组件使用方法详解
Jun 25 Javascript
JS实现水平遍历和嵌套递归操作示例
Aug 15 Javascript
vue页面加载时的进度条功能(实例代码)
Jan 13 Javascript
类和原型的设计模式之复制与委托差异
JS高级程序设计之class继承重点详解
Jul 07 #Javascript
JS class语法糖的深入剖析
Jul 07 #Javascript
MutationObserver在页面水印实现起到的作用详解
Jul 07 #Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
You might like
PHP在Web开发领域的优势
2006/10/09 PHP
PHP如何透过ODBC来存取数据库
2006/10/09 PHP
php实现文件下载简单示例(代码实现文件下载)
2014/03/10 PHP
美图秀秀web开放平台--PHP流式上传和表单上传示例分享
2014/06/22 PHP
php实现建立多层级目录的方法
2014/07/19 PHP
Smarty变量用法详解
2016/05/11 PHP
PHP自定义函数格式化json数据示例
2016/09/14 PHP
掌握PHP垃圾回收机制详解
2019/03/13 PHP
多次注册事件会导致一个事件被触发多次的解决方法
2013/08/12 Javascript
js jq 单击和双击区分示例介绍
2013/11/05 Javascript
Bootstrap每天必学之进度条
2015/11/30 Javascript
jQuery设置聚焦并使光标位置在文字最后的实现方法
2016/08/02 Javascript
js模仿微信朋友圈计算时间显示几天/几小时/几分钟/几秒之前
2017/04/27 Javascript
利用prop-types第三方库对组件的props中的变量进行类型检测
2017/05/02 Javascript
详解bootstrap用dropdown-menu实现上下文菜单
2017/09/22 Javascript
vue项目部署上线遇到的问题及解决方法
2018/06/10 Javascript
浅谈关于iview表单验证的问题
2018/09/29 Javascript
vue-router 手势滑动触发返回功能
2018/09/30 Javascript
Javascript ParentNode和ChildNode接口原理解析
2020/03/16 Javascript
vue 数据操作相关总结
2020/12/17 Vue.js
[42:06]2019国际邀请赛全明星赛 8.23
2019/09/05 DOTA
python基础教程之常用运算符
2014/08/29 Python
详解python实现识别手写MNIST数字集的程序
2018/08/03 Python
Python中最大递归深度值的探讨
2019/03/05 Python
python os.path.isfile 的使用误区详解
2019/11/29 Python
使用Tensorboard工具查看Loss损失率
2020/02/15 Python
python json.dumps中文乱码问题解决
2020/04/01 Python
django模型类中,null=True,blank=True用法说明
2020/07/09 Python
pytorch 把图片数据转化成tensor的操作
2021/03/04 Python
Michael Kors美国官网:美式奢侈生活风格的代表
2016/11/25 全球购物
周仰杰(JIMMY CHOO)法国官方网站:闻名世界的鞋子品牌
2019/09/27 全球购物
介绍一下Make? 为什么使用make
2016/07/31 面试题
酒店总经理岗位职责
2014/03/17 职场文书
2015年校医个人工作总结
2015/07/24 职场文书
解决redis sentinel 频繁主备切换的问题
2021/04/12 Redis
MySQL数据库⾼可⽤HA实现小结
2022/01/22 MySQL