JavaScript 趣味编程:从基础循环到函数式,解锁打印三角形的 N 种姿势


JavaScript 趣味编程:从基础循环到函数式,解锁打印三角形的 N 种姿势

对于许多程序员来说,用代码在控制台打印出一个小小的三角形,是学习生涯中一次难忘的"开窍"时刻。它不仅仅是一个简单的练习,更是我们理解循环、逻辑和模式识别的起点。

今天,我们不只满足于实现它,而是要玩出花样。本文将带你从最经典的双重 for 循环,一路走到更现代、更优雅的函数式写法,让你彻底掌握打印三角形的各种"姿势"。

第一站:梦开始的地方 ------ 直角三角形

我们的第一个目标是打印出这样的形状(以5行为例):

markdown 复制代码
*
**
***
****
*****
写法一:双重 for 循环------经典永不过时

这是每个初学者都会接触到的方法,它完美地诠释了嵌套循环的魅力。

javascript 复制代码
function printRightTriangleFor(rows) {
  // 外层循环控制行数 (i)
  for (let i = 1; i <= rows; i++) {
    let rowString = '';
    // 内层循环控制每一行打印的星号数量 (j)
    // 第 i 行,就有 i 个星号
    for (let j = 1; j <= i; j++) {
      rowString += '*';
    }
    console.log(rowString);
  }
}

printRightTriangleFor(5);

思路解析: 外层循环 i 像一个"行长",负责决定当前是第几行。内层循环 j 是一个"工人",i 告诉它这一行需要多少个 *,它就勤勤恳恳地拼接多少个。简单、直观,逻辑清晰。

写法二:巧用 .repeat() ------ 告别内层循环

进入现代 JavaScript 的世界,我们可以用更简洁的方式完成同样的工作。ES6 的 String.prototype.repeat() 方法简直是为此而生。

javascript 复制代码
function printRightTriangleRepeat(rows) {
  for (let i = 1; i <= rows; i++) {
    // 在第 i 行,直接生成一个重复 i 次的 '*' 字符串
    console.log('*'.repeat(i));
  }
}

printRightTriangleRepeat(5);

思路解析: 这种写法的代码量骤减,意图也更加明确。我们不再需要手动去拼接字符串,而是直接告诉 JavaScript:"给我一个包含 i 个星号的字符串"。代码更具可读性,也更"酷"。

写法三:函数式编程------换个"次元"看问题

如果你想让你的代码看起来更"极客",不妨试试函数式的写法。它不使用显式的循环,而是通过数组方法来生成结果。

javascript 复制代码
function printRightTriangleFunctional(rows) {
  const triangle = Array.from(
    { length: rows },           // 1. 创建一个指定长度的"空"数组
    (_, index) => '*'.repeat(index + 1) // 2. 映射每一项为所需的字符串
  ).join('\n');                 // 3. 用换行符拼接成最终成品

  console.log(triangle);
}

printRightTriangleFunctional(5);

思路解析: 这种方式将问题分解为三步:创建一个代表"行"的数组,将每一"行"映射成对应的星号字符串,最后将所有行合并。这是一种声明式的编程风格,你只关心"做什么",而不是"怎么做",代码非常优雅。


第二站:挑战升级 ------ 等腰三角形(金字塔)

现在,让我们来点更有挑战性的。打印一个居中的金字塔(以 6 行为例)。

markdown 复制代码
     *
    ***
   *****
  *******
 *********
***********

逻辑分析: 这次,每一行不仅有星号,还有前导空格。通过观察规律,我们发现对于第 i 行(i 从 1 开始):

  • 空格数量 = 总行数 - i
  • 星号数量 = (2 * i) - 1

有了这个公式,我们可以解锁多种实现方式。

姿势一:单循环 + .repeat() (最常用)

这是最直接、清晰的实现方式,将上面的数学公式直接翻译成代码。

javascript 复制代码
function printIsoscelesTriangle(rows) {
  for (let i = 1; i <= rows; i++) {
    // 计算并生成前导空格
    const spaces = ' '.repeat(rows - i);
    // 计算并生成星号
    const stars = '*'.repeat((2 * i) - 1);
    // 拼接并打印
    console.log(spaces + stars);
  }
}

printIsoscelesTriangle(6);
姿势二:函数式 Array.from + map

延续直角三角形的函数式思路,我们同样可以一行代码解决问题。

javascript 复制代码
function pyramidFunctional(n) {
  const rows = Array.from({ length: n }, (_, idx) => {
    const i = idx + 1;
    return ' '.repeat(n - i) + '*'.repeat(2 * i - 1);
  });
  console.log(rows.join('\n'));
}

pyramidFunctional(6);
姿势三:递归生成

对于喜欢挑战的同学,递归也是一个有趣的选择。

javascript 复制代码
function pyramidRecursive(n, i = 1, out = []) {
  if (i > n) {
    console.log(out.join('\n'));
    return;
  }
  out.push(' '.repeat(n - i) + '*'.repeat(2 * i - 1));
  pyramidRecursive(n, i + 1, out);
}

pyramidRecursive(6);
更多姿势一览

JavaScript 的灵活性远不止于此,我们还可以使用 while 循环、reduce 归纳、甚至是 Generator(生成器)来实现同样的效果。

  • while 循环版本:

    javascript 复制代码
    function pyramidWhile(n) {
      let i = 1;
      while (i <= n) {
        console.log(' '.repeat(n - i) + '*'.repeat(2 * i - 1));
        i++;
      }
    }
    pyramidWhile(6);
  • 生成器 (Generator) 逐行输出:

    javascript 复制代码
    function* pyramidGen(n) {
      for (let i = 1; i <= n; i++) {
        yield ' '.repeat(n - i) + '*'.repeat(2 * i - 1);
      }
    }
    for (const line of pyramidGen(6)) {
      console.log(line);
    }

最终站:封装归纳 ------ 打造一个通用的图形打印机

作为一名优秀的开发者,我们追求的是代码的复用性。让我们把上面的逻辑封装成一个更通用的函数,让它能打印不同类型、不同行数、甚至不同字符的图形。

javascript 复制代码
/**
 * 打印各种形状的函数
 * @param {number} rows - 总行数
 * @param {string} shape - 'right', 'inverted-right', 'isosceles'
 * @param {string} char - 用于打印的字符,默认为 '*'
 */
function printShape(rows, shape = 'right', char = '*') {
  console.log(`\n--- 打印 ${rows} 行的 ${shape} 三角形 (字符: ${char}) ---`);
  
  switch (shape) {
    case 'right':
      for (let i = 1; i <= rows; i++) console.log(char.repeat(i));
      break;
    case 'inverted-right': // 新增倒立直角
      for (let i = rows; i >= 1; i--) console.log(char.repeat(i));
      break;
    case 'isosceles':
      for (let i = 1; i <= rows; i++) {
        const spaces = ' '.repeat(rows - i);
        const chars = char.repeat((2 * i) - 1);
        console.log(spaces + chars);
      }
      break;
    default:
      console.log('抱歉,我还不会打印这种形状。');
  }
}

// 让我们来试试这个强大的打印机!
printShape(7, 'right', '#');
printShape(6, 'isosceles', '+');
printShape(5, 'inverted-right'); // 打印倒立直角三角形

总结

从一个简单的打印三角形问题,我们回顾了经典的循环逻辑,学习了现代 JavaScript 的简洁语法,体验了函数式编程的优雅,并最终将其封装成一个可复用的工具函数。

这正是编程的乐趣所在:同一个问题,总有更多、更好、更有趣的解法等待我们去发现。

现在,轮到你了!你还能想到哪些有趣的写法?或者,你能挑战一下打印一个菱形或者空心三角形吗?

相关推荐
Null1555 小时前
前端ZIP处理:JSZip vs fflate 全方位对比,让你的文件操作效率翻倍!
前端
Mishi5 小时前
前端踩坑日记-sass-resources-loader在Windows上遇到的问题
前端
Jacob02345 小时前
为什么现代 JavaScript 开发者正在远离回调与框架?
前端·javascript·前端框架
ZXT5 小时前
依赖管理
前端
ZXT5 小时前
vite & webpack
前端
yanlele5 小时前
前端面试第 77 期 - 2025.09.02 更新前端面试问题总结(15 道题)
前端·javascript·面试
徐小夕5 小时前
开源了一款基于 Vue 3 的高性能多维表格编辑器
前端·vue.js·github
小豆包api5 小时前
Nano-Banana速通指南,爆款 AI 绘图神器低成本接入方案
前端
一枚前端小能手6 小时前
🔥 Vue项目越来越卡?响应式系统的4个性能陷阱
前端·javascript·vue.js