大家好!JavaScript 作为前端开发的核心语言,也是全栈开发的必备技能,其灵活的语法特性既让开发者爱不释手,也让新手容易踩坑。
这篇文章会从 JavaScript 的核心语法入手,拆解变量类型与函数的核心逻辑,讲解事件监听,再结合实战案例巩固用法,帮你夯实 JS 基础,避开新手常见的语法陷阱。
一、JavaScript 核心认知:先搞懂 "它的定位与特点"
1.1 什么是 JavaScript?
JavaScript(简称 JS)是一门解释型、弱类型、面向对象(基于原型) 的脚本语言,最初设计用于前端页面交互,如今已扩展到后端(Node.js)、移动端、桌面端等领域,成为全栈开发的核心语言。
1.2 核心特点
- 弱类型 :变量类型无需提前声明,可动态改变(如
let a = 1; a = "hello"); - 解释执行:无需编译,由 JS 引擎(如 V8)逐行解析执行;
- 单线程:同一时间只能执行一个任务,通过事件循环实现异步;
- 基于原型:没有类的概念,通过原型链实现继承。
1.3 JavaScript的组成
- ECMAScript: 规定了JS基础语法核心知识,包括变量、数据类型、流程控制、函数、对象等。
- BOM:浏览器对象模型,用于操作浏览器本身,如:页面弹窗、地址栏操作、关闭窗口等。
- DOM:文档对象模型,用于操作HTML文档,如:改变标签内的内容、改变标签内字体样式等。
1.4 为什么要学好 JS 基础语法?
基础语法是编写高效、可维护代码的前提:
- 避免语法错误:比如变量提升、类型转换导致的逻辑 bug;
- 理解框架原理:Vue、React 等框架的底层都基于 JS 核心语法(如原型、闭包);
- 提升调试效率:能快速定位 "看似正确却运行异常" 的语法问题。
二、语法一:js基础语法(基础中的基础)
2.1 1. JS引入方式
js代码也是书写在html中的,那么html中如何引入js代码呢?主要通过下面的3种引入方式:
代码示例:对比三种引入方式
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS-引入方式</title>
</head>
<body>
<!--
ctrl+shift+/生成了html代码注释
Ctrl+S 保存 -->
<!--
写js代码方式1:行内方式,在html标签里面写js代码
<html标签 on属性="js代码"></html标签>
alert(字符串) 是js的函数,弹出提示框消息
onclick 是标签点击的行为,当用户点击标签元素就会触发js代码的执行
-->
<button onclick="alert('方式1')">按钮</button>
<!--
写js代码方式2:内部方式,js代码写在<script>标签内部 -->
<script>
alert('方式2')
</script>
<!--
写js代码方式3:外部方式,js代码写在外部的.js文件中
步骤: 1.创建js文件,编写js代码
2.在html网页中引入js文件,才会运行里面的js代码
注意: 用于引入外部js文件的<script src="./js/out.js"></script>
标签里面不能写js代码,因为这个src是用于引入外部的所以不会运行标签之间的代码
所有js代码都是从上往下运行的。 -->
<script src="./js/out.js">
alert('无效的代码位置')
</script>
<script src="js/demo01.js"></script>
</body>
</html>
核心总结:
- 疑问:三种方式,以后会使用哪种?
- 答:都会使用,但是第三种的复用性最好,因为不仅可以给当前页面使用,还可以给其他网页使用。
- 一般推荐,如果js编程操作某个元素推荐使用方式1,如果是多个页面都要用推荐方式3,其他就推荐方式2
2.2 输出语句
- Jdocument.write() :向HTML的body内输出内容
- window.alert() :弹出警告框
- console.log() :写入浏览器控制台(程序员调试使用)
代码示例:
javascript
<script>
//目标1:输出语句
// document.write():向HTML的body内输出内容
document.write('<h1>hello javascript')
// window.alert():弹出警告框, window代表当前窗口,是可以省略的
alert('hello javascript')
// console.log():写入浏览器控制台(程序员调试使用)
console.log('javascript');
</script>
三、语法二:变量与数据类型(基础中的基础)
2.1 变量的声明方式(核心区别与使用场景)
JS 中有三种变量声明方式,其作用域、提升规则、可修改性差异巨大,是新手最易混淆的点。
| 声明方式 | 作用域 | 变量提升 | 可重复声明 | 可修改值 | 适用场景 |
|---|---|---|---|---|---|
| var | 函数作用域 / 全局作用域 | 有(提升到作用域顶部,值为 undefined) | 可以 | 可以 | 旧代码兼容(不推荐) |
| let | 块级作用域({} 内) | 有(暂时性死区,不可提前使用) | 不可以 | 可以 | 需修改的变量(推荐) |
| const | 块级作用域 | 有(暂时性死区) | 不可以 | 不可以(引用类型可修改内部属性) | 常量、不修改的变量(推荐) |
代码示例:对比三种声明方式
javascript
// 1. var的问题:函数作用域+重复声明
var a = 1;
var a = 2; // 允许重复声明
if (true) {
var a = 3;
}
console.log(a); // 3(块级作用域失效)
// 2. let的优势:块级作用域+不可重复声明
let b = 1;
// let b = 2; // 报错:Identifier 'b' has already been declared
if (true) {
let b = 3;
}
console.log(b); // 1(块级作用域有效)
// 3. const的特性:不可修改值(引用类型除外)
const c = 1;
// c = 2; // 报错:Assignment to constant variable
const obj = { name: "张三" };
obj.name = "李四"; // 允许修改引用类型的内部属性
console.log(obj.name); // 李四
核心总结 :开发中优先使用let和const,避免var的坑;常量用const,变量用let。
2.2 数据类型(基本类型 + 引用类型 + 类型判断)
JS 的数据类型分为基本类型 和引用类型,两者的存储方式差异是导致 "值传递" 与 "引用传递" 的根本原因。
(1)基本类型(7 种)
包括:String、Number、Boolean、Null、Undefined、Symbol(ES6)、BigInt(ES11)。
特点:
- 存储在栈内存中,直接保存值;
- 赋值时是 "值传递",修改新变量不影响原变量。
代码示例:
运行
javascript
//目标:数据类型
// 基本数据类型:
// number:数字(整数、小数、NaN(Nota Number))
// boolean:布尔。true,false
// null:对象为空。JavaScript是大小写敏感的,因此null、Null、NULL是完全不同的
// undefined:当声明的变量未初始化时,该变量的默认值是undefined
// string:字符串,单引号、双引号、反引号皆可,推荐使用单引号
let c = 10
let d = 3.14
let e = true
let f = null
let g
let h = 'abc'
document.write('<p>1 的类型:' + typeof c + '</p>')
document.write('<p>2 的类型:' + typeof d + '</p>')
document.write('<p>3 的类型:' + typeof e + '</p>')
document.write('<p>4 的类型:' + typeof f + '</p>')
document.write('<p>5 的类型:' + typeof g + '</p>')
document.write('<p>6 的类型:' + typeof h + '</p>'
let num1 = 10;
let num2 = num1;
num2 = 20;
console.log(num1); // 10(值传递,互不影响)
字符串
对于字符串类型的数据,除了可以使用双引号("...")、单引号('...')以外,还可以使用反引号 (``)。 而使用反引号引起来的字符串,也称为 模板字符串。
-
模板字符串的使用场景:拼接字符串和变量。
-
模板字符串的语法:
- ` :反引号 (英文输入模式下键盘 tab 键上方波浪线 ~ 那个键)
- 内容拼接时,使用 ${ } 来引用变量
具体示例如下:
javascript
//目标4:模板字符串
//js字符串类型支持3种:'' "" ``
// '' "" 使用区别:外部是""内部使用'',获取外部''内部双引号
let x = '您好,"程序员根根"'
let y = "您好,'程序员根根'"
// `` 用于模板字符串简化字符串拼接
let name = '根根'
let age = 24
//传统拼接字符串
let str1 = '你好我的名字叫' + name + ',今年' + age + '岁了!'
//模板字符串拼接
let str2 = `你好我的名字叫${name},今年${age}岁了!`;
document.write(`<p>${str2}<\p>`)
(2)引用类型
包括:Object(含Array、Function、Date、RegExp等)。
特点:
- 存储在堆内存中,栈内存只保存指向堆的引用地址;
- 赋值时是 "引用传递",修改新变量会影响原变量。
代码示例:
javascript
let obj1 = { name: "张三" };
let obj2 = obj1;
obj2.name = "李四";
console.log(obj1.name); // 李四(引用传递,指向同一内存)
(3)类型判断的正确方式(面试高频)
新手常误用typeof判断所有类型,但其对Null和引用类型的判断有局限,需结合Object.prototype.toString.call()使用。
| 类型 | typeof 结果 | Object.prototype.toString.call () 结果 |
|---|---|---|
| String | "string" | "[object String]" |
| Number | "number" | "[object Number]" |
| Boolean | "boolean" | "[object Boolean]" |
| Null | "object"(历史 bug) | "[object Null]" |
| Undefined | "undefined" | "[object Undefined]" |
| Array | "object" | "[object Array]" |
| Object | "object" | "[object Object]" |
| Function | "function" | "[object Function]" |
代码示例:
javascript
console.log(typeof null); // "object"(坑)
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(typeof []); // "object"
console.log(Object.prototype.toString.call([])); // "[object Array]"
2.3 流程控制
在JS中,当然也存在对应的流程控制语句。常见的流程控制语句如下:
- if ... else if ... else ...
- switch
- for
- while
- do ... while
而JS中的流程控制语句与JAVA中的流程控制语句的作用,执行机制都是一样的。就不做过多说明。
代码
javascript
//目标:实现1~100的累加
//循环语法: for(let i=1;i<=100;i++){循环体代码}
function sum4(number) {
//1.定义累加变量 total1
let total1 = 0
//2.使用循环语法累加
for (let i = 1; i <= number; i++) {
total1 += i//相当于 total1 = total1 + i
}
return total1
}
//3.打印结果
document.write(`1~100的累加为:${sum4(100)}<br\>`)//<br/>是html标签换行
document.write(`abc<br\>`)//<br/>是html标签换行
四、语法三:函数
函数(function) 是被设计用来执行特定任务的代码块,方便程序的封装复用。 那我们学习函数,主要就是学习JS中函数的定义及调用的语法。
注意
因为JavaScript是弱数据类型的语言,所以有如下几点需要注意:
- 形参不需要声明类型,并且JS中不管什么类型都是let去声明,加上也没有意义。
- 返回值也不需要声明类型,直接return即可
作用域决定了变量的可访问范围,JS 的作用域规则可总结为:
- 全局作用域:在所有函数外部声明的变量,可在整个程序中访问;
- 函数作用域:在函数内部声明的变量,仅在函数内部可访问;
- 块级作用域 :
let/const声明的变量,在{}(如 if、for、while)内可访问。
3.1 方式一:命名函数
分类1:命名函数,需要定义函数名的函数
定义命名语法:function 函数名(形参列表){ 函数体代码 }
代码示例:
javascript
//方式1
function sum1(a, b, c) {
return a + b + c
}
//调用函数
let total = sum1(10, 20, 30)
document.write(`<p>${total}<\P>`)
3.2 方式二:匿名函数
刚才我们定义函数,是为函数指定了一个名字。 那我们也可以不为函数指定名字,那这一类的函数,我们称之为匿名函数。那接下来,方式二,就来介绍一下匿名函数的定义和调用。
分类2:匿名函数,不需要定义函数名的函数,分为函数表达式和箭头函数
1.函数表达式定义语法: let 变量 = function(形参列表){ 函数体代码 }
代码示例
javascript
//分类2:
let sum2 = function (a, b, c) {
return a + b + c
}
document.write(`<p>${sum2(10, 20, 30)}<\P>`)//60
2. 箭头定义语法: let 变量 = (形参列表)=>{ 函数体代码 }
代码示例
javascript
let sum3 = (a, b, c) => {
return a + b + c
}
document.write(`<p>${sum2(10, 20, 30, 40)}<\P>`)//60, 40是多余,函数没有定义,所以不接收
//注意:js中函数传递参数可以与形参不匹配,但是建议要匹配才有意义。
浏览器打开,发现没有错误,并且依然弹出60,这是为什么呢?
因为在JavaScript中,函数的调用只需要名称正确即可,参数列表不管的。如上述案例,10传递给了变量a,20传递给了变量b,而30和40没有变量接受,但是不影响函数的正常调用。
注意:由于JS是弱类型语言,形参、返回值都不需要指定类型。在调用函数时,实参个数与形参个数可以不一致,但是建议一致。
五、语法四:事件监听
5.1 事件介绍
什么是事件呢?HTML事件是发生在HTML元素上的 "事情",例如:
-
按钮被点击
-
鼠标移到元素上
-
输入框失去焦点
-
按下键盘按键
-
........
而我们可以给这些事件绑定函数,当事件触发时,可以自动的完成对应的功能,这就是事件监听。
例如:对于我们所说的百度注册页面,我们给用户名输入框的失去焦点事件绑定函数,当我们用户输入完内容,在标签外点击了鼠标,对于用户名输入框来说,失去焦点,然后执行绑定的函数,函数进行用户名内容的校验等操作。
JS事件是JS非常重要的一部分,接下来我们进行事件的学习。那么我们对于JavaScript事件需要学习哪些内容呢?我们得知道有哪些常用事件,然后我们得学会如何给事件绑定函数。
所以主要围绕2点来学习:①. 事件监听、②. 常用事件
5.2 事件监听
JS事件监听的语法: 事件源.addEventListener('事件类型', 要执行的函数);
在上述的语法中包含三个要素:
- 事件源: 哪个dom元素触发了事件, 要获取dom元素
- 事件类型: 用什么方式触发, 比如: 鼠标单击 click, 鼠标经过 mouseover
- 要执行的函数 : 要做什么事 代码示例:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS事件</title>
</head>
<body>
<!-- 方式1:标签事件属性直接绑定(推荐方式) -->
<input type="button" id="btn1" value="点我一下试试" onclick="demo1()">
<input type="button" id="btn2" value="点我一下试试">
<input type="button" id="btn3" value="点我一下试试">
<script>
function demo1() {
alert("按钮1")
}
//方式2:使用js代码推荐方式绑定
//语法:语法:事件源.addEventListener('事件类型',事件触发执行的函数);
//先获取dom元素id=btn2的标签对象
let btn2 = document.querySelector("#btn2")//#开头是id选择器,根据标签属性id="选择器名字"筛选获取标签
//再绑定事件
btn2.addEventListener('click', () => {
alert('按钮2');//会运行
})
btn2.addEventListener('click', () => {
alert('按钮22');//也会运行
})
//方式3:使用早期js代码绑定(不推荐)
//语法:事件源.on事件=()=>{}
document.querySelector('#btn3').onclick = () => {
alert('按钮3')//不会运行
}
document.querySelector('#btn3').onclick = () => {
alert('按钮33')//会运行,这个事件覆盖了上面相同类型的事件
}
</script>
</body>
</html>
小结
方式1和方式2都是常用推荐的方式
方式2和方式3区别
方式2可以绑定相同事件类型多个 方式3不可以绑定相同事件类型多个,如果有多个下面的会覆盖上面的。
5.3 常见的事件
代码示例:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS-事件-常见事件</title>
</head>
<body>
<form action="" style="text-align: center;">
<input type="text" name="username" id="username">
<input type="text" name="age" id="age">
<input id="b1" type="submit" value="提交">
<input id="b2" type="button" value="单击事件">
</form>
<br><br><br>
<table width="800px" border="1" cellspacing="0" align="center">
<tr>
<th>学号</th>
<th>姓名</th>
<th>分数</th>
<th>评语</th>
</tr>
<tr align="center">
<td>001</td>
<td>张三</td>
<td>90</td>
<td>很优秀</td>
</tr>
<tr align="center" id="last">
<td>002</td>
<td>李四</td>
<td>92</td>
<td>优秀</td>
</tr>
</table>
<script>
//click: 鼠标点击事件
document.querySelector('#b2').addEventListener('click', () => {
console.log("我被点击了...");
})
//mouseenter: 鼠标移入
document.querySelector('#last').addEventListener('mouseenter', () => {
console.log("鼠标移入了...");
})
//mouseleave: 鼠标移出
document.querySelector('#last').addEventListener('mouseleave', () => {
console.log("鼠标移出了...");
})
//keydown: 某个键盘的键被按下
document.querySelector('#username').addEventListener('keydown', () => {
console.log("键盘被按下了...");
})
//keydown: 某个键盘的键被抬起
document.querySelector('#username').addEventListener('keyup', () => {
console.log("键盘被抬起了...");
})
//blur: 失去焦点事件
document.querySelector('#age').addEventListener('blur', () => {
console.log("失去焦点...");
})
//focus: 元素获得焦点
document.querySelector('#age').addEventListener('focus', () => {
console.log("获得焦点...");
})
//input: 用户输入时触发
document.querySelector('#age').addEventListener('input', () => {
console.log("用户输入时触发...");
})
//submit: 提交表单事件
document.querySelector('form').addEventListener('submit', () => {
alert("表单被提交了...");
})
</script>
</body>
</html>
六、 踩坑高频点
6.1 常见踩坑点
==与===的区别 :==会进行隐式类型转换,===严格比较类型和值,开发中优先用===;null与undefined的区别 :null表示 "空对象",undefined表示 "未定义";- 数组的
length属性 :修改length可截断数组,但增加length不会添加元素; - 函数参数的默认值:默认参数的作用域是函数自身,避免与外部变量冲突。
七、总结与延伸
7.1 核心总结
- 变量声明优先用
let/const,避免var的作用域问题; - 区分基本类型和引用类型的存储差异,掌握正确的类型判断方法;