简短介绍
JavaScript
是一门用来与网页交互的脚本语言,包含以下三个组成部分。
1、ECMAScript:由 ECMA-262 定义并提供核心功能。
2、文档对象模型(DOM):提供与网页内容交互的方法和接口。
3、浏览器对象模型(BOM):提供与浏览器交互的方法和接口。
JavaScript 的这三个部分得到了五大 Web
浏览器(IE、Firefox、Chrome、Safari 和 Opera)
不同程度
的支持。所有浏览器基本上对 ES5(ECMAScript 5)
提供了完善的支持,而对 ES6(ECMAScript 6)
和ES7(ECMAScript 7)
的支持度也在不断提升。这些浏览器对 DOM 的支持各不相同,但对 Level 3 的支
持日益趋于规范。HTML5
中收录的 BOM
会因浏览器而异,不过开发者仍然可以假定存在很大一部分
公共特性。学习是一项长期的任务,让我来一起成长吧!
基础
JavaScript的数据类型有哪些?引用类型有哪些?
JavaScript 中的数据类型可以分为两类:基本类型(原始类型)
和 引用类型
。
原始类型:Number, String, Boolean, null,undefined, Symbol, BigInt。
引用类型:Object, Array, Function, Date, RegExp, Map, Set
如何判断JavaScript的数据类型
- 使用
typeof
运算符console.log(typeof BigInt(123)); // "bigint" console.log(typeof {}); // "object" console.log(typeof []); // "object" (数组也是对象)
- 使用
instanceof
运算符console.log([] instanceof Array); // true console.log({} instanceof Object); // true console.log(function(){} instanceof Function); // true
- 使用
Object.prototype.toString.call()
方法console.log(Object.prototype.toString.call([])); // "[object Array]" console.log(Object.prototype.toString.call({})); // "[object Object]" console.log(Object.prototype.toString.call(function(){})); // "[object Function]"
- 使用
Array.isArray()
方法console.log(Array.isArray([])); // true console.log(Array.isArray({})); // false
- 原始类型可以使用 typeof 进行判断,但对于 null 和数组的判断不够准确。
- 引用类型可以使用 instanceof 和 Array.isArray() 进行判断,或者通过 Object.prototype.toString.call() 来进行更准确的判断。
- 结合不同的方法,可以实现更灵活和准确的数据类型判断。
怎么判断两个对象相等?如何判断空对象?
比较对象的属性
要判断两个对象的属性是否相等,可以遍历它们的属性,逐一比较值。
function isEqual(obj1, obj2) { // 如果两个对象的引用相同,则直接返回 true if (obj1 === obj2) return true; // 如果两个对象其中一个不是对象,或者它们的类型不同,返回 false if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 === null || obj2 === null) { return false; } // 获取两个对象的属性名数组 const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); // 如果属性数量不同,返回 false if (keys1.length !== keys2.length) { return false; } // 遍历属性并递归比较 for (let key of keys1) { // 如果 obj2 中没有 obj1 的某个属性,或者属性值不相等,返回 false if (!keys2.includes(key) || !isEqual(obj1[key], obj2[key])) { return false; } } return true; } // 示例 const objA = { a: 1, b: { c: 2 } }; const objB = { a: 1, b: { c: 2 } }; console.log(isEqual(objA, objB)); // true
2. `判断空对象`
- 要判断一个对象是否为空对象(即没有任何可枚举的属性),可以使用 `Object.keys()` 或 `Object.getOwnPropertyNames()` 来获取对象的属性,然后判断属性数组是否为空。
```javascript
// 遍历keys
function isEmptyObject(obj) {
// 判断是否为对象,且是否为空
return obj && Object.keys(obj).length === 0 && obj.constructor === Object;
}
// 示例
console.log(isEmptyObject({})); // true
console.log(isEmptyObject({ a: 1 })); // false
0.1+0.2为什么不等于0.3?
原理解释
- 在二进制表示中,像 0.1 和 0.2 这样的十进制小数不能被精确表示。它们在二进制中是无限循环的数值,因此被截断为近似值。这种近似值在计算时会引发精度误差。
举个例子:
- 0.1 在二进制中的表示是:0.0001100110011001100110011001100110011001100110011001101(无限循环)
- 0.2 在二进制中的表示是:0.001100110011001100110011001100110011001100110011001101(无限循环)
当你进行 0.1 + 0.2 的计算时,JavaScript 将近似值相加,结果是 0.30000000000000004,这和你期望的 0.3 不完全相等。
console.log(0.1 + 0.2); // 输出 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // 输出 false
如何解决
为了避免这种精度问题,通常会使用四舍五入的方法来修正计算结果。例如,可以使用 toFixed()
方法或 Number.EPSILON
来比较两个浮点数:
let sum = (0.1 + 0.2).toFixed(1); // 四舍五入保留 1 位小数
console.log(sum === '0.3'); // 输出 true
// 使用 Number.EPSILON (允许小误差)
function areEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(areEqual(0.1 + 0.2, 0.3)); // 输出 true
如何判断一个对象是否为空对象?
方法一:使用for...in
循环
// 遍历keys
function isEmptyObject(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
}
function isEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(isEqual(0.1 + 0.2, 0.3)); // 输出 true
强制类型转换、隐式类型转换分别是什么?场景说明
- 强制类型转换,也称为显式类型转换,是通过开发者手动调用函数或运算符来将一个值转换为所需的类型。常见的类型转换包括将值转换为字符串、数字或布尔值。
常见的强制类型转换方法:
- 转换为字符串:使用 String() 函数或调用 toString() 方法
- 转换为数字:使用 Number()、parseInt()、parseFloat() 函数
- 转换为布尔值:使用 Boolean() 函数
// 将数字转换为字符串 (互转) let num = 123; let str = String(num); // str = "123" console.log(typeof str); // 输出 "string" // 将值转换为布尔值 let value = 0; let isTrue = Boolean(value); // isTrue = false, 因为 0 转换为 false console.log(isTrue); // 输出 false
2.隐式类型转换,也称为自动类型转换,是 JavaScript 在遇到不同类型的值时自动进行的类型转换。它通常发生在算术运算、比较操作等场景下。
常见的隐式类型转换:
- 数字与字符串混合运算时,JavaScript 会将数字转换为字符串进行拼接
- 使用双等号 == 进行比较时,JavaScript 会自动进行类型转换以比较值
- 在条件语句中,非布尔类型会被自动转换为布尔类型
// 字符串与数字相加
let num = 10;
let str = "5";
let result = num + str; // "10" + "5" -> "105" (字符串拼接)
console.log(result); // 输出 "105"
// 双等号比较
let num = 1;
let bool = true;
console.log(num == bool); // 输出 true,因为 `true` 被转换为 1
// 条件语句中的隐式转换
let value = "";
if (value) {
console.log("True");
} else {
console.log("False"); // 输出 "False",因为空字符串被转换为 `false`
}
总结:
- 强制类型转换:开发者主动调用函数将数据类型显式转换。
- 隐式类型转换:JavaScript 自动转换类型,可能导致意外的结果。
===和==的区别
===
:严格相等,不会进行类型转换,只有当值和类型都相等时才返回 true。==
:宽松相等,允许类型转换,会将两个值的类型转换为相同类型后再进行比较。1 === 1; // true 1 === '1'; // false (不同类型,一个是数字,一个是字符串) true === 1; // false (布尔值与数字不同类型) null === undefined; // false (不同类型) // == 0 == false; // true (0 被转换为 false) 0 === false; // false (不同类型) [] == false; // true (空数组被转换为 false) [] === false; // false (不同类型)
总结:
=== 更严格、更安全
,而== 允许隐式类型转换
,但可能导致意外行为。
元素拖动如何实现,实现原理
实现步骤
监听 mousedown 事件
:当用户按下鼠标时,记录鼠标当前位置,并开始监听鼠标移动事件。监听 mousemove 事件
:在鼠标移动过程中,计算鼠标的位移并更新元素的位置,使其跟随鼠标移动。监听 mouseup 事件
:当鼠标松开时,停止移动操作,移除鼠标移动的监听器。
实现原理
获取初始位置:
当用户按下鼠标(mousedown
)时,获取当前鼠标相对于页面的坐标,记录元素的初始位置。计算位移并更新位置:
在鼠标移动时(mousemove
),不断获取新的鼠标位置,计算与初始按下时的差值,更新元素的 left 和 top 样式,使元素随着鼠标移动。
- 结束拖动:
当用户松开鼠标(mouseup
),停止监听 mousemove 事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
#draggable {
width: 100px;
height: 100px;
background-color: skyblue;
position: absolute;
cursor: grab;
}
</style>
<title>Draggable Element</title>
</head>
<body>
<div id="draggable">Drag me</div>
<script>
const draggable = document.getElementById('draggable');
let offsetX, offsetY, isDragging = false;
// 当鼠标按下时
draggable.addEventListener('mousedown', (event) => {
isDragging = true;
offsetX = event.clientX - draggable.offsetLeft;
offsetY = event.clientY - draggable.offsetTop;
draggable.style.cursor = 'grabbing';
// 防止文本选择
event.preventDefault();
});
// 当鼠标移动时
document.addEventListener('mousemove', (event) => {
if (isDragging) {
draggable.style.left = (event.clientX - offsetX) + 'px';
draggable.style.top = (event.clientY - offsetY) + 'px';
}
});
// 当鼠标松开时
document.addEventListener('mouseup', () => {
isDragging = false;
draggable.style.cursor = 'grab';
});
</script>
</body>
</html>
节流和防抖
- 节流: 降低用户事件响应的频率(节流是指在规定的时间间隔内,只执行一次操作)
function throttle(func, delay) { let lastTime = 0; return function(...args) { const now = Date.now(); if (now - lastTime >= delay) { lastTime = now; func.apply(this, args); } }; }
// 使用
window.addEventListener(‘scroll’, throttle(() => {
console.log(‘Throttled scroll event’);
}, 1000));
2. **防抖: 只响应最后一个用户事件(只有在规定的时间内没有再次触发)**
```javascript
function debounce(func, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// 使用
const input = document.getElementById('searchInput');
input.addEventListener('input', debounce(() => {
console.log('Debounced input event');
}, 500));
结合场景选择
节流:适合持续触发的场景,例如滚动、拖拽等事件,避免处理函数的频繁调用而影响性能。
防抖:适合非持续触发的场景,例如搜索框输入、表单校验等,只有在用户停止输入后才执行操作。
promise 和 async/await的关系
Promise
是异步处理的基础,它通过.then()
和.catch()
来处理异步操作及其结果。async/await
是基于Promise
的语法糖
,使得异步代码看起来像同步代码,减少嵌套和复杂的链式调用,提高了代码的可读性和维护性。
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data fetched');
}, 1000);
});
}
async function getData() {
try {
const result = await fetchData();
console.log(result); // 'Data fetched'
} catch (error) {
console.error(error);
}
}
getData();
webworker
什么是 Web Worker?
Web Worker
是一个独立的JavaScript`` 线程,与主线程并行执行代码。它不能直接操作
DOM,但可以通过消息传递机制与主线程通信。
Web Worker的主要用途是在后台运行长时间运行的
JavaScript` 任务,例如数据处理、文件解析等,避免在主线程上执行这些操作时出现界面卡顿的问题。Web Worker 的特性
独立运行:
Web Worker
运行在浏览器的单独线程中,不影响主线程(也就是 UI 线程)的执行。主线程负责处理用户交互
和DOM
操作,而Web Worker
运行复杂计算、数据处理等。无法访问 DOM:
Web Worker
不能直接访问和操作DOM
元素,因此它不适合处理与用户界面直接相关的操作。消息传递:主线程和
Web Worker
之间通过postMessage
和onmessage
进行通信。主线程可以向Worker
发送消息,Worker
可以返回结果。异步执行:因为
Worker
运行在独立的线程中,执行是异步的,主线程不会被阻塞。
3.1 创建 Worker 文件
// worker.js
self.onmessage = function(event) {
// 获取主线程发送过来的数据
const data = event.data;
// 执行复杂的任务
let result = 0;
for (let i = 0; i < data; i++) {
result += i;
}
// 将结果发送回主线程
self.postMessage(result);
};
3.2 在主线程中使用 Worker
// main.js
const worker = new Worker('worker.js');
// 向 Worker 发送消息
worker.postMessage(1000000000);
// 接收来自 Worker 的结果
worker.onmessage = function(event) {
console.log('Worker 计算结果:', event.data);
};
// 处理 Worker 错误
worker.onerror = function(error) {
console.error('Worker 出错:', error.message);
};
- Web Worker 允许在主线程之外运行 JavaScript 代码,用于处理后台的复杂计算任务,避免阻塞用户界面。
- 通信机制:主线程和 Worker 通过 postMessage 传递消息,不能直接访问 DOM。
- 适用场景:适合处理长时间的计算、数据处理任务,提高页面性能和用户体验
最后,如果项目和教程对你有所帮助或者你看见了还算比较喜欢,欢迎给我
star
,谢谢您!
持续更新中…,如果遇到问题欢迎联系我,在文章最后评论区【留言和讨论】,当然,欢迎点击文章最后的打赏按键,请博主一杯冰阔乐,笑~