JavaScript 完整详细教程
适合零基础入门,由浅入深,包含语法、DOM、ES6+、异步、实战要点。
JS 是一门弱类型、动态、解释型语言,既可在浏览器运行,也可在 Node.js 后端运行。
一、JS 三种引入方式
1. 行内(不推荐)
<button onclick="alert('Hello')">点我</button>
2. 内嵌 script
<script>
// JS代码写这里
console.log("内嵌JS");
</script>
3. 外部文件(推荐,后缀 .js)
<script src="main.js"></script>
注意:script 默认阻塞页面解析,可加
defer/async
defer:下载脚本,等HTML解析完再按顺序执行async:下载完立刻执行,顺序不确定
<script src="main.js" defer></script>
二、基础语法
1. 输出
console.log("控制台打印");
alert("弹窗");
document.write("页面输出");
2. 变量声明:var / let / const
| 关键字 | 作用域 | 是否可修改 | 提升 |
|---|---|---|---|
| var | 函数作用域 | 可重新赋值、重复声明 | 变量提升 |
| let | 块级作用域 {} | 可赋值,不可重复声明 | 存在提升,暂时性死区 |
| const | 块级作用域 | 常量,不可重新赋值;对象属性可改 | 存在提升,暂时性死区 |
✅ 推荐:优先 const,需要修改用 let,尽量不用 var
let name = "张三";
const age = 18;
name = "李四";
// age = 20; // 报错,const不能重新赋值
块级作用域:
if / for / {}内部,外面访问不到 let/const
3. 数据类型
JS 分为 原始类型(基本类型) 和 引用类型
原始类型(值拷贝)
string字符串:"abc"/'abc'/\模板字符串``number数字:整数、小数、NaN、Infinityboolean布尔:true/falsenull:空值(typeof null === 'object' 历史bug)undefined:未定义,变量声明没赋值symbolES6:唯一标识bigintES2020:超大整数
引用类型(地址拷贝)
object 对象、array 数组、function 函数、Date、RegExp 等
let obj1 = {name:"A"}
let obj2 = obj1; // 复制地址,不是复制对象
obj2.name = "B";
console.log(obj1.name); // B
判断类型
typeof "abc" // 'string'
typeof 123 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof null // 'object' ❗
typeof {} // 'object'
typeof [] // 'object'
typeof function(){} // 'function'
// 判断数组
Array.isArray([]) // true
// 判断null
myVal === null
4. 运算符
算术运算符
+ - * / % ++ --
+遇到字符串会变成字符串拼接
1 + "2" // "12"
赋值运算符
= += -= *= /= %=
比较运算符
> < >= <= == === != !==
==:宽松相等,自动类型转换===:严格相等,值和类型都要一样,推荐使用
1 == "1" // true
1 === "1" // false
逻辑运算符
&& 与,|| 或,! 非
短路特性:
a && b:a为真才执行b;a假直接返回a
a || b:a为真直接返回a;a假返回b
三元运算符
let res = age >=18 ? "成年":"未成年";
三、流程控制
1. if else
let score = 80;
if(score >=90){
console.log("优秀");
}else if(score >=60){
console.log("及格");
}else{
console.log("不及格");
}
2. switch
let day = 2;
switch(day){
case 1: console.log("周一");break;
case 2: console.log("周二");break;
default: console.log("其他");
}
switch 使用
===判断,记得加break,否则穿透
3. for 循环
for(let i=0;i<5;i++){
console.log(i);
}
4. while / do while
let i=0;
while(i<3){
console.log(i);
i++;
}
// do while 先执行一次再判断
let j=0;
do{
console.log(j);
j++;
}while(j<3)
5. for...in 遍历对象(不推荐遍历数组)
let obj = {name:"小明",age:16}
for(let key in obj){
console.log(key, obj[key])
}
6. for...of ES6 遍历可迭代对象:数组、字符串、Map、Set
let arr = [10,20,30]
for(let item of arr){
console.log(item)
}
关键字
break:跳出循环;continue:结束本次循环,进入下一次
四、函数 Function
1. 函数声明
function sayHi(name){
console.log("Hi,"+name);
return "ok"; // return 返回值,没有return默认返回undefined
}
sayHi("张三");
2. 函数表达式
const say = function(){
console.log("函数表达式");
}
say();
3. ES6 箭头函数(重点)
const add = (a,b) => a+b;
// 多行需要{} + return
const test = (x)=>{
let res = x*2;
return res;
}
⚠️ 箭头函数特点
- 没有自己的this,继承外层作用域this
- 不能用作构造函数 new
- 没有 arguments 对象,可以用剩余参数
...args
4. 参数
- 默认参数
function fn(a=10){
console.log(a);
}
fn(); //10
- 剩余参数
...args,必须放最后
function sum(a,...args){
console.log(a, args);
}
sum(1,2,3,4);
5. 作用域 & 闭包
- 全局作用域、函数作用域、块级作用域
{}>
闭包:函数A内部返回函数B,B可以访问A的变量,即使A执行完毕
function outer(){
let num = 10;
return function inner(){
console.log(num);
}
}
const f = outer();
f(); // 10
闭包用途:保存私有变量;缺点:变量常驻内存,容易内存泄漏
6. this 指向(高频考点)
- 普通函数调用:
this -> window(浏览器)/ global(node),严格模式下undefined - 对象方法调用:
this指向调用方法的对象 - new 构造函数:
this指向新实例 - call / apply / bind:手动绑定this
- 箭头函数:this 定义时外层的this,和调用无关
let obj = {
name:"test",
fn:function(){
console.log(this.name);
}
}
obj.fn(); // this是obj
call / apply / bind
function hello(){console.log(this.name)}
let user = {name:"老王"}
hello.call(user);
hello.apply(user);
const boundHello = hello.bind(user);
boundHello();
call( thisArg, 参数1,参数2 )
apply( thisArg, [参数数组] )
bind 返回新函数,不会立刻执行
五、数组 Array
let arr = [1,2,3];
常用方法(改变原数组)
push() 末尾添加;pop() 删除末尾;unshift() 头部添加;shift() 删除头部
splice(start, deleteCount, 新增元素):增删改
reverse() 反转;sort() 排序(默认按字符排序!数字排序需要回调)
let nums = [2,1,10];
nums.sort((a,b)=>a-b); //升序
不改变原数组
slice(start,end) 截取;concat() 拼接;join() 转字符串;indexOf / includes 判断是否存在
ES6 数组迭代方法(非常常用)
const list = [1,2,3,4];
// map:返回新数组,对每个元素加工
const res1 = list.map(item=>item*2);
// filter:过滤,返回满足条件元素组成新数组
const res2 = list.filter(item=>item>2);
// reduce:累加器
const total = list.reduce((sum,item)=>sum+item,0);
// forEach:遍历,无返回值
list.forEach(item=>console.log(item));
// some:只要一个true,返回true
// every:全部true才返回true
六、对象 Object
const user = {
name:"小李",
age:20,
say:function(){
console.log(this.name)
}
}
// 访问属性
console.log(user.name);
console.log(user["name"]); // 方括号支持变量
// 添加/修改属性
user.gender = "男";
// 删除
delete user.age;
ES6 对象简写
let name = "Tom";
const obj = {
name, // name:name简写
hello(){ // 方法简写
console.log("hi")
}
}
对象常用API
Object.keys(obj) // 获取所有key数组
Object.values(obj) // 获取所有value数组
Object.entries(obj) // [key,value]数组
Object.assign(target, source) // 对象浅拷贝合并
浅拷贝:只拷贝第一层;嵌套对象仍然是引用。深拷贝需要递归或者结构化克隆
七、ES6+ 核心语法
1. 解构赋值
// 对象解构
const {name,age} = user;
// 数组解构
const [a,b] = [10,20];
// 剩余
const [x,...rest] = [1,2,3]
2. 模板字符串 \`
let n = "小明";
let str = `名字:${n}`; //支持换行、变量嵌入
3. 展开运算符 ...
const arr1 = [1,2];
const arr2 = [...arr1,3,4]; //复制数组
const o1 = {a:1};
const o2 = {...o1, b:2}; //对象浅拷贝
4. Set / Map
- Set:集合,元素唯一
const s = new Set([1,2,2,3]);
console.log([...s]); //去重数组
- Map:键可以是任意类型(Object key只能是字符串/Symbol)
const m = new Map();
m.set("name","test");
m.get("name");
八、DOM 文档对象模型(浏览器JS)
DOM:把HTML文档转成JS对象树,可以操作页面元素。
获取元素
// id
document.getElementById("box");
// class
document.getElementsByClassName("item");
// 标签
document.getElementsByTagName("div");
// css选择器(推荐)
document.querySelector(".box"); //匹配第一个
document.querySelectorAll(".item"); //返回NodeList集合
操作内容
ele.innerText //纯文本
ele.innerHTML // 支持html标签
ele.value //表单input的值
操作属性
ele.setAttribute("data-id",100);
ele.getAttribute("data-id");
ele.removeAttribute("data-id");
// 原生属性直接 .
ele.src = "xxx.jpg";
操作样式
ele.style.backgroundColor = "red"; //行内样式,驼峰命名
// 类名控制(推荐,不要直接写大量style)
ele.classList.add("active");
ele.classList.remove("active");
ele.classList.toggle("active"); //有就删,没有就加
ele.classList.contains("active");
节点操作
const newDiv = document.createElement("div");
parent.appendChild(newDiv); //末尾追加
parent.insertBefore(newDiv, oldNode);
parent.removeChild(child);
ele.cloneNode(true); //true深度克隆,包含子节点
事件
绑定事件
const btn = document.querySelector("button");
btn.addEventListener("click", function(e){
console.log("点击");
e.target //触发事件元素
e.preventDefault(); //阻止默认行为(a跳转、表单提交)
e.stopPropagation(); //阻止事件冒泡
})
事件流:捕获 -> 目标 -> 冒泡。addEventListener第三个参数true为捕获,默认false冒泡。
常用事件:click、input、change、mouseover、mouseout、keydown、keyup、load
九、BOM 浏览器对象模型
BOM:浏览器窗口相关API
window.alert()
window.location.href = "[https://xxx.com](https://xxx.com)"; //跳转页面
location.reload(); //刷新
console.log(navigator.userAgent); //浏览器信息
history.back(); history.forward(); //历史记录
// 定时器
const timer = setTimeout(()=>{
console.log("延迟1秒");
},1000);
clearTimeout(timer);
// 循环定时器
const inter = setInterval(()=>{
console.log("每隔1秒");
},1000);
clearInterval(inter);
JS 是单线程,定时器不是精确到时间,放入任务队列等待主线程空闲才执行
十、异步 JavaScript(重点难点)
JS 单线程,同步代码阻塞,异步用来处理请求、定时器、文件读写。
1. 回调函数(回调地狱)
// 回调嵌套过多,可读性差
getData(function(a){
getMore(a, function(b){
})
})
2. Promise ES6
Promise 三种状态:pending等待、fulfilled成功、rejected失败。状态一旦改变不可回退。
function fetchData(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("数据成功");
// reject("失败");
},1000)
})
}
fetchData()
.then(res=>{console.log(res)})
.catch(err=>{console.log(err)})
.finally(()=>{console.log("不管成功失败都会执行")})
链式调用:.then 返回新Promise
Promise.all([p1,p2]):全部成功才成功,一个失败直接失败
Promise.race([p1,p2]):谁先完成返回谁
3. async / await ES2017(Promise语法糖)
async 修饰函数,返回Promise;await 只能在async函数内部,等待Promise完成
async function load(){
try{
const res = await fetchData();
console.log(res);
}catch(err){
console.log(err);
}
}
load();
宏任务、微任务(事件循环 Event Loop)
执行顺序:
- 先执行同步代码
- 执行所有微任务(Promise.then、queueMicrotask、MutationObserver)
- 再取一个宏任务执行(setTimeout、setInterval、AJAX、DOM渲染),然后清空微任务,循环往复。
十一、JSON & 网络请求
// 对象转JSON字符串
const jsonStr = JSON.stringify(user);
// JSON字符串转回对象
const obj = JSON.parse(jsonStr);
fetch(浏览器原生网络请求)
async function getApi(){
const res = await fetch("https://xxx/api");
const data = await res.json();
console.log(data);
}
旧方案:XMLHttpRequest
十二、错误处理
try{
// 可能出错代码
let a = b +1;
}catch(err){
console.log("捕获错误:",err.message);
}finally{
console.log("一定会执行");
}
手动抛出错误:throw new Error("自定义错误")
十三、面向对象 OOP ES6 Class
class Person{
constructor(name){ //构造函数,new的时候自动执行
this.name = name;
}
say(){
console.log(this.name);
}
}
const p1 = new Person("张三");
p1.say();
// 继承 extends
class Student extends Person{
constructor(name,score){
super(name); //调用父类构造,必须写在前面
this.score = score;
}
}
十四、常见坑 & 最佳实践
- 不要用
==,一律=== - 全局变量尽量少,污染命名空间
- 定时器用完记得清除,防止内存泄漏
- 闭包慎用,避免内存无法释放
- 数组sort默认字符排序,数字一定要传回调
this是动态的,箭头函数不要滥用- DOM操作尽量少,频繁操作会重绘重排,性能差
- const优先,let次之,var废弃
十五、学习路线建议
- 基础语法、变量、循环、函数(敲代码练习)
- 数组对象、ES6语法
- DOM操作,写小案例:计数器、待办Todo、轮播
- 异步:Promise、async await、事件循环
- 网络请求,接口联调
- 学习框架:Vue / React(JS基础扎实后)
- Node.js,后端JS
如果你想要,我可以继续提供:
- 配套可直接复制练习的练习题
- 简易实战项目:Todo清单
- 单独一份 JS面试核心精简版
- 分章节Markdown文档版本方便导入Obsidian
你想要哪一种?