一等公民-函数

227 阅读4分钟

函数是一等公民这句话大概一直知道了十年了,但是也就是刚刚,也许我才知道它的真实含义

函数是一等公民,大概意思就是,函数就和一个数字,一个字符串一样,它就相当于是一个“变量‘,可以传递,可以输出返回,可以打印,可以做任何变量可以做的事

这是我对js函数式编程这本书的这一章的注解,我想应该是没问题,能很好地解释函数是一等公民这句话

  • 函数与数字一样可以存储为变量
var two = function() { return 2 }
  • 函数与数字一样可以存储为数组的一个元素
var arrTwo = [2, function() { return 2 }]
  • 函数与数字一样可以作为对象的成员变量
var objTwo = {
	number: 2,
	funNum: function() { return 2 }
}
  • 函数与数字一样可以在使用时直接创建出来
2 + (function() { return 2 })()
//4
  • 函数与数字一样可以传递给另一个函数
function add(num, fun) {
	return num + fun()
}
add(2, function() { return 2 })
//4
  • 函数与数字一样可以被另一个函数返回
return 2
return function() { return 42 }

最后两个,函数作为参数函数作为返回结果就是高阶函数的定义

理解了函数是一等公民是理解函数式编程的基础,当然,JavaScript也不止支持函数式编程这一种风格,作为风格,很显然还有其他的方式,接下来先来看看都有那些:

命令式编程

刚看完这一章,我惊奇的发现,这不就是我每天工作内容吗,需要个什么工作,我就做一个能完成这个功能的”命令“

什么是命令式编程,大概意思就是,平铺直叙的完成一个函数的功能,书中有一个例子,

var lyrics = []
for(var bottles = 99; bottles > 0; bottles--) {
	lyrics.push(bottles + ' bottles of beer on the wall')
	lyrics.push('take one down')
	if(bottles > 1) {
		lyrics.push((bottles - 1) + ' bottles of beer on the wall')
	}else {
		lyrics.push('no more bottles of beer')
	}
}

这段代码能基本概括我职业生涯的大部分逻辑了,当然也不能说这样不好,还是要视情况而定的,但是这里我们还是要想一想,如果是函数式编程,要怎么改这段代码?

const generateLyrics = (bottles) => {
  // 创建一个从 bottles 到 1 的数组
  const bottleArray = Array.from({ length: bottles }, (_, i) => bottles - i);
  // 使用 map 函数生成歌词
  const lyrics = bottleArray.flatMap(bottle => {
    const lines = [
      `${bottle} bottles of beer on the wall`,
      'take one down'
    ];
    if (bottle > 1) {
      lines.push(`${bottle - 1} bottles of beer on the wall`);
    } else {
      lines.push('no more bottles of beer');
    }
    return lines;
  });
  return lyrics;
};
// 调用函数并输出结果
const lyrics = generateLyrics(99);

上面这段代码时ai给我的结果,我让kimi把上面那段命令式的代码改成函数式编程的风格,不过既然函数式编程的思路是将程序拆分并抽象成多个函数再组装回去,那这段代码是不是还能继续改。

const createVerse = (bottles) => {
  if (bottles === 1) {
    return [
      `${bottles} bottle of beer on the wall`,
      'take one down',
      'no more bottles of beer'
    ];
  } else {
    return [
      `${bottles} bottles of beer on the wall`,
      'take one down',
      `${bottles - 1} bottles of beer on the wall`
    ];
  }
};
const generateLyrics = (startBottles) => {
  // 使用 reduce 函数生成所有章节的歌词
  return Array.from({ length: startBottles }, (_, i) => createVerse(startBottles - i))
    .flat(); // 将所有章节的歌词数组扁平化
};

// 调用函数并输出结果
const lyrics = generateLyrics(99);
console.log(lyrics.join('\n'));

这还是ai给我的优化结果,但是我想,是不是createVerse方法作为参数传递会不会更好呢

const createVerse = (bottles) => {
  if (bottles === 1) {
    return [
      `${bottles} bottle of beer on the wall`,
      'take one down',
      'no more bottles of beer'
    ];
  } else {
    return [
      `${bottles} bottles of beer on the wall`,
      'take one down',
      `${bottles - 1} bottles of beer on the wall`
    ];
  }
};
const generateLyrics = (startBottles, creatSong) => {
  // 使用 reduce 函数生成所有章节的歌词
  return Array.from({ length: startBottles }, (_, i) => creatSong(startBottles - i))
    .flat(); // 将所有章节的歌词数组扁平化
};

// 调用函数并输出结果
const lyrics = generateLyrics(99, createVerse);
console.log(lyrics.join('\n'));

哇哦,多么神奇,好像写的高深了许多,可惜不是我手动敲出来的,不知道我真的掌握了还是看懂了

基于原型的面向对象编程

在我的编程之路,是从c语言开始的,那时候的学习都只是加减乘除,大概知道编程是怎么一回事。关于面向对象,大概是大二的java课程吧,所以当我接触到js的面向对象时,我是蒙的,面向对象不就应该是java,c#那种,class什么的吗? 我低估了JavaScript的灵活性

真正理解了js的面向对象,大概就是从终于搞懂了原型链,prototype,__proto__ 这里就不说这些了,指路重新认识构造函数、原型和原型链 | 木易杨前端进阶

元编程

这应该是我第一次接触这个概念,也可能是之前听说过忘了。

对元编程比较好的定义是:编写代码做一些事情叫做编程,而元编程是当你写的代码改变了某些代码被解释的方式

说实话没看懂,不过看个例子就好理解了

function point2d(x, y) {
	this.x = x;
	this.y = y;
}
new point2d(0, 1)
// {x:0, y:1}

我们用new创建了一个二维对象实例,现在来改造一下

function point3d(x, y, z) {
	point2d.call(this, x, y)
	this.z = z
}
new point3d(2, 3, 4)
//{x:2,y:3,z:4}

这就是元编程了,point2d派生为新的point3d的构造器

大概过段时间,还会把这个定义给忘了。