JavaScript中OOP和FP是什么,有何区别吗
Admin 2022-09-06 群英技术资讯 312 次浏览
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
在面向对象编程中最常见的表现形式就是类,提供了面向对象的 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) }
}
函数式编程提倡函数是第一公民【指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值】,纯粹的函数式编程,是纯函数【如果传入的参数相同,就会返回相同的结果,不依赖于外部的数据状态【如下实例】】,函数编程特点
// 纯函数
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 函数,在遍历每个对象值的时候生成一个新对象 很抽象,简单来说 函子是一个持有值的容器。嗨难懂,上代码。
// 实现一个基本定义的函子,满足定义
// 实现 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
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
JSON.stringify的用法是什么?JSON.stringify()方法就是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,小编觉得JSON.stringify()方法比较实用,因此分享给大家做个参考,感兴趣的朋友就接着看吧。
本篇文章带大家聊聊JavaScript解构(Destructuring)的5个有趣用法,希望给大家有所帮助!
本文主要给大家介绍JavaScript脚本被执行的相关内容,大家在编写JavaScript脚本时,有没有想过JavaScript脚本什么时候被执行呢?脚本执行是怎样的呢?下面我们就来了解一下这些。
这篇文章主要为大家详细介绍了js实现有趣的倒计时效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
很多朋友向小编反映一个问题关于React onClick 传递参数的问题,当点击删除按钮需要执行删除操作,针对这个问题该如何处理呢?下面小编给大家带来了React onClick 传递参数的问题,感兴趣的朋友一起看看吧
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008