Back to Blog
· 5 min read

JavaScript 基础概念

js

你是否曾经遇到过这样的代码?在 setTimeout 回调里想访问外层的 this,却发现它变成了 undefined。或者在处理数组时,写了一堆 for 循环,最后发现可以用一行 map 解决。这些问题,都指向 JavaScript 最基础,也是最核心的概念。

本文主要记录使用和学习 JavaScript 的过程中,我认为比较重要的概念:箭头函数、高阶函数、原型链、代码拆分和模板。这些概念,对于无论是刚入门的新手,还是想巩固基础的开发者,都至关重要。


1. 箭头函数:简洁语法的背后

ES6 引入的箭头函数,不仅仅是语法糖。它解决了 JavaScript 中 this 绑定的长期困扰。

1.1 基本语法

const add = (a, b) => {
  return a + b;
};

// 隐式返回 - 省略花括号和 return
const multiply = (a, b) => a * b;

// 单参数 - 省略括号
const double = x => x * 2;

ps: 当函数体只有单个表达式时,省略花括号会让代码更简洁。

1.2 核心特性:词法 this 绑定

箭头函数最重要的特性:不绑定自己的 this

// 箭头函数:this 指向定义时的上下文
function Counter() {
  this.count = 0;
  setInterval(() => {
    this.count++; // 这里的 this 指向 Counter 实例
    console.log(this.count);
  }, 1000);
}

const counter = new Counter();
// 输出: 1, 2, 3, ...

对比普通函数:

// 普通函数:this 指向调用时的上下文
function Counter() {
  this.count = 0;
  setInterval(function() {
    this.count++; // 这里的 this 指向 global 或 undefined
    console.log(this.count);
  }, 1000);
}

1.3 使用限制

箭头函数不是万能的。以下场景不适合使用:

  • 对象方法this 无法动态绑定
  • 构造函数:不能使用 new 调用
  • 原型方法prototype 属性不存在
// ❌ 错误用法
const obj = {
  name: 'test',
  getName: () => this.name  // this 指向外层作用域,不是 obj
};

// ✅ 正确用法
const obj = {
  name: 'test',
  getName() { return this.name; }  // 普通方法
};

2. 高阶函数:函数式编程的基石

既然说到了函数,就不得不提高阶函数——JavaScript 函数式编程的核心。

高阶函数是指:接收函数作为参数,或返回函数的函数。

2.1 数组处理的三板斧

const numbers = [1, 2, 3, 4, 5];

// map: 转换每个元素
const doubled = numbers.map(x => x * 2);
// [2, 4, 6, 8, 10]

// filter: 筛选符合条件的元素
const evens = numbers.filter(x => x % 2 === 0);
// [2, 4]

// reduce: 汇总为单个值
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
// 15
graph LR
    A["原始数组 1,2,3,4,5"] --> B["map"]
    B --> C["新数组 2,4,6,8,10"]
    A --> D["filter"]
    D --> E["筛选结果 2,4"]
    A --> F["reduce"]
    F --> G["单一值 15"]

2.2 组合使用

高阶函数的真正威力在于组合:

const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 17 },
  { name: 'Charlie', age: 30 }
];

// 获取成年用户名字
const adultNames = users
  .filter(user => user.age >= 18)
  .map(user => user.name);

// ['Alice', 'Charlie']

性能提示: 避免在 map/filter/reduce 中创建不必要的中间数组。链式调用虽然简洁,但数据量大时可能需要手动优化。

2.3 更多高阶函数

// find: 查找第一个匹配元素
const firstEven = numbers.find(x => x % 2 === 0);

// some: 是否有任意匹配
const hasNegative = numbers.some(x => x < 0);

// every: 是否全部匹配
const allPositive = numbers.every(x => x > 0);

// bind: 绑定 this 和参数
const log = console.log.bind(console);

3. 原型链:JavaScript 的继承

提到继承,Java 和 C++ 开发者可能会想到”类”。但 JavaScript 没有类 —— 它用的是原型。

3.1 原型链工作机制

每个 JavaScript 对象都有一个 __proto__ 属性,指向它的原型。当访问对象的属性时,如果对象本身没有这个属性,JavaScript 会沿着原型链向上查找。

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  return `Hello, I'm ${this.name}`;
};

const alice = new Person('Alice');
console.log(alice.greet()); // "Hello, I'm Alice"

// 原型链: alice → Person.prototype → Object.prototype → null
graph TD
    A["alice 实例"] -->|"__proto__"| B["Person.prototype"]
    B -->|"__proto__"| C["Object.prototype"]
    C -->|"__proto__"| D["null"]

3.2 原型继承

function Employee(name, jobTitle) {
  Person.call(this, name);  // 调用父构造函数
  this.jobTitle = jobTitle;
}

// 原型继承
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;

Employee.prototype.work = function() {
  return `${this.name} is working as ${this.jobTitle}`;
};

const bob = new Employee('Bob', 'Developer');
console.log(bob.greet()); // "Hello, I'm Bob"  (继承自 Person)
console.log(bob.work()); // "Bob is working as Developer"

3.3 ES6 Class 的本质

很多人以为 ES6 的 class 是 JavaScript 新的继承方式。其实,它只是原型继承的语法糖。

// ES6 Class 写法
class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return `Hello, I'm ${this.name}`;
  }
}

// 等价于原型写法
function Person(name) {
  this.name = name;
}
Person.prototype.greet = function() {
  return `Hello, I'm ${this.name}`;
};

3.4 常见陷阱

// ❌ 错误:修改原型影响所有实例
Person.prototype.sayHi = () => console.log('Hi');
// 所有 Person 实例都受影响

// ✅ 正确:只在实例上添加
alice.sayHi = () => console.log('Hi');
// 只有 alice 实例有这个方法

4. 代码拆分:性能优化的关键

首屏加载太慢?用户流失严重。此时需要的可能就是代码拆分(Code Splitting)。

4.1 动态导入

ES6 的 import() 允许动态加载模块:

// 静态导入 - 立即加载
import { utils } from './utils.js';

// 动态导入 - 按需加载
button.addEventListener('click', () => {
  import('./heavyModule.js')
    .then(module => {
      module.doSomething();
    });
});
sequenceDiagram
    participant User
    participant Page
    participant Network
    participant Server

    User->>Page: 点击按钮
    Page->>Network: import('./heavyModule.js')
    Network->>Server: 请求模块
    Server-->>Network: 返回模块代码
    Network-->>Page: 加载完成
    Page->>Page: 执行模块代码

4.2 Webpack 拆分策略

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',      // 分割所有类型的 chunk
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
  },
};

4.3 拆分策略对比

策略适用场景优点缺点
路由级拆分SPA 应用简单易实现粒度粗
组件级拆分大型组件按需加载配置复杂
库级拆分依赖稳定长期缓存初始请求多

实战建议: 将 node_modules 单独打包。利用浏览器的长期缓存,减少重复下载。


5. 模板:动态渲染的艺术

如何安全地拼接动态数据到 HTML?模板技术是关键。

5.1 模板字符串

ES6 模板字符串极大缓解了拼接字符串时不断输入引号的折磨:

const user = { name: 'Alice', age: 25 };

// 模板字符串
const html = `
  <div class="user-card">
    <h2>${user.name}</h2>
    <p>Age: ${user.age}</p>
  </div>
`;

// 对比旧写法
const htmlOld = '<div class="user-card">' +
  '<h2>' + user.name + '</h2>' +
  '<p>Age: ' + user.age + '</p>' +
  '</div>';

5.2 模板引擎

复杂场景下,使用模板引擎:

<!-- Handlebars 模板 -->
<script id="user-template" type="text/x-handlebars-template">
  <div class="user">
    <h2>{{name}}</h2>
    {{#if age}}
      <p>Age: {{age}}</p>
    {{/if}}
    <ul>
      {{#each hobbies}}
        <li>{{this}}</li>
      {{/each}}
    </ul>
  </div>
</script>
// 编译并渲染
const template = Handlebars.compile(
  document.getElementById('user-template').innerHTML
);

const html = template({
  name: 'Alice',
  age: 25,
  hobbies: ['Reading', 'Coding', 'Gaming']
});

5.3 安全注意

XSS 防护: 永远不要直接将用户输入插入模板。使用模板引擎的自动转义功能。

// ❌ 危险
const html = `<div>${userInput}</div>`;

// ✅ 安全 - 自动转义
const html = template({ userInput });

6. 总结

核心要点 概念应用场景
箭头函数词法 this 绑定回调、数组方法
高阶函数函数作为参数/返回值数据处理、函数组合
原型链基于原型的继承对象创建、继承模式
代码拆分按需加载性能优化
模板数据驱动渲染UI 动态更新

推荐

  • 深入理解: MDN 原型文档
  • 进阶主题:
    • 异步编程 (Promise, async/await)
    • 模块系统 (CommonJS, ES Modules)
    • 装饰器与代理

学习是一个持续的过程,基础概念是进阶的前提,常看常新,共勉。