JS 入门:从变量、函数到 DOM 操作

运营资讯4小时前发布 jdkjadmin
0 00
JS 入门:从变量、函数到 DOM 操作

JS 入门:从变量、函数到 DOM 操作

JavaScript(简称 JS)是网页开发中常用的编程语言。它可以在浏览器中响应点击、校验输入、更新页面内容,也可以运行在服务器、命令行工具和桌面应用环境中。初学者接触 JS,通常从变量、条件判断、函数和 DOM 操作开始。

本文会逐步介绍这些基础内容,并说明它们适合解决什么问题、容易在哪里出错。示例以现代浏览器支持的 JavaScript 语法为主。将示例代码放进 HTML 页面的 <script> 标签,或放入浏览器开发者工具的 Console 中运行即可。涉及页面元素的示例需要先在页面中创建对应的 HTML 元素。

一、JavaScript 在哪里运行

浏览器内的 JavaScript 可以操作当前网页,但它并不是 HTML 的替代品。HTML 描述内容结构,CSS 描述样式,JavaScript 负责行为和动态逻辑。例如,按钮本身由 HTML 创建,颜色和布局由 CSS 控制,点击后显示提示则可以由 JavaScript 处理。

网页中的脚本可以写在 HTML 文件里,也可以放在单独的 .js 文件中。初学时可以用内嵌脚本快速验证;项目稍大后,建议拆分文件,便于维护。外部脚本通常这样引入:

<script src="app.js" defer></script>

defer 表示脚本在 HTML 解析完成后执行,并按文档中的顺序执行。对于需要访问页面元素的普通脚本,它可以避免脚本运行时元素尚未解析的问题。另一种做法是把脚本放在 </body> 结束标签前,但应选择一种清晰一致的方式。

二、变量:保存和更新数据

变量用来给数据起名字,让程序可以读取或更新它。现代 JavaScript 通常使用 const 和 let:

  • const 用于不会重新赋值的变量。
  • let 用于需要重新赋值的变量。
  • var 是较早的声明方式,作用域规则与前两者不同,新代码通常优先使用 const 和 let。
const siteName = "示例网站";
let visitCount = 0;

visitCount = visitCount + 1;

console.log(siteName);
console.log(visitCount);

const 限制的是变量绑定不能被重新赋值,并不代表它引用的对象内容不可更改:

const user = { name: "小林" };

user.name = "小周"; // 可以修改对象的属性
console.log(user.name);

// user = {}; // TypeError:不能给 const 变量重新赋值

变量名应当表达用途,例如 totalPrice 比 x 更易读。变量名区分大小写,不能以数字开头,也不能使用保留关键字。常见风格是使用小驼峰命名法:userName、isVisible。

三、常见数据类型与比较

初学阶段经常遇到的类型包括字符串、数字、布尔值、null、undefined、对象和数组。可以使用 typeof 查看很多值的类型,但它并不能对所有类型给出直观结果。

const title = "JavaScript";
const price = 19.9;
const isReady = true;
let currentUser;
const emptyValue = null;

console.log(typeof title);       // "string"
console.log(typeof price);       // "number"
console.log(typeof isReady);     // "boolean"
console.log(typeof currentUser); // "undefined"
console.log(typeof emptyValue);  // "object"(历史遗留行为)

undefined 常表示一个值尚未赋值,例如声明了变量但没有给它赋值;null 通常用于主动表示“这里没有对象或值”。这两者不是同一个值。

比较时,推荐优先使用严格相等运算符 === 和严格不等运算符 !==。它们不会像宽松比较 == 那样尝试进行类型转换。

console.log(5 === 5);   // true
console.log(5 === "5"); // false
console.log(5 == "5");  // true,发生了类型转换

字符串与数字混用时,先明确转换规则,不要依赖宽松比较或隐式转换。比如从输入框读取到的内容通常是字符串,即使用户输入的是数字:

const inputValue = "42";

console.log(inputValue + 1);        // "421",字符串拼接
console.log(Number(inputValue) + 1); // 43

Number() 可以尝试把值转换为数字,但不合法的输入会得到 NaN。可用 Number.isNaN() 检查转换结果。不要仅凭“看起来像数字”就假设转换一定成功。

const amount = Number("abc");

if (Number.isNaN(amount)) {
  console.log("请输入有效数字");
}

四、条件判断与循环

条件判断适合根据不同状态执行不同逻辑。条件表达式会按真值或假值进行判断。常见假值包括 false、0、空字符串 ""、null、undefined 和 NaN;空数组和空对象则是真值。

const score = 86;

if (score >= 90) {
  console.log("优秀");
} else if (score >= 60) {
  console.log("通过");
} else {
  console.log("需要继续练习");
}

使用真值判断时要留意:如果数字 0 是有意义的输入,直接写 if (value) 会把它当成假值。需要区分“没有提供值”和“提供了 0”时,应明确检查 null 或 undefined 等情况。

for 循环适合已知次数或遍历数组的情形;while 循环适合循环次数取决于条件的情况。循环条件必须能在合适的时候变为假,否则会造成无限循环。

const colors = ["红", "绿", "蓝"];

for (let i = 0; i < colors.length; i += 1) {
  console.log(colors[i]);
}

let count = 3;
while (count > 0) {
  console.log(count);
  count -= 1;
}

遍历数组还可以使用 for...of,它直接取得每一项,通常比手动维护下标更简洁:

for (const color of colors) {
  console.log(color);
}

五、函数:把逻辑组织起来

函数用于封装可重复使用的逻辑。它可以接收参数,也可以通过 return 返回结果。函数适合将较大的任务拆成有明确职责的小部分,例如格式化文本、计算金额或处理一次按钮交互。

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

const result = add(3, 4);
console.log(result); // 7

函数声明可以在其所在作用域内先调用、后声明,这是声明提升的结果。函数表达式和箭头函数则通常要先完成赋值,再调用:

const multiply = (a, b) => {
  return a * b;
};

console.log(multiply(3, 4)); // 12

只有一个参数时,箭头函数的参数括号可以省略;只有一个表达式并且要直接返回时,可以省略大括号和 return:

const double = number => number * 2;

console.log(double(6)); // 12

函数参数在 JavaScript 中没有统一的、面向所有环境和场景的“最多参数数量”保证。引擎、运行环境和具体调用方式可能存在实现限制,不应把极大的参数列表当作可靠设计。实际代码中应让函数职责清晰;参数较多时,可以传入对象并使用命名属性。

function createAccount({ name, email, isAdmin = false }) {
  return { name, email, isAdmin };
}

const account = createAccount({
  name: "小陈",
  email: "[email protected]"
});

console.log(account);

解构参数中的默认值只会在属性值为 undefined 时生效,传入 null 不会触发默认值。调用函数时少传参数,缺少的参数通常是 undefined;多传的参数不会自动报错,但未命名接收的参数不会出现在普通参数变量中。需要读取可变数量的参数时,可以使用剩余参数语法:

function sum(...numbers) {
  return numbers.reduce((total, number) => total + number, 0);
}

console.log(sum(2, 3, 5)); // 10

剩余参数必须放在参数列表末尾。它接收的是一个数组,和较早的 arguments 对象不同。设计函数时,最好检查参数是否符合业务要求;参数类型、范围和缺省行为应当清晰,而不要假设调用者总会传入正确数据。

函数还有作用域的概念。函数内部用 let 或 const 声明的变量,通常不能在函数外直接访问。合理的作用域有助于避免变量意外被其他逻辑修改。

六、数组与对象

数组用于按顺序保存多个值。常见操作包括读取下标、添加元素和遍历。数组下标从 0 开始,访问不存在的下标通常会得到 undefined。

const tasks = ["阅读", "练习"];

console.log(tasks[0]); // "阅读"

tasks.push("复习");
console.log(tasks.length); // 3

for (const task of tasks) {
  console.log(task);
}

需要从数组中筛选、转换或查找数据时,可以使用 filter、map、find 等方法。这些方法会根据回调函数处理元素,适合表达“得到一个新数组”或“找出符合条件的项”。

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

const doubled = numbers.map(number => number * 2);
const evenNumbers = numbers.filter(number => number % 2 === 0);
const firstLarge = numbers.find(number => number > 2);

console.log(doubled);    // [2, 4, 6, 8]
console.log(evenNumbers); // [2, 4]
console.log(firstLarge);  // 3

对象用于把相关数据组织成键值对。属性可以用点号访问,也可以在属性名动态变化时使用方括号。

const product = {
  name: "笔记本",
  price: 35
};

console.log(product.name);

const key = "price";
console.log(product[key]);

对象和数组赋值时传递的是引用。把对象赋给另一个变量,并不会自动复制对象本身:

const original = { count: 1 };
const alias = original;

alias.count = 2;
console.log(original.count); // 2

如果确实需要复制,浅拷贝可以使用展开语法,但它只复制第一层;嵌套对象仍可能共享引用。应根据数据结构选择合适的复制方式,不要误以为展开语法能完成任意深度的独立复制。

七、DOM:读取和修改网页元素

DOM(文档对象模型)是浏览器对 HTML 文档提供的编程接口。页面加载后,JavaScript 可以用 DOM 查找元素、更新文字、设置属性和监听事件。它适用于网页交互,不适用于没有网页 DOM 的普通运行环境,例如某些服务器端脚本环境。

现代代码通常使用 document.querySelector() 按 CSS 选择器获取第一个匹配元素;要获取多个匹配项,可以使用 document.querySelectorAll()。找不到元素时,querySelector() 会返回 null,所以调用属性或方法前要先确认元素存在。

<h1 id="page-title">欢迎</h1>
<p class="status">等待操作</p>

<script>
  const title = document.querySelector("#page-title");
  const status = document.querySelector(".status");

  if (title && status) {
    title.textContent = "JavaScript 入门";
    status.textContent = "页面已就绪";
  }
</script>

textContent 用于设置或读取纯文本。若目标是展示用户输入,优先使用它,而不是把输入拼成 HTML 后赋给 innerHTML。innerHTML 会把字符串按 HTML 解析;如果字符串含有不可信内容,可能造成跨站脚本攻击(XSS)。

const message = document.querySelector("#message");
const userInput = "<img src=x onerror=alert('危险')>";

if (message) {
  message.textContent = userInput; // 按文本显示,不会把输入解析成标签
}

textContent 适合普通文字,但不负责验证链接、脚本、样式或其他业务规则。需要把内容作为 HTML 渲染时,不能简单认为“过滤几个字符”就足够安全。应使用经过维护的 HTML 清理工具,并根据具体上下文采取安全处理;对于不可信的 URL,也要检查协议和允许的目标。

八、事件:响应用户操作

事件机制让页面可以响应点击、输入、提交等操作。使用 addEventListener() 注册监听器,可以把事件行为与 HTML 结构分开。事件处理函数会收到事件对象,常见属性包括触发事件的目标元素。

<button id="counter-button" type="button">点击次数:0</button>

<script>
  const button = document.querySelector("#counter-button");
  let count = 0;

  if (button) {
    button.addEventListener("click", () => {
      count += 1;
      button.textContent = `点击次数:${count}`;
    });
  }
</script>

如果监听器需要在之后移除,可以保存同一个函数引用,再传给 removeEventListener()。临时写入的匿名函数没有可复用的引用,不能用另一个内容相同的匿名函数移除原监听器。

const button = document.querySelector("#save-button");

function handleSave() {
  console.log("已保存");
}

if (button) {
  button.addEventListener("click", handleSave);

  // 需要时移除同一个函数引用
  button.removeEventListener("click", handleSave);
}

表单提交时,浏览器默认会尝试提交表单并可能刷新页面。若要在当前页面自行处理,需要在提交事件中调用 event.preventDefault()。这只会阻止该次默认行为,不等于输入内容已安全或合法。

<form id="name-form">
  <label for="name-input">姓名</label>
  <input id="name-input" name="name" required>
  <button type="submit">提交</button>
</form>
<p id="form-result"></p>

<script>
  const form = document.querySelector("#name-form");
  const input = document.querySelector("#name-input");
  const result = document.querySelector("#form-result");

  if (form && input && result) {
    form.addEventListener("submit", event => {
      event.preventDefault();

      const name = input.value.trim();

      if (name.length === 0) {
        result.textContent = "请输入姓名";
        return;
      }

      result.textContent = `你好,${name}`;
    });
  }
</script>

表单上的 required 等 HTML 属性可以提供浏览器端的基础约束,但不能替代服务端验证。用户可以关闭或绕过页面脚本,直接构造请求。凡是关系到权限、账户、价格、资源访问或数据完整性的规则,都必须在可信的服务端重新验证。

九、DOM 操作的时机与常见问题

常见错误之一是脚本运行时元素还不存在。例如脚本写在 <head> 中并立即查询页面下方的按钮,查询结果可能是 null。可以使用外部脚本的 defer,或等 DOM 解析完成后再执行代码:

document.addEventListener("DOMContentLoaded", () => {
  const button = document.querySelector("#action-button");

  if (!button) {
    return;
  }

  button.addEventListener("click", () => {
    console.log("DOM 已就绪,按钮被点击");
  });
});

另一个常见问题是混淆 querySelector() 和 querySelectorAll():前者返回单个元素或 null,后者返回匹配元素集合,即使没有匹配项,也可以得到一个空集合。遍历多个元素时,可以这样写:

const items = document.querySelectorAll(".item");

items.forEach(item => {
  item.classList.add("processed");
});

修改样式时,优先通过 classList 添加、移除或切换 CSS 类,而不是在脚本里到处写行内样式。这样能让样式职责仍由 CSS 管理。

const panel = document.querySelector(".panel");

if (panel) {
  panel.classList.toggle("is-open");
}

十、初学者常见错误清单

  • 变量未定义或拼写不一致:JavaScript 区分大小写,userName 与 username 是不同名称。善用开发者工具的错误提示。
  • 把输入框内容当作数字:input.value 通常是字符串,需要按业务规则转换并检查结果。
  • 使用 == 导致意外转换:没有明确理由时使用 ===,减少难以察觉的比较行为。
  • 忘记 DOM 查询可能失败:检查选择器是否写对、元素是否已加载,并在访问前处理 null。
  • 把用户输入塞进 innerHTML:不可信字符串可能被浏览器当作 HTML 解析。展示文字优先用 textContent。
  • 只在浏览器校验数据:前端校验改善交互体验,但不能建立信任。服务端仍需验证并执行权限检查。
  • 循环无法结束:检查循环变量是否更新,以及条件是否能最终变为假。
  • 误以为 const 会冻结对象:const 阻止重新赋值,不会自动冻结对象或数组。

十一、组合练习:用 DOM 添加清单项目

下面把变量、事件、表单输入和 DOM 操作组合起来。将完整代码放入一个 HTML 文件中即可运行。提交非空文字后,页面会增加一条清单项目。示例使用 textContent 写入用户内容,避免把输入作为 HTML 解析。

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>清单练习</title>
</head>
<body>
  <h1>今日清单</h1>

  <form id="task-form">
    <label for="task-input">新任务</label>
    <input id="task-input" name="task" required>
    <button type="submit">添加</button>
  </form>

  <p id="notice" aria-live="polite"></p>
  <ul id="task-list"></ul>

  <script>
    const form = document.querySelector("#task-form");
    const input = document.querySelector("#task-input");
    const list = document.querySelector("#task-list");
    const notice = document.querySelector("#notice");

    if (form && input && list && notice) {
      form.addEventListener("submit", event => {
        event.preventDefault();

        const taskText = input.value.trim();

        if (taskText.length === 0) {
          notice.textContent = "请输入任务内容";
          return;
        }

        const item = document.createElement("li");
        item.textContent = taskText;
        list.append(item);

        input.value = "";
        notice.textContent = "任务已添加";
        input.focus();
      });
    }
  </script>
</body>
</html>

练习时可以继续添加删除按钮、任务完成状态或本地保存功能。每增加一种行为,都先确定数据从哪里来、要修改哪一部分页面,以及用户提供的内容是否会被当作可执行代码。这样逐步扩展,比把所有逻辑堆进一个事件回调更容易调试。

结语

学习 JavaScript 的基础路线可以概括为:先用变量和数据类型表达状态,再用条件与循环控制流程,用函数拆分逻辑,最后通过 DOM 和事件让页面响应用户操作。写网页脚本时,尤其要处理好元素可能不存在、输入类型需要转换、前端校验不能代替服务端验证,以及不可信文本不能随意作为 HTML 渲染等边界。

掌握语法之后,建议在浏览器开发者工具中逐段运行代码,观察变量值、事件触发顺序和 DOM 变化。遇到问题时,先检查控制台报错,再确认选择器、数据类型和执行时机,通常就能定位大多数入门阶段的错误。

© 版权声明

相关文章