"千里之行,始于足下;万行代码,始于 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 都是一次修炼,每一次崩溃都是一次突破的契机。
下一篇,我们将进入小有所成------当你开始关注代码规范、开始思考"为什么要这样写"的时候,你就踏入了三流高手的大门。
预告:小有所成
在小有所成阶段,你将学习:
- 代码规范与最佳实践
- 设计模式入门
- 单元测试的重要性
- 版本控制的正确使用
- 如何写出"可维护"的代码
敬请期待!
本文是《程序员武学修炼手册》系列的第一篇。
如果你正处于初学乍练阶段,不要焦虑,每个大侠都是从扎马步开始的。
欢迎在评论区分享你初学乍练时的"黑历史"------毕竟,能笑着回忆过去的蠢,说明你已经成长了。 😄