程序员武学修炼手册(一):入门篇——初学乍练,从 Hello World 到能跑就行

"千里之行,始于足下;万行代码,始于 Hello World。" ------ 《程序员武学心法》

开篇:程序员的武学境界

在武侠世界里,习武之人从入门弟子到一代宗师,需要经历漫长的修炼。

在程序员的世界里,我们也有类似的成长路径。今天,让我们用武学的视角,重新审视程序员的修炼之路。

复制代码
┌─────────────────────────────────────────────────────────────┐
│              程序员武学境界总览                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   第一境:初学乍练 ------  能写代码,能跑就行(入门弟子)          │
│   第二境:小有所成 ------  代码规范,知其所以然(三流高手)        │
│   第三境:融会贯通 ------  架构设计,独当一面(一流高手)          │
│   第四境:登峰造极 ------  技术专家,指点江山(绝顶高手)          │
│   第五境:开宗立派 ------  自成一家,影响江湖(一代宗师)          │
│                                                             │
│   番外:走火入魔 ------ 那些修炼路上的歧途                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

本系列将分为六篇,今天是第一篇:初学乍练


第一章:初学乍练的特征

1.1 什么是初学乍练?

初学乍练,是每个程序员的起点。

就像武侠小说里刚入门的弟子,刚拿到一本剑谱,连基本的扎马步都不稳,但眼里充满了对武学的向往。

初学乍练程序员的典型特征:

  • 刚学会一门语言,觉得自己无所不能
  • 代码能跑就是胜利
  • 变量命名:a, b, c, temp, temp2, temp3
  • 注释?不存在的
  • 遇到 Bug 第一反应:重启试试?

1.2 初学乍练的心理状态

javascript 复制代码
// 初学乍练程序员的内心独白

const beginnerMindset = {
  day1: "Hello World跑通了!我是天才!",
  day7: "这个for循环好难...算了,复制粘贴",
  day30: "我已经会写函数了,可以找工作了吧?",
  day60: "为什么我的代码别人看不懂?明明很清晰啊",
  day90: "原来我写的是屎山...",
}

第二章:初学乍练的修炼内容

2.1 第一式:Hello World

每个程序员的第一招,必是 Hello World。

javascript 复制代码
// JavaScript版
console.log("Hello, World!")

// 练气期程序员的进阶版
console.log("Hello, World!")
console.log("Hello, World!!")
console.log("Hello, World!!!")
// 为什么要打三遍?因为我能!
python 复制代码
# Python版
print("Hello, World!")

# 练气期程序员的"优化"版
for i in range(100):
    print("Hello, World!" + "!" * i)
# 这样更有气势!
java 复制代码
// Java版
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

// 练气期程序员的困惑:
// 为什么打印个Hello World要写这么多?
// public是什么?static是什么?void又是什么?
// 算了,能跑就行...

2.2 第二式:变量与类型

javascript 复制代码
// 练气期的变量命名艺术

// 阶段1:单字母时期
let a = 1
let b = 2
let c = a + b

// 阶段2:拼音时期
let nianling = 18
let mingzi = "张三"
let gongzi = 10000

// 阶段3:中英混搭时期
let userNianling = 18
let userName = "张三"
let userGongzi = 10000

// 阶段4:开始觉醒
let userAge = 18
let userName = "张三"
let userSalary = 10000
// 虽然还是有点问题,但已经在进步了

2.3 第三式:条件判断

javascript 复制代码
// 练气期的if-else艺术

// 初级写法:一路if到底
function getGrade(score) {
  if (score >= 90) {
    return "A"
  }
  if (score >= 80) {
    return "B"
  }
  if (score >= 70) {
    return "C"
  }
  if (score >= 60) {
    return "D"
  }
  return "F"
}

// 练气期"优化"版:嵌套地狱
function getGradeNested(score) {
  if (score >= 60) {
    if (score >= 70) {
      if (score >= 80) {
        if (score >= 90) {
          return "A"
        } else {
          return "B"
        }
      } else {
        return "C"
      }
    } else {
      return "D"
    }
  } else {
    return "F"
  }
}
// 能跑!完美!(并不)

// 练气期的终极形态:复制粘贴大法
function processUser(user) {
  if (user.type === "admin") {
    if (user.status === "active") {
      if (user.level >= 5) {
        // 100行代码
      } else {
        // 复制上面100行,改几个字
      }
    } else {
      // 再复制100行
    }
  } else {
    // 继续复制...
  }
}

2.4 第四式:循环

javascript 复制代码
// 练气期的循环修炼

// 第一层:for循环
for (let i = 0; i < 10; i++) {
  console.log(i)
}
// 掌握了!我是天才!

// 第二层:while循环
let i = 0
while (i < 10) {
  console.log(i)
  // 忘记 i++ 了...
  // 程序卡死
  // 为什么???
}

// 第三层:嵌套循环
for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    for (let k = 0; k < 10; k++) {
      // 三层循环,性能杀手
      // 但是能跑!
    }
  }
}

// 练气期经典Bug:死循环
function infiniteLoop() {
  let arr = [1, 2, 3]
  for (let i = 0; i < arr.length; i++) {
    arr.push(i) // 数组越来越长...
    // 电脑风扇开始狂转
    // 浏览器卡死
    // 强制关机
  }
}

2.5 第五式:函数

javascript 复制代码
// 练气期的函数修炼

// 阶段1:不知道什么是函数
// 所有代码写在一起,500行一个文件

// 阶段2:知道函数,但不会用
function doSomething() {
  // 200行代码
  // 做了10件不同的事
  // 函数名叫doSomething,因为确实在做something
}

// 阶段3:函数太多,不知道怎么组织
function step1() {}
function step2() {}
function step3() {}
function step4() {}
function step5() {}
// ...
function step99() {}

function main() {
  step1()
  step2()
  step3()
  // ...
  step99()
}

// 阶段4:开始理解函数的意义
function calculateTax(income) {
  // 只做一件事:计算税
  return income * 0.2
}

function formatCurrency(amount) {
  // 只做一件事:格式化金额
  return `¥${amount.toFixed(2)}`
}

2.6 第六式:数组与对象

javascript 复制代码
// 练气期的数据结构

// 数组的使用
let arr = [1, 2, 3, 4, 5]

// 练气期的数组操作
// 想删除第3个元素
arr[2] = undefined // 这样对吗?
// 结果:[1, 2, undefined, 4, 5]
// 好像不太对...

// 正确做法(但练气期不知道)
arr.splice(2, 1)

// 对象的使用
let user = {
  name: "张三",
  age: 18,
}

// 练气期的对象操作
user.mingzi = "李四" // 为什么不生效?
// 因为属性名是name不是mingzi...

// 练气期经典困惑:引用类型
let arr1 = [1, 2, 3]
let arr2 = arr1
arr2.push(4)
console.log(arr1) // [1, 2, 3, 4] ???
// 为什么arr1也变了???
// 这个Bug找了三天...

第三章:初学乍练的常见走火入魔

3.1 复制粘贴走火入魔

javascript 复制代码
// 症状:同样的代码出现N次

function processUserA(user) {
  // 验证用户
  if (!user.name) throw new Error("name required")
  if (!user.email) throw new Error("email required")
  if (!user.phone) throw new Error("phone required")

  // 处理数据
  user.name = user.name.trim()
  user.email = user.email.toLowerCase()

  // 保存
  database.save(user)

  // 发送通知
  sendEmail(user.email, "Welcome!")
}

function processUserB(user) {
  // 复制上面的代码
  // 改了一个字
  // 验证用户
  if (!user.name) throw new Error("name required")
  if (!user.email) throw new Error("email required")
  if (!user.phone) throw new Error("phone required")

  // 处理数据
  user.name = user.name.trim()
  user.email = user.email.toLowerCase()

  // 保存
  database.save(user)

  // 发送通知
  sendEmail(user.email, "Welcome Back!") // 就改了这里
}

// 后果:改一个地方要改N个地方,总有漏的

3.2 过度注释走火入魔

javascript 复制代码
// 症状:注释比代码还多

// 定义一个变量i,初始值为0
let i = 0

// 使用while循环
while (i < 10) {
  // 当i小于10时
  // 打印i的值
  console.log(i) // 输出当前的i
  // i自增1
  i++ // i = i + 1
} // while循环结束

// 这种注释不如不写...

3.3 变量命名走火入魔

javascript 复制代码
// 症状:变量名让人怀疑人生

// 拼音派
let nianling = 18
let xingming = "张三"
let shouji = "13800138000"

// 缩写派
let usrNm = "张三"
let usrAg = 18
let usrPh = "13800138000"

// 数字派
let user1 = {}
let user2 = {}
let user11 = {} // 不是user1的升级版
let user111 = {} // 也不是user11的升级版

// 混搭派
let getUserXinxi = () => {} // 中英混搭
let YONGHU_INFO = {} // 拼音+英文

// 终极形态
let aaaa = 1
let bbbb = 2
let cccc = aaaa + bbbb
let dddd = cccc * 2
// 一周后自己都看不懂

3.4 缩进走火入魔

javascript 复制代码
// 症状:缩进混乱,Tab和空格混用

function chaos() {
  if (true) {
    console.log("2空格")
    console.log("4空格?不,是Tab")
    console.log("这是几个空格?")
    console.log("这是Tab")
    console.log("Tab+空格混搭")
  }
}

// 后果:代码在不同编辑器里显示不一样
// 同事打开你的代码:这是什么鬼?

第四章:初学乍练的突破契机

4.1 第一次代码审查

javascript 复制代码
// 你提交的代码
function calc(a, b, c) {
  let temp = a + b
  let temp2 = temp * c
  return temp2
}

// 代码审查反馈
/*
 * 1. 函数名calc太模糊,计算什么?
 * 2. 参数a, b, c是什么意思?
 * 3. temp, temp2命名不清晰
 * 4. 可以直接return,不需要中间变量
 *
 * 建议修改为:
 */
function calculateTotalPrice(unitPrice, quantity, taxRate) {
  return unitPrice * quantity * taxRate
}

// 你的内心:原来代码还可以这样写...

4.2 第一次维护别人的代码

javascript 复制代码
// 你接手的代码
function f(x) {
  let a = x[0]
  let b = x[1]
  let c = x[2]
  if (a > 0) {
    if (b > 0) {
      if (c > 0) {
        return a * b * c
      } else {
        return a * b
      }
    } else {
      return a
    }
  } else {
    return 0
  }
}

// 你的任务:修改这个函数,支持第四个参数

// 你的内心:
// 这是什么?
// x是什么?
// a, b, c是什么?
// 为什么要这样嵌套?
// 我改哪里?
// 算了,我重写一个...

// 顿悟:原来写清晰的代码这么重要

4.3 第一次生产事故

javascript 复制代码
// 你写的代码
function deleteUser(userId) {
  database.delete("users", { id: userId })
}

// 调用时
deleteUser() // 忘记传参数了

// 结果
database.delete("users", { id: undefined })
// 某些数据库会把这理解为:删除所有用户
// 用户表清空了...

// 事后复盘
function deleteUserSafe(userId) {
  // 参数校验!
  if (!userId) {
    throw new Error("userId is required")
  }

  // 二次确认
  const user = database.findOne("users", { id: userId })
  if (!user) {
    throw new Error("user not found")
  }

  // 软删除而不是硬删除
  database.update("users", { id: userId }, { deleted: true })

  // 记录日志
  logger.info(`User ${userId} deleted`)
}

// 顿悟:防御性编程真的很重要

第五章:初学乍练的修炼心法

5.1 心法一:代码是写给人看的

javascript 复制代码
// 练气期的认知
// "代码是写给电脑执行的"

// 突破后的认知
// "代码首先是写给人看的,其次才是给电脑执行的"

// 实践
// 不好的代码
let d = new Date()
let h = d.getHours()
let m = d.getMinutes()
let s = d.getSeconds()
let t = h + ":" + m + ":" + s

// 好的代码
const currentDate = new Date()
const hours = currentDate.getHours()
const minutes = currentDate.getMinutes()
const seconds = currentDate.getSeconds()
const timeString = `${hours}:${minutes}:${seconds}`

5.2 心法二:不要重复自己(DRY)

javascript 复制代码
// 练气期的代码
function validateEmail(email) {
  if (!email) return false
  if (!email.includes("@")) return false
  return true
}

function validateUserEmail(user) {
  if (!user.email) return false
  if (!user.email.includes("@")) return false
  return true
}

function validateAdminEmail(admin) {
  if (!admin.email) return false
  if (!admin.email.includes("@")) return false
  return true
}

// 突破后的代码
function validateEmail(email) {
  if (!email) return false
  if (!email.includes("@")) return false
  return true
}

function validateUserEmail(user) {
  return validateEmail(user?.email)
}

function validateAdminEmail(admin) {
  return validateEmail(admin?.email)
}

5.3 心法三:先让它工作,再让它正确,最后让它快

javascript 复制代码
// 练气期的误区:一开始就追求完美

// 正确的修炼顺序:

// 第一步:让它工作(Make it work)
function sort(arr) {
  // 冒泡排序,O(n²),但能用
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        let temp = arr[j]
        arr[j] = arr[j + 1]
        arr[j + 1] = temp
      }
    }
  }
  return arr
}

// 第二步:让它正确(Make it right)
function sort(arr) {
  // 添加参数校验
  if (!Array.isArray(arr)) {
    throw new Error("Input must be an array")
  }
  // 不修改原数组
  const result = [...arr]
  // 排序逻辑...
  return result
}

// 第三步:让它快(Make it fast)
function sort(arr) {
  // 使用更高效的算法
  // 或者直接用内置方法
  return [...arr].sort((a, b) => a - b)
}

第六章:初学乍练的出师考核

6.1 自测题

javascript 复制代码
// 问题1:这段代码有什么问题?
function add(a, b) {
  return a + b
}
console.log(add("1", "2")) // 输出什么?

// 问题2:这段代码会输出什么?
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100)
}

// 问题3:找出Bug
function findMax(arr) {
  let max = 0
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] > max) {
      max = arr[i]
    }
  }
  return max
}
console.log(findMax([-1, -2, -3])) // 期望-1,实际输出?

// 问题4:这段代码的问题是什么?
let user = { name: "张三", age: 18 }
let copy = user
copy.name = "李四"
console.log(user.name) // 输出什么?

6.2 出师标准

当你能做到以下几点,就可以准备突破到小有所成了:

arduino 复制代码
┌─────────────────────────────────────────────────────────────┐
│              初学乍练出师标准 ✓                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   □ 能独立完成简单功能的开发                                 │
│   □ 知道变量、函数、循环、条件判断的基本用法                  │
│   □ 能看懂简单的报错信息                                     │
│   □ 知道Google/Stack Overflow怎么用                         │
│   □ 开始意识到代码规范的重要性                               │
│   □ 能写出别人勉强能看懂的代码                               │
│   □ 经历过至少一次"我写的代码怎么不能用了"的崩溃             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

结语:初学乍练的意义

初学乍练是每个程序员必经的阶段。

在这个阶段,你会:

  • 写出很多"能跑就行"的代码
  • 犯很多现在看来很蠢的错误
  • 经历很多"为什么不行"的崩溃时刻

但这些都是成长的必经之路。

没有人一开始就是高手。

那些现在写出优雅代码的大侠,当年也写过var a = 1; var b = 2; var c = a + b;

所以,不要因为自己是新手而气馁。

每一个 Bug 都是一次修炼,每一次崩溃都是一次突破的契机。

下一篇,我们将进入小有所成------当你开始关注代码规范、开始思考"为什么要这样写"的时候,你就踏入了三流高手的大门。


预告:小有所成

在小有所成阶段,你将学习:

  • 代码规范与最佳实践
  • 设计模式入门
  • 单元测试的重要性
  • 版本控制的正确使用
  • 如何写出"可维护"的代码

敬请期待!


本文是《程序员武学修炼手册》系列的第一篇。

如果你正处于初学乍练阶段,不要焦虑,每个大侠都是从扎马步开始的。

欢迎在评论区分享你初学乍练时的"黑历史"------毕竟,能笑着回忆过去的蠢,说明你已经成长了。 😄

相关推荐
修己xj10 小时前
三月,我只想做好这四件事
程序员
不要秃头啊16 小时前
别再谈提效了:AI 时代的开发范式本质变了
前端·后端·程序员
jonjia17 小时前
引入新维度化解权衡难题
程序员
jonjia17 小时前
优秀的工程师如何打破规则
程序员
jonjia17 小时前
在大厂交付大型项目的策略
程序员
jonjia17 小时前
RFC 与设计文档
程序员
jonjia17 小时前
为什么你(或任何人)应该成为一名研发经理?
程序员
jonjia17 小时前
管理技术质量 (Manage Technical Quality)
程序员
jonjia17 小时前
大厂软件工程师职业发展路径
程序员
jonjia17 小时前
关于工程师与影响力
程序员