JS 命令式 声明式 函数式 编程?

本文介绍了JS中的命令式、声明式和函数式编程风格,强调了声明式编程的可读性优势,并通过React框架为例进行说明。JS支持函数式编程,其中函数被视为变量,具有不可变性、纯函数、数据转换和高阶函数等特点。文中还探讨了柯里化和递归的概念。

JS 命令式 声明式 函数式 编程?

——烟雨仔的读书笔记

1. 命令式 和 声明式 是啥?

命令式和声明式是按照编程风格来划分的.

声明式有一个突出特点: 对执行结果的描述远胜于执行过程.

相对地, 命令式的特点是: 关注达成目标的具体过程.

或许你还是很懵, 看下面的命令式编程例子.

	const arr = ['h', 'e', 'l', 'l', 'o' ]
	for(let i = 0; i < arr.length; i++) {
		arr[i] = arr[i].toUpperCase()
	}
	console.log(arr)

在上面的例子中, 包含了完成这个任务的整个过程, 即遍历整个数组并转换成大写.

接下来看声明式编程如何完成.

	const arr = ['h', 'e', 'l', 'l', 'o']
	const arrNew = arr.reduce((prev, cur) => 
		prev += cur.toUpperCase(), '')
	console.log(arrNew)

对于上述代码, 我们之所以明白它发生了什么是由于看到 reduce 方法, 但具体遍历处理的细节被抽象封装到 reduce 内部, 我们并没有看到它循环遍历的过程.

简单理解就是命令式代码的过程相当细节: 切块, 去籽, 扔进搅拌机, 而声明式代码描述的是具体结果: 西瓜, 榨成西瓜汁.

因此声明式编程代码是可读性更强的, 因为我们并不在意榨成西瓜汁的细节. React 框架采用的就是声明式编程, render 函数调用来构建 DOM, 但 DOM 渲染的具体细节是被封装的. 虽然看不到细节, 但我们看到 render 方法就清楚地知道这是在渲染组件.

2. JS 中的函数

JS 可以进行函数式编程, 这意味着变量可以做的事情, 函数也可以.

是的, 接下来你需要谨记: “函数就是变量!

  1. 可以像声明变量那样去声明一个函数(函数表达式)
    let fn = function(msg) {
    	console.log(msg)
    }
    
  2. 函数就是变量! 因此可以添加到对象
    const obj = {
    	msg: 'hello watermelon',
    	foo (msg) {
    		console.log(msg)
    	}
    }
    
  3. 函数就是变量! 因此可以添加到数组
    const arr = [
    	'hello watermelon',
    	msg => console.log(msg),
    	'hello world'
    ]
    
    arr[1](arr[0])	// hello watermelon
    
  4. 函数就是变量! 因此可以作为其他函数的参数传递
    let foo = fn => fn('hello watermelon')
    foo(msg => console.log(msg))
    // hello watermelon
    
  5. 函数就是变量! 因此可以作为其他函数的执行结果返回
    const foo = arg1 => arg2 => console.log(arg1 + ' ' + arg2)
    const log = foo('hello')// log 是 foo 返回的函数
    log('watermelon')
    

看过以上介绍, 烟雨仔相信你开始有一点了解了.

函数式编程的核心概念

函数式编程是声明式编程的一部分. 这部分偏理论, 以至于烟雨仔也有些迷惑…

不可变性

在函数式编程中, 数据不可变, 数据是无法被修改的. 这意味着不能修改原始数据, 只能进行拷贝编辑. 关于如何拷贝这里就不进行赘述.

纯函数

纯函数是一个返回结果只依赖输入参数的函数. 我们需要明确以下几点.

  1. 纯函数至少需要接收一个参数, 并将参数当做不可变数据.
  2. 总是返回一个值或其他函数.
  3. 不会产生副作用, 即不会修改作用域外的变量.

以下是一个非纯函数.

	const person = {
		name: 'mistrain',
		age: 18,
		hobby: 'watermelon'
	}
	function changeName() {
		person.name = 'zongzi'
		return person
	}
	changeName()
	console.log(person)
	// {name: 'zongzi', age: 18, hobby: 'watermelon'}

以上 changeName 函数是非纯函数. 它并没有接收参数, 也没有返回一个值或其他函数, 并且修改了作用域外数据 person.

接下来看纯函数.

	const person = {
  		name: 'mistrain',
  		age: 18,
  		hobby: 'watermelon'
	}
	const newPerson = info => ({...info, name: 'zongzi'})
	// 扩展运算符, 并修改部分对象属性, 烟雨仔第一次用这种写法, 妙~
	console.log(newPerson(person))
	// {name: 'zongzi', age: 18, hobby: 'watermelon'}
	console.log(person)
	// {name: 'mistrain', age: 18, hobby: 'watermelon'}

以上是一个纯函数, 它实现了不改变 person, 并返回一个新的对象, 因此没有产生副作用.

数据转换

由于函数式编程中数据不可变性的存在, 有时往往需要将一种数据转换成另一种数据副本来使用. ES6提供了一些高阶函数使转换副本的代码更加简单.

高阶函数

  1. 第一类是 reduce, map, filter等, 它们都将函数作为参数进行传递.
  2. 第二类是将函数作为返回值. 如柯里化(currying)就是通过在一个函数内返回另一个函数实现的.

这里放一个问烂了的经典柯里化面试题, 在了解函数式编程后, 重新来看这个题相信你会有不一样的理解.

实现一个函数, 调用得到:
fn(1) = 1
fn(1, 2, 3) = 6
fn(1)(2, 3)(4, 5, 6) = 21

递归

鲁迅先生说: “不懂数据结构的前端也可以是好前端.”(烟雨仔瞎编的)相信你懂递归是怎么一回事, 即"自己调用自己". 这里烟雨仔也不赘述了.

一些废话

本篇是烟雨仔在学习《React 学习手册》时的一些笔记. 毕竟理论的东西就是很容易忘记, 但确实很有用~

写得很浅, 不正确的地方望诸位大佬指正.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值