图解:

前面视为为排序,后面视为已排序,每趟将未排序数组中最大的那个放在已排序数组中的最前面。

一般思路

const bubbleSort=function(nums){
    let len=nums.length;
    for(let i=0; i<len;i++){
   // 内层循环应该从0开始,到len-i-1结束
        for(let j=0;j<len-i-1;j++){
            if(nums[j]>nums[j+1]){
	            let temp=nums[j];
	            nums[j]=nums[j+1];
	            nums[j+1]=temp;
            }
        }
    }
    return nums
}

递归调用

起始索引从n开始,知道起始位置0 相较于选择排序,他们之间很类似,只不过索引走的顺序是相反的

function bubbleSortRecursive(nums, n) {
  // 如果调用时未提供 n,则默认为整个数组的长度
  if (n === undefined) {
    n = nums.length;
  }
 
  // 基本情况:如果数组只有一个或没有元素,则它已经是有序的
  if (n <= 1) {
    return nums;
  }
 
  // --- 执行“一趟”冒泡 ---
  // 这个循环将当前 n 个元素中最大的一个“冒泡”到末尾(索引 n-1 的位置)
  for (let i = 0; i < n - 1; i++) {
    // 比较相邻元素
    if (nums[i] > nums[i + 1]) {
      // 如果顺序错误,则交换它们
      [nums[i], nums[i + 1]] = [nums[i + 1], nums[i]];
    }
  }
 
  // --- 递归调用 ---
  // 现在最大的元素已经就位,对剩下的 n-1 个元素重复此过程
  bubbleSortRecursive(nums, n - 1);
 
  // 返回修改后的数组
  return nums;
}

reference