嘿,朋友!如果你正在看这篇文章,我猜你大概是被JavaScript的各种"神奇"行为折磨过,或者正准备踏入这个既迷人又有点"神经质"的编程世界。不管怎样,坐稳了,咱们一起聊聊这门让人又爱又恨的语言。

一、JavaScript是个啥?别被名字骗了!

首先,JavaScript和Java的关系,就像雷锋和雷峰塔的关系——除了名字有点像,其他八竿子打不着。

1995年,一个叫Brendan Eich的大佬花了10天搞出了这门语言。没错,就是10天!所以如果你发现JS有些地方设计得有点"随意",嗯... 那是因为它真的是赶工出来的 😅

JavaScript最初叫Mocha,后来改名LiveScript,最后因为跟Sun公司(Java的东家)有合作,就蹭了个热度叫JavaScript。这波营销,我给满分!


二、数据类型:JS的"性格分裂"

JavaScript有7种原始类型(Primitive Types),记住它们,后面要考:

类型说明举个栗子
Number数字(数字+小数)42,3.14
BigInt超大整数956562232484
String字符串“HelloWorld”
Boolean布尔值true,false
Symbol唯一标识符Symbol(‘desc’)
Undefined未定义变量声明了但没赋值
Null空值表示"故意为空"

💡小提示:typeof的“迷惑行为”

typeof null;        // "object"  ← 这是JS的历史bug,一直没修
typeof undefined;   // "undefined"
typeof [];          // "object"  ← 数组也是object
typeof {};          // "object"

看到没?typeof null返回"object",这是JavaScript著名的历史遗留bug,从1995年活到现在,为了兼容性一直没改。所以判断null要这样:

value === null;  // 正确姿势

三、变量声明:var、let、const的"宫斗剧"

1. var —— 老员工,但有点"坑"

var是ES5及之前的变量声明方式,它有两个"致命伤":

问题一:变量提升(Hoisting)

console.log(name);  // undefined(不会报错!)
var name = "张三";

上面的代码实际相当于:

var name;           // 声明被提升到顶部
console.log(name);  // undefined
name = "张三";       // 赋值留在原地

问题二:没有块级作用域

if (true) {
    var x = 10;
}
console.log(x);  // 10  ← 居然能访问到!

2. let —— 新时代的"好员工"(ES6)

console.log(name);  // ReferenceError!报错才是正常的
let name = "李四";

if (true) {
    let y = 20;
}
console.log(y);  // ReferenceError!块级作用域生效了

3. const —— "一旦认定,绝不改变"

const PI = 3.14159;
PI = 3.14;  // TypeError: Assignment to constant variable.

但是! const只保证引用不变,对象内容还是可以改的:

const person = { name: "王五" };
person.name = "赵六";  //  可以!
person = {};           //  报错!不能重新赋值

最佳实践:

  • 默认用 const —— 能不变就不变,代码更安全
  • 需要改变时用 let —— 比如循环计数器
  • 尽量别用 var —— 除非你在维护老代码

四、函数:从"传统派"到"箭头派"

传统函数写法

function add(a, b) {
    return a + b;
}

// 或者函数表达式
const add = function(a, b) {
    return a + b;
};

箭头函数 —— ES6的"语法糖"(真香!)

// 完整写法
const add = (a, b) => {
    return a + b;
};

// 简写:只有一行return时可以省略花括号和return
const add = (a, b) => a + b;

// 只有一个参数时可以省略括号
const square = x => x * x;

// 没有参数时括号不能省
const greet = () => "Hello World";

箭头函数的this:"我继承我爸的"

这是箭头函数最重要的特性!

const obj = {
    name: "小明",
    sayHi: function() {
        setTimeout(function() {
            console.log("Hi, " + this.name);  // "Hi, undefined"
        }, 100);
    },
    sayHiArrow: function() {
        setTimeout(() => {
            console.log("Hi, " + this.name);  // "Hi, 小明" 
        }, 100);
    }
};

箭头函数没有自己的this,它会继承外层作用域的this。这在回调函数里特别有用!


五、解构赋值:"开箱即用"的艺术

数组解构

// 传统写法
const arr = [1, 2, 3];
const a = arr[0];
const b = arr[1];
const c = arr[2];

// 解构写法(优雅!)
const [a, b, c] = [1, 2, 3];

// 跳过某些元素
const [first, , third] = [1, 2, 3];  // first=1, third=3

// 剩余元素
const [head, ...tail] = [1, 2, 3, 4];  // head=1, tail=[2,3,4]

// 默认值
const [x = 10, y = 20] = [1];  // x=1, y=20

对象解构

const user = {
    name: "张三",
    age: 25,
    address: {
        city: "北京",
        street: "长安街"
    }
};

// 基本解构
const { name, age } = user;

// 重命名
const { name: userName } = user;  // userName = "张三"

// 默认值
const { name, gender = "未知" } = user;  // gender = "未知"

// 嵌套解构
const { address: { city } } = user;  // city = "北京"

// 函数参数解构(超实用!)
function printUser({ name, age }) {
    console.log(`${name}今年${age}岁`);
}
printUser(user);  // "张三今年25岁"

六、模板字符串:告别"+"号拼接

ES6之前,字符串拼接是这样的:

const name = "李四";
const age = 30;
const str = "你好," + name + "!你今年" + age + "岁了。";

ES6之后,用反引号(`)和${}:

const str = `你好,${name}!你今年${age}岁了。`;

// 还能写多行!
const html = `
    <div>
        <h1>${title}</h1>
        <p>${content}</p>
    </div>
`;

// 甚至能放表达式
const result = `2 + 3 = ${2 + 3}`;  // "2 + 3 = 5"

七、数组方法:函数式编程入门

JavaScript数组有一堆超实用的方法,学会它们,代码质量直接起飞:

map —— "每个都变一变"

const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2);  // [2, 4, 6]

filter —— "只留符合条件的"

const nums = [1, 2, 3, 4, 5];
const evens = nums.filter(n => n % 2 === 0);  // [2, 4]

reduce —— "汇总成一个值"

const nums = [1, 2, 3, 4];
const sum = nums.reduce((acc, n) => acc + n, 0);  // 10

find —— "找到第一个符合条件的"

const users = [{name: "张三"}, {name: "李四"}];
const user = users.find(u => u.name === "李四");  // {name: "李四"}

includes —— "包不包含?"

[1, 2, 3].includes(2);  // true
"hello".includes("ell");  // true

八、异步编程:从回调地狱到async/await

时代1:回调函数(Callback Hell)

getData(function(a) {
    getMoreData(a, function(b) {
        getMoreData(b, function(c) {
            // ... 嵌套越来越深,人称"回调地狱"
        });
    });
});

时代2:Promise

getData()
    .then(a => getMoreData(a))
    .then(b => getMoreData(b))
    .then(c => console.log(c))
    .catch(err => console.error(err));

时代3:async/await—— 终极解决方案

async function fetchData() {
    try {
        const a = await getData();
        const b = await getMoreData(a);
        const c = await getMoreData(b);
        console.log(c);
    } catch (err) {
        console.error(err);
    }
}

async/await让异步代码看起来像同步代码,可读性大大提升!


九、一些"坑"和冷知识

1. == vs ===:严格相等的重要性

0 == "0";    // true  ← 会进行类型转换
0 === "0";   // false ← 类型不同直接false

null == undefined;   // true
null === undefined;  // false

// 最佳实践:永远用 === 和 !==

2. 假值(Falsy Values)大全

下面这些值在条件判断中都是"假":

false
0
"" (空字符串)
null
undefined
NaN
document.all  // 浏览器里的历史遗留

3. 0.1 + 0.2 !== 0.3

0.1 + 0.2 === 0.3;  // false!
0.1 + 0.2;          // 0.30000000000000004

这是因为计算机用二进制存储浮点数,有些小数无法精确表示。解决方法:

Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON;  // true

4. 闭包(Closure)—— JS的"黑科技"

function makeCounter() {
    let count = 0;
    return function() {
        return ++count;
    };
}

const counter = makeCounter();
counter();  // 1
counter();  // 2
counter();  // 3

内部函数"记住"了外部函数的变量,这就是闭包!


十、总结:JavaScript学习路线图

  1. 基础语法 → 变量、数据类型、运算符、条件、循环
  2. 函数 → 声明、调用、作用域、箭头函数
  3. 对象和数组 → 创建、访问、常用方法
  4. ES6+新特性 → let/const、解构、模板字符串、展开运算符
  5. 异步编程 → Promise、async/await
  6. 进阶概念 → 闭包、原型链、事件循环

总结

JavaScript是一门容易上手,但难以精通的语言。它有很多"坑",但也有很多巧妙的设计。希望这篇文章能帮你建立起对JS的整体认知,少走弯路。

记住:最好的学习方式就是写代码。看完这篇文章,打开你的编辑器,动手试试吧!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐