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 相关文章推荐
jQuery使用手册之二 DOM操作
Mar 24 Javascript
extjs DataReader、JsonReader、XmlReader的构造方法
Nov 07 Javascript
JQERY limittext 插件0.2版(长内容限制显示)
Aug 27 Javascript
基本jquery的控制tabs打开的数量的代码
Oct 17 Javascript
javascript学习总结之js使用技巧
Sep 02 Javascript
JS实现自动变换的菜单效果代码
Sep 09 Javascript
基于bootstrap的选择框插件icheck
Dec 23 Javascript
vuejs如何配置less
Apr 25 Javascript
Vue的轮播图组件实现方法
Mar 03 Javascript
jQuery实现input[type=file]多图预览上传删除等功能
Aug 02 jQuery
微信小程序 checkbox使用实例解析
Sep 09 Javascript
微信小程序APP的生命周期及页面的生命周期
Apr 19 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
经典的星际争霸,满是回忆的BGM
2020/04/09 星际争霸
用php实现批量查询清除一句话后门的代码
2008/01/20 PHP
php入门学习知识点五 关于php数组的几个基本操作
2011/07/14 PHP
PHP 时间日期操作实战
2011/08/26 PHP
PHP+Mysql+jQuery查询和列表框选择操作实例讲解
2015/10/22 PHP
PHP使用curl_multi_select解决curl_multi网页假死问题的方法
2018/08/15 PHP
thinkphp5框架路由原理与用法详解
2020/02/11 PHP
基于jquery插件实现常见的幻灯片效果
2013/11/01 Javascript
浅谈javascript的分号的使用
2015/05/12 Javascript
基于jquery实现即时检查格式是否正确的表单
2016/05/06 Javascript
json数据处理及数据绑定
2017/01/25 Javascript
详谈javascript精度问题与调整
2017/07/08 Javascript
浅谈Node.js CVE-2017-14849 漏洞分析(详细步骤)
2017/11/10 Javascript
layui实现table加载的示例代码
2018/08/14 Javascript
深入理解Vue router的部分高级用法
2018/08/15 Javascript
一步一步的了解webpack4的splitChunk插件(小结)
2018/09/17 Javascript
vue限制输入框只能输入8位整数和2位小数的代码
2019/11/06 Javascript
浅谈webpack和webpack-cli模块源码分析
2020/01/19 Javascript
JS实现网站楼层导航效果代码实例
2020/06/16 Javascript
python基础入门详解(文件输入/输出 内建类型 字典操作使用方法)
2013/12/08 Python
Python查看多台服务器进程的脚本分享
2014/06/11 Python
Python3中的2to3转换工具使用示例
2015/06/12 Python
用python处理图片之打开\显示\保存图像的方法
2018/05/04 Python
CentOS7下python3.7.0安装教程
2018/07/30 Python
详解Python用户登录接口的方法
2019/04/17 Python
python开发实例之Python的Twisted框架中Deferred对象的详细用法与实例
2020/03/19 Python
使用opencv识别图像红色区域,并输出红色区域中心点坐标
2020/06/02 Python
Python判断变量是否是None写法代码实例
2020/10/09 Python
详解基于Facecognition+Opencv快速搭建人脸识别及跟踪应用
2021/01/21 Python
亚马逊意大利站点:Amazon.it
2020/12/31 全球购物
《一株紫丁香》教学反思
2014/02/19 职场文书
公司贷款承诺书
2014/05/30 职场文书
领导班子专题民主生活会情况想汇报
2014/09/30 职场文书
2015年父亲节寄语
2015/03/23 职场文书
大学生心理健康教育心得体会
2016/01/12 职场文书
Mysql中mvcc各场景理解应用
2022/08/05 MySQL