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中使用css需要注意的地方小结
Sep 01 Javascript
node.js中的socket.io入门实例
Apr 26 Javascript
Node.js编码规范
Jul 14 Javascript
javascript实现网页背景烟花效果的方法
Aug 06 Javascript
Bootstrap的图片轮播示例代码
Aug 31 Javascript
基于JavaScript代码实现pc与手机之间的跳转
Dec 23 Javascript
JavaScript模版引擎的基本实现方法浅析
Feb 15 Javascript
JavaScript实现解析INI文件内容的方法
Nov 17 Javascript
微信小程序中post方法与get方法的封装
Sep 26 Javascript
利用nvm管理多个版本的node.js与npm详解
Nov 02 Javascript
vue实现a标签点击高亮方法
Mar 17 Javascript
解决vue项目input输入框双向绑定数据不实时生效问题
Aug 05 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读取大文件示例分享(文件操作类)
2014/04/13 PHP
javascript 火狐(firefox)不显示本地图片问题解决
2008/07/05 Javascript
在IE,Firefox,Safari,Chrome,Opera浏览器上调试javascript
2008/12/02 Javascript
javascript中String对象的slice()方法分析
2014/12/20 Javascript
JavaScript DOM操作表格及样式
2015/04/13 Javascript
JavaScript中使用指数方法Math.exp()的简介
2015/06/15 Javascript
js实现当前输入框高亮显示的方法
2015/08/19 Javascript
第一次接触神奇的Bootstrap导航条
2016/08/09 Javascript
一份超级详细的Vue-cli3.0使用教程【推荐】
2018/11/15 Javascript
vue实现移动端触屏拖拽功能
2020/08/21 Javascript
python数字图像处理之高级形态学处理
2018/04/27 Python
python dataframe向下向上填充,fillna和ffill的方法
2018/11/28 Python
selenium+python自动化测试之使用webdriver操作浏览器的方法
2019/01/23 Python
使用Python 统计高频字数的方法
2019/01/31 Python
python通过paramiko复制远程文件及文件目录到本地
2019/04/30 Python
使用Python的OpenCV模块识别滑动验证码的缺口(推荐)
2019/05/10 Python
python实现QQ邮箱发送邮件
2020/03/06 Python
python和pywin32实现窗口查找、遍历和点击的示例代码
2020/04/01 Python
Django如何使用jwt获取用户信息
2020/04/21 Python
Django之富文本(获取内容,设置内容方式)
2020/05/21 Python
Python requests上传文件实现步骤
2020/09/15 Python
Python 排序最长英文单词链(列表中前一个单词末字母是下一个单词的首字母)
2020/12/14 Python
收藏!10个免费高清视频素材网站!【设计、视频剪辑必备】
2021/03/18 杂记
Urban Outfitters美国官网:美国生活方式品牌
2016/08/26 全球购物
保护环境演讲稿
2014/05/10 职场文书
负责人任命书范本
2014/06/04 职场文书
2014年重阳节敬老活动方案
2014/09/16 职场文书
2014年政风行风工作总结
2014/11/22 职场文书
护理专业自我评价
2015/03/11 职场文书
面试通知短信
2015/04/20 职场文书
2015年语文教师工作总结
2015/05/25 职场文书
单位政审意见范文
2015/06/04 职场文书
同意报考证明
2015/06/17 职场文书
2016应届大学生自荐信模板
2016/01/28 职场文书
Pytest之测试命名规则的使用
2021/04/16 Python
Oracle更换为MySQL遇到的问题及解决
2021/05/21 Oracle