一、基础
1.1 简洁
- JavaScript:是一种运行在浏览器中的解释型的编程语言
1.2 数据类型
1.3 字符串
// 转义字符
'i\'am ok' // i'am ok
// ASCII字符
'\x41'; // 'A'
// Unicode字符
'\u4e2d\u6587'; // '中文'
// 多行字符串(``)
`你好
世界
`
// 模板字符串
`my name is ${name}`
// 获取长度
let s = 'hello world';
s.length; // 11
s[0]; // 'h' 可以获取,但修改无效
// 大小写转换
let s = 'Hello';
s.toUpperCase(); // 'HELLO'
s.toLowerCase(); // 'hello'
// indexOf;搜索字符出现的位置
let s = 'hello';
s.indexOf('el'); // 1
s.indexOf('s'); // -1 找不到返回-1
// subString;字符串截取(前闭后开)
let s = 'hello world';
s.subString(0, 5); // hello
s.subString(6); // world
1.4 数组
- 常用方法
- 修改原数组:push/pop,shift/unshift,splice,reverse/sort,fill,copyWithin
- 不修改原数组:find,includes,slice,reduce等
- 相关示例
// (1)fill - 数组填充 - fill(value, start, end)
let arr = new Array(5);
arr.fill(0);
console.log(arr); // [0, 0, 0, 0, 0]
arr.fill(2, 0, 2); // [2, 2, 0, 0, 0]
// (2)copyWithin - 复制并覆盖 - copyWithin(target, start, end)
let arr = ['A', 'B', 'C', 'D', 'E'];
arr.copyWithin(0, 3, 5);
console.log(arr); // ['D', 'E', 'C', 'D', 'E']
// (3)splice - 删除并截取 第一个参数:开始索引,第二个参数:删除个数,后面参数:添加的内容
let arr = [1, 2, 3, 4, 5];
arr.splice(1, 2, 6) // [2, 3]
console.log(arr) // [1, 6, 4, 5]
// (4)slice - 数组截取,两个参数均为索引(前闭后开)
let arr = [1, 2, 3, 4, 5];
arr.slice(1, 2) // [2]
arr.slice(1) // [2, 3, 4, 5]
let arr1 = [1, 2, 3, 4, 5, 6];
let copeArr = arr1.slice(); // [1, 2, 3, 4, 5, 6]
// (5)join/split
let arr = [1, 2, 3];
arr.join('@'); // '1@2@3'
let str = '1-34';
str.split('-'); // ['1', '34']
// (6)flat
let arr = [[1, 2], [3, 4]];
console.log(arr.flat(Infinity)) // [1, 2, 3, 4];
console.log(arr.toString().split(',').map(Number)) // [1, 2, 3, 4];
// (7)reduce/new Set
let arr = [1, 2, 3, 4, 2, 2, 1];
let newArr = arr.reduce((acc, cur) => {
if(!acc.includes(cur)) {
acc.push(cur)
}
return acc;
}, []);
console.log(newArr); // [1, 2, 3, 4];
console.log(Array.from(new Set(arr))); // [1, 2, 3, 4];
1.5 对象
let person = {
name: '张三',
age: 20,
score: 90
}
// 获取
console.log(person.name)
console.log(person[name])
// 新增
person.hobby = '睡觉';
// 修改
person.score = 99;
// 删除
delete person.age;
- in操作符 - 判断对象是否拥有某个属性(属性也可能是继承获取的)
let person = {
name: '张三',
age: 22
}
'name' in person // true
'toString' in person // true - person继承Object
person.hasOwnProperty('toString') // false - 判断是否对象独有
1.6 条件判断和循环
if(){...}else{...}
if()(...)else if(){...}else{...}
// (1)for循环(用于遍历数组/类数组)
for (let i=0; i<length; i++){}
// (2)for in (用于遍历数组/对象(会遍历对象本身及原型链上**可枚举的属性**))
let parent = { hobby: '睡觉' };
let child = Object.create(parent);
child.name = '张三';
for(let key in child) {
console.log(key) // name hobby
}
// (3)只遍历自身属性 - hasOwenProperty
for (let key in child) {
if (child.hasOwnProperty(key)) {
console.log(key); // 只输出自身属性
}
}
Object.keys(child).forEach(key => {
console.log(key);
});
Object.getOwnPropertyNames(child).forEach(key => {
console.log(key);
});
1.7 Map/Set及iteable(遍历可迭代对象的值 - 按顺序访问的元素的对象)
- Map:键值对的集合,具有极快的查找速度(和对象区别)
- 特点
- 属性名可以是任意类型
- 有顺序,保持键值对插入顺序
- 更易读的API:set()、get()、has()、delete()
- 示例
let map = new Map();
map.set('name', '张三');
map.set('age', 25);
console.log(map.get('name')); // '张三'
console.log(map.has('name')); // true
// 二维数组写法
let m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
- Set:拥有唯一值的集合(操作高效,复杂度低)
- 特点
- 自动去重
- 查找高效(复杂度是 O(1))
- 插入和删除高效(复杂度是 O(1))
- 示例
// 去重
let set = new Set([1, 2, 2, 3, 4, 4]);
console.log([...set]); // [1, 2, 3, 4]
// 查找
let set = new Set([1, 2, 3, 4]);
console.log(set.has(3)); // 输出: true
console.log(set.has(5)); // 输出: false
- iterable(可迭代对象能够被统一遍历)
- for…of 可以遍历 Iterable 类型的数据
- 可遍历:数组/字符串/Set/Map/arguments
- 不可遍历:普通对象/null/undefined/Boolean/Number
- 相关示例
// 遍历数组
let arr = [1, 2, 3]
for(let item of arr) {
console.log(item) // 1 2 3
}
// 遍历Set
let set = new Set([1, 2, 3, 4]);
for(let item of set) {
console.log(item) // 1 2 3 4
}
// 遍历Map
let map = new Map([['a', 1], ['b', 2]]);
for (let [key, value] of map) {
console.log(key, value); // 输出 'a' 1, 'b' 2
}
- for of 和 for in区别
- for of 遍历可迭代对象的值(数组/字符串/Set/Map/arguments)
- for in 遍历不可迭代对象的属性名(以及继承的属性名)
二、函数
2.1 函数的重要概念
- 定义函数的方式
- 函数声明:function add(x,y) { return x + y }
- 匿名函数:let add = function(x,y){ return x + y }
- arguments:(传入的参数列表-类数组)
- rest参数:剩余参数function foo(x, y, …rest) {}
- return语句:终止函数执行,并返回一个值
2.2 函数的类型
2.3 闭包
- 定义:函数里面嵌套函数,并把内部函数返回出来,内部函数可以访问外部变量
- 作用:
- 私有变量:防止外部污染变量
- 做状态缓存:节流/防抖
- 示例:
// 定时器介绍
// setTimeout(callback, interval, ...args) // args可选参数传递给回调函数
// setInterval(callback, interval, ...args)// args可选参数传递给回调函数
// 防抖 - 单位时间内多次操作只执行最后一次
function debounce(func, delay = 300) {
let timer = null;
return function(...args) { // 返回普通函数
clearTimeout(timer);
timer = setTimeout(() => { // 箭头函数使this指向继承定义时的this
func.apply(this, args)
})
}
}
let obj = {
objName: 'zhangsan',
handler: debounce(function(){
console.log(this.objName)
}, 300)
}
obj.handler();
// 节流 = 单位时间内多次执行间隔执行
function throller(func, interval = 300){
let lastTime = 0;
return function(...args) { // 返回普通函数
const now = Date.now();
if(now - lastTime > interval){
func.apply(this, args);
lastTime = now;
}
}
}
2.4 this指向及改变this指向的方法
- 分类
- (1)普通函数:谁调用指向谁,正常指向undefined/window
- (2)箭头函数:继承定义时的this
- (3)定时器:默认指向全局对象,除非使用箭头函数或显示绑定
- (4)dom事件this:触发事件的DOM元素
- 代码示例
// 1.普通函数this指向(谁调用指向谁)
function fn() {
console.log(this);
}
fn(); // window/undefined
fn.apply({ name: 'zhangsan' }); // {name: 'zhangsan'}
// 2.箭头函数
const person = {
personName: 'zhangsan',
arrow: () => console.log(this.personName),
regular: function() {
return console.log(this.personName)
}
};
person.arrow() // undefined (对象字面量不创建作用域,箭头函数仍在全局定义)
person.regular() // 'zhangsan'(继承regular的this)
let regularFn = person.regular;
regularFn(); // undefined (隐式丢失,默认绑定)
// 3.定时器
setTimeout(function() {
console.log(this); // window/undefined
}, 100);
// 箭头函数
const obj = {
name: 'obj',
start() {
setTimeout(() => {
console.log(this.name); // 'obj' // 继承于定义时的外层作用域this
}, 100);
}
};
obj.start();
// 显示绑定
const obj = { name: 'obj' };
setTimeout(function() {
console.log(this.name); // 'obj'
}.bind(obj), 100);
// 4.dom事件中的this
document.getElementById('btn').addEventListener('click', function() {
console.log(this); // dom元素 btn按钮
});
- 改变this指向的方法
- call:参数一个一个传,返回一个函数,需手动出发
- apply:参数以数组形式传,立即执行
- bind:参数一个一个传,立即执行
function greet(name, age) {
console.log(`hello ${name}, i am ${this.name}` )
}
const person = { name: 'zhangsan' };
greet.apply(person, ['lisi']);
2.5 递归函数(函数自我调用)
// 阶乘
function factorial(n) {
if(n <= 1) return 1; // 终止条件
return n * factorial(n-1); // 自我调用+问题缩小
}
三、标准对象
3.1 Date
let now = new Date();
console.log(now);
console.log(now.getFullYear());
console.log(now.getMonth()); // 月份从0开始的所以需要加1
console.log(now.getDate());
console.log(now.getHours());
console.log(now.getMinutes());
console.log(now.getSeconds());
3.2 RegExp
3.3 JSON
- 介绍:一种轻量级的数据交换格式,用于不同系统间传输
- 核心方法(不支持函数,日期)
- JSON.stringify:对象 -> JSON字符串
- JSON.parse:JSON字符串-> 对象
四、面向对象编程
4.1 创建对象方式
- 创建对象
- (1)字面量:let obj = {}
- (2)new Object:let obj = new Object()等价于字面量
- (3)构造函数:let person = new Person()
- (4)Object.create:创建一个新对象,并把它连接到指定原型
- 合并对象:Object.assign
var test = Object.assign(target,...sources) // test == target
- 示例
function Person(name) {
this.name = name;
this.greet = function() { console.log(this.name) }
}
var obj = new Person('张三');
// Object.create
var obj = { name:'张三', age: 20 };
var obj1 = Object.create(obj);
obj1.age = 18;
console.log(obj1.age) // 18
console.log(obj.age) // 20
4.2 原型和原型链
- 概念解读
- 构造函数:创建对象的函数
- 原型:构造函数的一个属性,存放着实例共享的属性/方法
- 原型链:对象继承属性的链条
- 示例
// 注意:原型对象的constructor默认指向构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
let p = new Person('张三', 22);
console.log(Person.prototype.constructor === Person); // true
console.log(p.constructor === Person.prototype.constructor); // true
console.log(p.constructor === Person); // true
console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
4.3 继承方式 - 代码复用和逻辑分层
- 作用:
- 复用性:子类可以复用父类的属性和方法
- 扩展性:子类可以在父类基础上扩展
- 多态性:子类可重写父类的方法
- 层次清晰
- 方式
- 1.原型链继承 - 适合方法继承(修改引用属性影响所有实例)
- 2.构造函数继承 - 适合属性继承(无法复用方法)
- 3.寄生组合式继承 - (构造函数提供属性+原型链提供方法+Object.create做桥梁)
- 4.class继承 - 底层基于寄生组合式继承
- 示例
// 1.原型链继承 - 引用数据类型共享
function Animal() { this.food = ["肉","菜"] }
function Dog() {}
Dog.prototype = new Animal(); // 核心:把父类实例作为子类原型
const dog1 = new Dog();
dog1.food.push("狗粮"); // 会污染所有实例
// 2.构造函数继承
function Animal(){
this.name = '动物';
this.say = function() {
return this.name;
}
}
function Dog(){
Animal.call(this) // 核心:在子类中调用父类构造函数
}
let dog1 = new Dog();
let dog2 = new Dog();
console.log(dog1.name === dog2.name); // true
console.log(dog1.say === dog2.say); // false - 方法不共享
// 3.寄生组合式继承
function Animal(name){
this.name = name;
this.color = ['black']
}
Animal.prototype.say = function(){
return this.name;
}
function Dog(name, age){
Animal.call(this, name); // 继承实例属性
this.age = age;
};
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复构造函数
let dog1 = new Dog('小狗', 18);
let dog2 = new Dog('大狗', 20);
console.log(dog1);
console.log(dog2);
// 圣杯模式
let inherit = (function(){
function F(){}; // 私有化变量,立即执行函数,执行后即销毁
return function(Target, Origin){
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
}
}());
// 使用Object.create简化
let inherit = (function() {
return function(Target, Origin) {
Target.prototype = Object.create(Origin.prototype); // 使用 Object.create 建立原型链
Target.prototype.constructor = Target; // 修复构造函数指向
Target.prototype.uber = Origin.prototype; // 保留对父类原型的引用
}
}());
// 4.class继承
class Animal {
// 构造函数
constructor(name){
this.name = name;
}
// 原型方法
say(){
return this.name
}
}
class Dog extends Animal {
constructor(name, age) {
super(name); // 相当于调用Animal.call(this, name)
this.age = age;
}
// 独有
bark() {
console.log("汪汪汪!");
}
}
let dog = new Dog('小狗', 4);
dog.bark(); // 汪汪汪!
let animal = new Animal('大狗');
animal.bark(); // 报错
五、浏览器
5.1 浏览器对象
- window:浏览器窗口 - innerWidth/innerHeight/outWidth/outHeight
- navigator:浏览器信息 - appName/appVersion/language/platform/userAgent
- screen:屏幕信息 - widht/height/colorDepth
- location:当前页面url信息 - protocol/host/port/pathname/search/hash
- document:当前页面 - cookie/title
- history:浏览器历史记录 - back/forward/pushState
5.2 Dom操作
- 查询:querySelector/querySelectorAll
- 获取单个:document.querySelector(‘#submit-btn’)
- 获取多个:document.querySelectorAll(‘.list-item’);
- 更新:innerHTML/innerText/textContent
- 插入:appendChild/insertBefore
- 删除:removeChild
- 创建:createElement
- 属性操作:setAttribute/getAttribute
- 类操作:classList.add()/.remove()
- 事件处理:addEventListener
- 样式操作:style
六、异步操作
6.0 异步编程的解决方案
- 回调函数:基础但容易嵌套
- Promise:链式调用解决嵌套问题
- async/await:同步写法写异步代码,基于 Promise
- 其他方案
- 事件监听:button.addEventListener(‘click’, callback)
- 发布订阅:eventBus.on(‘event’, handler)
6.1 Promise
- 定义:异步编程的解决方案
- 三种状态:pending/fulfilled/rejected
- 静态方法:
- 中断promise链的方式
- 返回未解决的Promise:return new Promise(() => {})
- 使用状态变量 + return 跳出函数
- AbortController
- 基础使用
// 1.简单示例
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(666)
}, 2000)
})
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}).finally(() => {
console.log('结束')
})
// 2.链式调用(必须返回Promise才能保持顺序,否则后续操作会立即执行)
// 示例:用户登录 → 获取用户信息 → 获取用户订单
login(email, password)
.then(loginData => {
// 处理登录结果
return getUserInfo(loginData.token);
})
.then(userInfo => {
// 处理用户信息
return getUserOrders(userInfo.id);
})
.then(orders => {
// 处理订单数据
console.log('完整数据获取成功!');
})
.catch(error => {
// 统一错误处理
console.error('流程出错:', error);
});
// 3.手写Promise.all
Promise.myAll = function(promises) {
return new Promise((resolve, reject) => {
let results = [];
let completed = 0;
promises.forEach((p, index) => {
p.then(res => {
results[index] = res;
completed++;
if(completed === promises.length) {
resolve(results)
}
}).catch(reject)
});
})
}
6.2 async/await
- 定义:基于Promise的语法糖,同步写法解决异步问题
- 注意:
- async:标记函数为异步函数,使其返回promis对象
- await:暂停异步函数执行,等待Promise解决
- 错误处理:使用try/catch替代Promise的.catch()
- 示例
// 1.基本介绍
async function getData() {
return 666 // 等同于Promise.resolve(666)
}
getData().then(res=> {
console.log(res)
})
// 2.把异步操作写成同步执行的形式
async function fetchUserData() {
try {
// 用户登录
const loginData = await login(email, password);
// 获取用户信息
const userInfo = await getUserInfo(loginData.token);
// 获取用户订单
const orders = await getUserOrders(userInfo.id);
// 处理订单数据
console.log('完整数据获取成功!', orders);
return orders;
} catch (error) {
// 统一错误处理
console.error('流程出错:', error);
throw error; // 可以选择继续抛出或处理
}
}
七、AJAX及其他请求方式
7.1 AJAX介绍
- 简介:让网页与服务器异步通信、无需刷新页面的技术
- 示例:
// 1. 创建 XMLHttpRequest 对象
const xhr = new XMLHttpRequest();
// 2. 配置请求(GET 请求,异步模式)
xhr.open('GET', 'https://api.example.com/data', true);
// 3. 定义响应处理函数
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) { // 请求完成
if (xhr.status === 200) { // 成功响应
const data = JSON.parse(xhr.responseText);
document.getElementById("result").innerHTML = data.message;
} else {
console.error('请求失败:', xhr.status);
}
}
};
// 4. 发送请求
xhr.send();
7.2 常见的请求方式
- 原生AJAX
- Fetch API
- axios
- 支持PromiseAPI
- 拦截请求和响应
- 取消请求
- 超时处理
- jQuery.ajax()
八、重点总结
8.1 深浅拷贝
- 浅拷贝:只拷贝一层,嵌套引用类型仍然共享
- 常见方法
- 对象:Object.assign/…
- 数组:slice/concat/…
- 深拷贝:拷贝所有层级,避免引用数据共享
- 常见方式
- JSON.stringify/.parse:无法处理函数/RegExp/Date/Set/Map等特殊对象
- Lodash库的_.cloneDeep():提供更为完备的深拷贝功能
- 递归拷贝
- 1.基本数据类型判断 - 终止条件
- 2.处理特殊对象
- 3.递归拷贝(创建对应的数组/对象)
- 4.返回结果
- 手写深拷贝
// 1.初始版本
function deepClone(origin, target) {
// 1.处理基本数据类型 - 终止条件
if (origin == null || typeof origin != 'object') return origin;
// 2.处理特殊对象(正则,日期,函数,Set,Map等)
if (origin instanceof RegExp) { // 正则
return new RegExp(origin.source, origin.flags);
}
if (origin instanceof Date) { // 日期
return new Date(origin.getTime());
}
if (typeof origin === 'function') { // 函数
return origin;
}
// 3.判断是数组还是对象,并创建对应的数组或对象
if(!target) {
target = Array.isArray(origin) ? [] : {};
}
// 4.递归遍历拷贝所有属性
for (let key in origin) {
if(origin.hasOwnProperty(key)) {
target[key] = deepClone(origin[key], target[key])
}
}
return target;
}
// Map形式