为了更加高效的准备面试,所以面试题都来源于牛客网真题,让面试题更符合实际。
Cookie:
sessionStorage:
localStorage:
CSS 的 position 属性有以下几种属性值:
This is static
This is relative
This is absolute
This is fixed
This is sticky
// 1. 全局上下文中的 this
console.log(this); // 输出全局对象,如 window(浏览器环境)
// 2. 函数调用中的默认绑定
function foo() {
console.log(this);
}
foo(); // 输出全局对象,如 window(非严格模式下)
// 3. 对象方法调用中的 this
const obj = {
name: 'John',
greet: function() {
console.log(this.name);
}
};
obj.greet(); // 输出 'John'
// 4. 构造函数中的 this
function Person(name) {
this.name = name;
}
const person1 = new Person('Alice');
console.log(person1.name); // 输出 'Alice'
// 5. 使用 call、apply、bind 改变 this 的指向
const obj2 = {
name: 'Emily'
};
function introduce() {
console.log(`My name is ${this.name}`);
}
introduce.call(obj2); // 输出 'My name is Emily'
// 6. 箭头函数中的 this
const obj3 = {
name: 'Tom',
greet: () => {
console.log(this.name); // this 指向全局对象,因为箭头函数没有自己的 this 绑定
}
};
obj3.greet(); // 输出全局对象的 name(如果定义了)
1.定义泛型函数:
泛型函数使用 或其他字母来表示类型变量,这些类型变量可以在函数体内用作参数类型或返回类型的占位符。
function identity(arg: T): T {
return arg;
}
在上面的示例中, 表示这是一个泛型函数,T 是类型变量,它表示任意类型。函数 identity 接受一个参数 arg,类型为 T,并返回一个 T 类型的值。
2. 使用泛型函数:
// 调用泛型函数,并传入具体类型为 number
let result1 = identity(5); // result1 的类型为 number
console.log(result1); // 输出 5
// 调用泛型函数,并传入具体类型为 string
let result2 = identity("Hello"); // result2 的类型为 string
console.log(result2); // 输出 "Hello"
在这个示例中,identity(5) 和 identity(“Hello”) 分别传入了 number 类型和 string 类型的参数,TypeScript 根据传入的参数类型推导出 result1 和 result2 的具体类型。
3. 泛型函数处理数组示例:
function reverse(items: T[]): T[] {
return items.reverse();
}
let numbers = [1, 2, 3, 4, 5];
let reversedNumbers = reverse(numbers); // reversedNumbers 的类型为 number[]
console.log(reversedNumbers); // 输出 [5, 4, 3, 2, 1]
let names = ["Alice", "Bob", "Charlie"];
let reversedNames = reverse(names); // reversedNames 的类型为 string[]
console.log(reversedNames); // 输出 ["Charlie", "Bob", "Alice"]
在这个示例中,reverse 函数是一个泛型函数,接受一个数组 items,其中的元素类型为 T。根据传入的 numbers 和 names 数组,分别推导出 reversedNumbers 和 reversedNames 的具体类型。
盒模型概述:
CSS 盒模型是用来描述每个 HTML 元素在页面布局中所占空间的模型。它包括了元素的内容区域、内边距、边框和外边距四个部分。
组成部分详解:
width
和 height
属性控制大小。padding
属性设置,如 padding: 10px;
。border
属性定义边框的样式、宽度和颜色,如 border: 1px solid #000;
。margin
属性设置,如 margin: 10px;
。动画概述:
动画在前端开发中广泛应用,用于增强用户体验和提升网页交互性。它能够吸引用户的注意力,改善页面流畅性,并提供反馈和状态转换的视觉效果。
实现方式:
CSS 动画:使用 @keyframes
定义动画的关键帧,结合 transition
属性实现简单的过渡效果和动画效果。例如:
.box {
width: 100px;
height: 100px;
background-color: red;
transition: width 0.3s ease-in-out;
}
.box:hover {
width: 150px;
}
JavaScript 动画:通过 JavaScript 可以实现更复杂的动画效果和交互。常用的库如 GSAP(GreenSock Animation Platform)提供了丰富的动画 API,可以实现复杂的序列动画和交互效果。
// 使用 GSAP 库实现动画
gsap.to(".box", { duration: 1, x: 100, rotation: 360, ease: "power2.inOut" });
性能优化和交互设计:
Flexbox 概述:
Flexbox 是一种用于页面布局的弹性盒子模型,通过设置父容器的 display: flex;
属性,可以控制子项目的布局和排列方式。
水平居中实现:
.container {
display: flex;
justify-content: center; /* 水平居中 */
}
上述代码中,通过设置 .container 容器为 flex 布局,并使用 justify-content: center; 属性将子项目水平居中。
闭包概述:
闭包是指在函数内部创建另一个函数,并且内部函数可以访问外部函数的变量和参数,即使外部函数已经执行完毕。闭包通过保存了外部函数的执行环境,使得内部函数能够继续访问和操作外部函数的变量。
特性和作用:
应用场景示例:
事件处理函数:在事件监听中使用闭包可以保存事件触发时的状态或数据。
function addClickHandler() {
var count = 0;
document.getElementById('btn').addEventListener('click', function() {
count++;
console.log('Button clicked ' + count + ' times');
});
}
定时器:利用闭包可以保存定时器中的变量状态,实现循环或延时操作。
function delayedMessage() {
var count = 0;
var interval = setInterval(function() {
count++;
console.log('Delayed message ' + count);
if (count >= 5) {
clearInterval(interval);
}
}, 1000);
}
深拷贝与浅拷贝概述:
浅拷贝的实现方法:
使用 Object.assign()
实现浅拷贝:
let obj1 = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, obj1);
使用展开运算符 ...
实现浅拷贝:
let shallowCopy = { ...obj1 };
深拷贝的实现方法:
使用递归函数手动实现深拷贝:
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
let copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
使用 JSON.parse(JSON.stringify())
实现深拷贝(注意局限性):
let deepCopy = JSON.parse(JSON.stringify(obj1));
使用 Lodash 的 _.cloneDeep
方法实现深拷贝:
let _ = require('lodash');
let deepCopy = _.cloneDeep(obj1);
实际应用和注意事项:
JSON.parse(JSON.stringify())
方法时需要注意它的局限性,如不能复制函数、不可枚举属性、以及处理循环引用的能力。简要概述 ES6:
ES6 是 ECMAScript 的第六版,也是 JavaScript 的重大更新,带来了许多新特性和改进,提升了开发效率和代码可读性。
主要新特性及应用:
let
和 const
声明:块级作用域变量声明。
let x = 10;
const y = 20;
箭头函数:更简洁的函数语法和词法作用域的 this
绑定。
const add = (a, b) => a + b;
console.log(add(5, 3)); // 8
模板字符串:嵌入变量的字符串模板,更加简洁易读。
const name = 'Alice';
const greeting = `Hello, ${name}!`;
console.log(greeting); // Hello, Alice!
解构赋值:从数组或对象中提取值到变量中。
const [a, b] = [1, 2];
const {x, y} = {x: 3, y: 4};
console.log(a, b); // 1, 2
console.log(x, y); // 3, 4
默认参数:为函数参数设置默认值。
function greet(name = 'Guest') {
return `Hello, ${name}!`;
}
console.log(greet()); // Hello, Guest!
console.log(greet('Bob')); // Hello, Bob!
展开运算符(...
):用于数组和对象的展开。
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // [1, 2, 3, 4, 5]
for...of
循环:遍历可迭代对象的值。
const numbers = [10, 20, 30];
for (const num of numbers) {
console.log(num);
}
Map
和 Set
数据结构:提供更灵活的数据存储方式。
const map = new Map();
map.set('key1', 'value1');
console.log(map.get('key1')); // value1
const set = new Set([1, 2, 3]);
set.add(4);
console.log(set.has(2)); // true
模块化:使用 import
和 export
进行模块化开发。
// module.js
export const greet = (name) => `Hello, ${name}!`;
// main.js
import { greet } from './module.js';
console.log(greet('Alice')); // Hello, Alice!
类(Class):更接近面向对象编程的语法糖。
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}!`;
}
}
const alice = new Person('Alice');
console.log(alice.greet()); // Hello, Alice!
Promise:异步编程的新方式,取代回调函数。
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('Data fetched'), 1000);
});
};
fetchData().then((data) => console.log(data)); // Data fetched
箭头函数概述:
箭头函数是一种更简洁的函数书写方式,引入了新的语法,减少了冗余代码。箭头函数不会创建自己的 this
,而是从外层作用域继承 this
。
箭头函数的语法:
=>
定义函数。const add = x => x + 10;
console.log(add(5)); // 15
const add = (x, y) => x + y;
console.log(add(5, 3)); // 8
const square = x => x * x;
console.log(square(4)); // 16
return
语句:const sum = (x, y) => {
const result = x + y;
return result;
};
console.log(sum(5, 3)); // 8
箭头函数的特性:
this
绑定:箭头函数不会创建自己的 this
,而是继承自外层作用域:function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
const timer = new Timer(); // 每秒输出递增的数字
arguments
对象:箭头函数没有自己的 arguments
对象,可使用展开运算符 ...args
代替:const sum = (...args) => args.reduce((acc, val) => acc + val, 0);
console.log(sum(1, 2, 3, 4)); // 10
new
关键字实例化对象。prototype
属性:箭头函数没有 prototype
属性。应用场景:
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8]
this
上下文:在需要保持当前 this
上下文的场景中使用,如事件处理器、定时器等:class MyComponent {
constructor() {
this.name = 'MyComponent';
document.getElementById('myButton').addEventListener('click', () => {
console.log(this.name); // 输出 'MyComponent'
});
}
}
const component = new MyComponent();
基本概念:
Object
:用于存储键值对,键名只能是字符串或符号(Symbol
)。Map
:一种新的键值对存储结构,键名可以是任意类型的值(包括对象)。主要区别:
键的类型:
Object
的键名只能是字符串或符号。Map
的键名可以是任意类型。键值对的迭代顺序:
Object
的键值对没有固定的迭代顺序。Map
的键值对按照插入顺序进行迭代。性能:
Map
在频繁增删键值对时性能较优。Object
在简单的、静态键值对存储时较为高效。常用操作:
Map
提供了更丰富的操作方法,如 set
、get
、has
、delete
、clear
。Object
需要使用更多的原生方法来实现类似功能。大小:
Map
有 size
属性,可以直接获取键值对的数量。Object
没有直接获取大小的属性,需要手动计算。原型链:
Object
有原型链,会继承一些默认属性和方法。Map
不会继承任何默认属性和方法。代码示例:
创建和操作 Map
:
const map = new Map();
map.set('key1', 'value1');
map.set(2, 'value2');
console.log(map.get('key1')); // 'value1'
console.log(map.get(2)); // 'value2'
console.log(map.size); // 2
map.delete('key1');
console.log(map.has('key1')); // false
创建和操作 Object
:
const obj = {};
obj['key1'] = 'value1';
obj[2] = 'value2';
console.log(obj['key1']); // 'value1'
console.log(obj[2]); // 'value2'
console.log(Object.keys(obj).length); // 2
delete obj['key1'];
console.log('key1' in obj); // false
MVVM 和 MVC 概述:
结构和特性:
MVC:
MVVM:
主要区别:
数据绑定:
关注点分离:
复杂度和灵活性:
框架支持:
代码示例:
MVC:
// Model
class Model {
constructor() {
this.data = '';
}
setData(newData) {
this.data = newData;
}
getData() {
return this.data;
}
}
// View
class View {
constructor(controller) {
this.controller = controller;
this.init();
}
init() {
document.getElementById('button').addEventListener('click', () => {
this.controller.updateModel();
});
}
render(data) {
document.getElementById('output').innerText = data;
}
}
// Controller
class Controller {
constructor(model, view) {
this.model = model;
this.view = view;
}
updateModel() {
this.model.setData('Hello MVC');
this.view.render(this.model.getData());
}
}
const model = new Model();
const controller = new Controller(model);
const view = new View(controller);
MVVM(以 Vue.js 为例):
<div id="app">
<input v-model="message" />
<p>{{ message }}p>
div>
<script>
// ViewModel
new Vue({
el: '#app',
data: {
message: 'Hello MVVM'
}
});
script>
Vue 生命周期概述:
Vue 实例在创建过程中会经历一系列的初始化过程,从创建、挂载、更新到销毁,这一过程称为生命周期。
各个生命周期钩子函数的介绍:
代码示例:
展示一个简单的 Vue 组件,使用所有生命周期钩子函数:
<template>
<div>
<p>{{ message }}</p>
<button @click="updateMessage">Update Message</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
};
},
beforeCreate() {
console.log('beforeCreate: 实例初始化之后,数据观测和事件配置之前调用');
},
created() {
console.log('created: 实例创建完成,数据观测和事件配置已完成,但还未挂载到 DOM');
},
beforeMount() {
console.log('beforeMount: 在挂载开始之前调用,相关的 render 函数首次被调用');
},
mounted() {
console.log('mounted: 实例挂载到 DOM 后调用,DOM 操作可以在此进行');
},
beforeUpdate() {
console.log('beforeUpdate: 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前');
},
updated() {
console.log('updated: 由于数据更改导致虚拟 DOM 重新渲染和打补丁后调用');
},
beforeDestroy() {
console.log('beforeDestroy: 实例销毁之前调用,此时实例仍然完全可用');
},
destroyed() {
console.log('destroyed: 实例销毁后调用,组件的所有事件监听器被移除,所有子实例也被销毁');
},
methods: {
updateMessage() {
this.message = 'Message Updated!';
}
}
};
</script>
父子组件生命周期钩子的基本概念:
了解 Vue 组件的生命周期钩子:beforeCreate
、created
、beforeMount
、mounted
、beforeUpdate
、updated
、beforeDestroy
和 destroyed
。
父子组件生命周期的触发顺序:
创建阶段:
beforeCreate
created
beforeMount
beforeCreate
created
beforeMount
mounted
mounted
更新阶段:
beforeUpdate
beforeUpdate
updated
updated
销毁阶段:
beforeDestroy
beforeDestroy
destroyed
destroyed
代码示例:
展示一个简单的父子组件,打印出生命周期钩子的触发顺序:
父组件:
Parent Component
子组件:
Child Component
双向数据绑定的基本概念:
Vue 的双向数据绑定是指数据模型层和视图层之间的自动同步,数据的变化会实时更新到视图,而视图中的变化也会反映到数据模型中。
数据劫持(Object.defineProperty):
Vue 使用 Object.defineProperty
方法来劫持对象属性的访问和修改。通过这种方式,Vue 能够监听到每个属性的变化,从而实现数据的响应式更新。
发布订阅模式:
在 Vue 中,每个数据都有对应的依赖收集器(Dep),而视图中的指令(比如 v-model
)会创建一个监听器(Watcher)。当数据变化时,会触发对应属性的依赖收集器,通知所有相关的监听器更新视图。
响应式系统的运作机制:
Object.defineProperty
进行数据劫持。组件通信的基本方式:
props / $emit:父子组件通信的基础方式。父组件通过 props 向子组件传递数据,子组件通过 $emit 触发事件向父组件传递数据。
事件总线:利用 Vue 实例作为中央事件总线,可以在任意关系的组件之间传递事件或数据,适合于兄弟组件通信。
Vuex:用于大型应用中集中管理共享状态。通过 store 存储和管理状态,各组件通过 getters 和 mutations 修改和获取状态。
$attrs / $listeners:透传父组件的属性和监听器到子组件,简化了属性的传递和事件的监听,适合于需要传递大量属性的情况。
provide / inject:祖先组件通过 provide 提供数据,后代组件通过 inject 注入数据,适合于跨层级组件通信的场景。
场景和优缺点分析:
props / $emit:简单直观,适合于父子组件通信,但对于复杂嵌套或多层级组件通信可能会显得繁琐。
事件总线:适合于非直接关系的组件通信,但组件之间的关系不明确,不易于追踪和调试。
Vuex:适合于状态复杂或需要多个组件共享状态的场景,但对于小型应用可能会显得过于复杂。
$attrs / $listeners:适合于需要将父组件的属性和事件传递给子组件的情况,但不适合于多层级嵌套的场景。
provide / inject:适合于祖先组件向所有后代组件传递依赖,但当跨级关系不清晰时会导致组件之间的耦合性增加。
示例代码:
父子组件通信示例(props / $emit):
ParentComponent.vue:
Parent Component
ChildComponent.vue:
Child Component
这个示例展示了父组件通过 props 向子组件传递数据,并通过 $emit 触发事件向父组件传递数据的过程。
同源策略:
浏览器的同源策略限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
解决跨域的常见方法:
CORS(跨域资源共享):通过服务端设置响应头部,如 Access-Control-Allow-Origin
,允许指定的源访问资源。
JSONP(JSON with Padding):利用 标签的跨域特性,动态创建
标签请求跨域数据,并通过回调函数处理返回数据。
代理服务器:前端通过向同域名下的代理服务器发起请求,再由代理服务器转发到目标服务器,绕过浏览器的同源策略限制。
WebSocket:使用 WebSocket 协议进行通信,支持跨域,但需要服务器端支持 WebSocket。
跨域资源嵌入:在响应中设置合适的跨域资源嵌入标记,如 和
。
安全性考虑:
使用 CORS 时,确保服务器设置合适的响应头,避免开放过多权限,防止恶意请求。
JSONP 存在安全隐患,必须信任响应的内容,可能容易受到 XSS 攻击的影响。
使用代理服务器时,需要进行严格的安全审查和过滤,防止恶意请求通过代理访问目标服务器。
栈(Stack):
堆(Heap):
使用场景:
TCP(传输控制协议):
UDP(用户数据报协议):
TCP(Transmission Control Protocol):
UDP(User Datagram Protocol):
对于直播应用,UDP 通常是更好的选择,因为它提供了更低的延迟和更高的传输效率,适合实时传输。然而,完全使用UDP可能会遇到数据丢失的问题,特别是在不稳定的网络环境下。因此,实际应用中可能会采用以下策略:
最终,选择哪种协议或技术,需要根据具体的应用需求和网络环境进行权衡。
TCP三次握手的步骤和原因:
第一次握手 - SYN:
x
,向服务器发送一个SYN报文段(SYN=1),请求建立连接。SYN_SENT
状态,等待服务器确认。第二次握手 - SYN-ACK:
x+1
,表示期望收到从 x+1
开始的数据。SYN_RCVD
状态。第三次握手 - ACK:
y+1
。ESTABLISHED
状态,连接建立成功。为什么需要三次握手:
结论:
三次握手是TCP连接建立过程中的一个关键步骤,它确保了连接的可靠性和数据传输的稳定性。通过这个机制,TCP协议能够在不可靠的IP网络层之上提供可靠的数据传输服务。
TCP四次挥手的步骤和原因:
第一次挥手 - FIN:
FIN-WAIT-1
状态。第二次挥手 - ACK:
FIN-WAIT-2
状态。第三次挥手 - FIN:
LAST-ACK
状态。第四次挥手 - ACK:
TIME-WAIT
状态。为什么需要四次挥手:
结论:
四次挥手是TCP连接终止过程中的一个关键步骤,它确保了连接的有序和安全关闭。通过这个机制,TCP协议能够在保证数据传输完整性的同时,安全地结束通信会话。
标准盒模型和怪异盒模型的区别:
标准盒模型(W3C标准):
width
和 height
指的是内容区域的宽度和高度。width
加上左右两侧的 padding
和 border
。怪异盒模型(IE盒模型):
width
和 height
包括了内容区域、内边距、边框,但不包括外边距。width
,这个值实际上已经包含了 padding
和 border
。示例比较:
100px
,内边距 10px
,边框 5px
。width: 100px;
,元素的总宽将是 100px + 10px*2 + 5px*2 = 130px
。width: 100px;
,元素的总宽仍然是 100px
,因为这里的 100px
已经包括了 padding
和 border
。为什么存在两种盒模型:
box-sizing
属性来统一盒模型的行为。结论:
了解两种盒模型的差异对于跨浏览器开发非常重要。开发者可以通过设置 box-sizing: border-box;
来让元素的 width
和 height
按照标准盒模型来计算,这样可以简化布局并提高代码的可维护性。
上下两个元素的外边距间距问题:
假设我们有两个元素,每个元素的 margin-bottom
和 margin-top
都是20px。
外边距折叠现象:
在CSS中,当两个垂直方向相邻的元素相遇时,它们的外边距不会累加。相反,它们会折叠成一个单一的外边距,这个外边距的值等于两个相遇外边距中较大的那个。
间距计算规则:
margin-bottom
是10px,另一个元素的 margin-top
是20px,那么间距将是20px,因为20px是两者中较大的值。结论:
在这种情况下,两个元素的外边距都是20px,所以它们之间的间距是20px,而不是40px。这是因为发生了外边距折叠,只保留了较大的外边距值。
CSS垂直水平居中的方法:
使用margin
属性(传统方法):
margin
属性为auto
来实现水平居中。line-height
属性。使用Flexbox:
display: flex;
,然后使用justify-content: center;
和align-items: center;
实现水平和垂直居中。.container {
display: flex;
justify-content: center;
align-items: center;
}
使用Grid:
.container {
display: grid;
place-items: center;
}
使用绝对定位和transform属性:
.container {
position: relative;
}
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
使用text-align和line-height(仅限于内联或内联块元素):
假设我们使用React Router来实现前端路由:
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import HomePage from './HomePage';
import AboutPage from './AboutPage';
function App() {
return (
);
}
then
和catch
方法的使用,以及Promise链的构建。实现一个基本的Promise可以按照以下步骤进行:
以下是使用JavaScript实现的简单Promise示例:
class MyPromise {
constructor(executor) {
// 初始化状态为Pending
this.state = 'Pending';
// 成功或失败的结果值
this.value = undefined;
// 存储then方法的回调队列
this.onFulfilled = [];
this.onRejected = [];
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
resolve(value) {
// 状态改变为Fulfilled,并存储值
if (this.state === 'Pending') {
this.state = 'Fulfilled';
this.value = value;
this.onFulfilled.forEach(fn => fn(value));
}
}
reject(error) {
// 状态改变为Rejected,并存储错误
if (this.state === 'Pending') {
this.state = 'Rejected';
this.value = error;
this.onRejected.forEach(fn => fn(error));
}
}
then(onFulfilled, onRejected) {
// 处理Fulfilled状态
if (this.state === 'Fulfilled') {
onFulfilled(this.value);
} else if (this.state === 'Rejected' && typeof onRejected === 'function') {
onRejected(this.value);
}
// 将回调加入队列,等待状态改变时调用
return new MyPromise((resolve, reject) => {
if (this.state === 'Pending') {
this.onFulfilled.push(value => {
// 提供对 then 的链式调用支持
resolve(onFulfilled ? onFulfilled(value) : value);
});
this.onRejected.push(error => {
reject(onRejected ? onRejected(error) : error);
});
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
}
NaN
和undefined
这两种特殊值。typeof
、instanceof
等操作符进行类型检测。JavaScript的类型可以分为两大类:基本数据类型和引用数据类型。
// Number
let age = 25;
// String
let name = 'John Doe';
// Boolean
let isApproved = false;
// Undefined
let x;
// Null
let y = null;
// Symbol (ES6 新增)
let mySymbol = Symbol('mySymbol');
// Object
let person = {
name: 'Alice',
age: 30
};
// Array
let numbers = [1, 2, 3, 4, 5];
// Function
function sayHello() {
console.log('Hello, World!');
}
'5' + 3; // "53",字符串和数字相加时,数字会被转换为字符串
全局作用域:
var globalVar = "I am a global variable";
function showGlobalVar() {
console.log(globalVar); // 可以访问到 globalVar
}
showGlobalVar(); // 输出:I am a global variable
var
、let
或 const
声明的变量会自动成为全局变量:function createGlobalVar() {
undeclaredVar = "I am also a global variable";
}
createGlobalVar();
console.log(undeclaredVar); // 输出:I am also a global variable
函数作用域:
function localScope() {
var localVar = "I am a local variable";
console.log(localVar); // 可以访问到 localVar
}
localScope();
console.log(localVar); // 报错:localVar 未定义
块级作用域:
在块级作用域中声明的变量只能在块内部访问。
例如:
if (true) {
let blockVar = "I am a block variable";
console.log(blockVar); // 可以访问到 blockVar
}
console.log(blockVar); // 报错:blockVar 未定义
const
也具有块级作用域:
for (const i = 0; i < 3; i++) {
console.log(i); // 输出 0, 1, 2
}
console.log(i); // 报错:i 未定义
var
的作用域:
var
声明的变量具有函数作用域。例如:
function varExample() {
console.log(varVar); // 输出 undefined(变量提升)
var varVar = "I am a var variable";
console.log(varVar); // 输出 "I am a var variable"
}
varExample();
console.log(varVar); // 报错:varVar 未定义
let 的作用域:
if (true) {
let letVar = "I am a let variable";
console.log(letVar); // 输出 "I am a let variable"
}
console.log(letVar); // 报错:letVar 未定义
if (true) {
const constVar = "I am a const variable";
console.log(constVar); // 输出 "I am a const variable"
}
console.log(constVar); // 报错:constVar 未定义
// 声明后不能重新赋值
const anotherConstVar = "Initial value";
anotherConstVar = "New value"; // 报错:Assignment to constant variable.
静态定位(static
):
top
、right
、bottom
、left
和 z-index
属性的影响。相对定位(relative
):
top
、right
、bottom
、left
属性进行偏移。绝对定位(absolute
):
static
)进行定位。固定定位(fixed
):
粘性定位(sticky
):
协议概述:
安全性:
端口:
性能:
证书:
SEO和用户信任:
在 Vue 3 中,ref
和 reactive
都是用于创建响应式数据的工具,但它们在底层实现上有所不同。
基本概念:
ref
用于创建包含单个值的响应式引用,适用于基本类型和需要单独引用的场景。reactive
用于创建包含多个属性的响应式对象,适用于复杂对象。使用场景:
ref
适合处理基本类型(如字符串、数字)或需要对整个对象进行引用变更的场景。例如:const count = ref(0);
count.value++; // 通过 .value 访问和修改值
reactive
适合处理复杂对象和嵌套结构。例如:const state = reactive({ count: 0, nested: { foo: 'bar' } });
state.count++; // 直接访问和修改属性
state.nested.foo = 'baz'; // 深层次属性的响应式更新
底层实现:
ref
使用 RefImpl
类来实现。其核心是一个对象,用于存储内部值,并通过 getter
和 setter
来拦截读取和修改操作。ref
包装的值变化时,dep
依赖收集机制会通知所有依赖此 ref
的副作用进行更新。reactive
使用 Proxy
来代理对象的所有属性访问和变更。getter
和 setter
触发依赖收集和派发更新,确保对象内部的任何属性变化都能引起响应式更新。性能和优化:
ref
的性能较优,因为其包装开销较小。reactive
提供了更全面的响应式支持,但在频繁的深层次属性访问和变更时可能有较高的性能开销。在 CSS 中,伪类和伪元素用于增强选择器的功能和样式的控制。以下是常见的伪类和伪元素的介绍。
伪类:
伪类用于选择元素的特定状态,常见伪类包括:
动态伪类:
:hover
:鼠标悬停时应用的样式。:active
:元素被激活(通常是被点击)时应用的样式。:focus
:元素获得焦点时应用的样式。结构性伪类:
:first-child
:选择父元素的第一个子元素。:last-child
:选择父元素的最后一个子元素。:nth-child(n)
:选择父元素的第 n 个子元素。:nth-of-type(n)
:选择父元素中特定类型的第 n 个子元素。其他伪类:
:disabled
:选择被禁用的表单元素。:checked
:选择被选中的表单元素。:not(selector)
:选择不匹配指定选择器的元素。示例:
a:hover {
color: red;
}
input:focus {
border-color: blue;
}
li:nth-child(odd) {
background-color: lightgray;
}
伪元素:
伪元素用于创建元素的特定部分,常见伪元素包括:
::before
:在元素内容之前插入内容。::after
:在元素内容之后插入内容。::first-line
:选择元素的第一行文本。::first-letter
:选择元素的第一个字母。::placeholder
:选择输入字段的占位符文本。示例:
p::first-line {
font-weight: bold;
}
p::first-letter {
font-size: 2em;
color: red;
}
.element::before {
content: "Prefix";
color: gray;
}
.element::after {
content: "Suffix";
color: gray;
}
input::placeholder {
color: darkgray;
}
实际应用:
:hover
用于改变按钮在鼠标悬停时的样式,:focus
用于改变输入框在获得焦点时的样式。::before
和 ::after
插入图标或额外的文本装饰。实现大文件上传的常见方法是使用分块上传,主要步骤和原理如下:
基本概念:
大文件上传面临的主要挑战包括网络不稳定、上传时间长、服务器内存占用高等。分块上传通过将大文件分成小块逐个上传,可以提高上传的可靠性和效率。
分块上传的实现步骤:
前端:
File
对象的 slice
方法切割文件:function sliceFile(file, chunkSize) {
const chunks = [];
for (let start = 0; start < file.size; start += chunkSize) {
const chunk = file.slice(start, start + chunkSize);
chunks.push(chunk);
}
return chunks;
}
async function uploadChunk(chunk, index) {
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('index', index);
await fetch('/upload', { method: 'POST', body: formData });
}
const file = document.getElementById('file').files[0];
const chunks = sliceFile(file, 1024 * 1024); // 1MB per chunk
chunks.forEach((chunk, index) => uploadChunk(chunk, index));
后端:
from flask import Flask, request
app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload():
chunk = request.files['chunk']
index = request.form['index']
with open(f'/tmp/upload_{index}', 'wb') as f:
f.write(chunk.read())
return 'OK'
import os
def merge_chunks(file_path, total_chunks):
with open(file_path, 'wb') as merged_file:
for i in range(total_chunks):
chunk_path = f'/tmp/upload_{i}'
with open(chunk_path, 'rb') as chunk_file:
merged_file.write(chunk_file.read())
os.remove(chunk_path)
# 假设前端通知合并所有块
merge_chunks('/tmp/final_file', 100)
续传和错误处理:
// 简单的续传示例
async function resumeUpload(chunks) {
const uploadedChunks = await getUploadedChunks();
for (let i = 0; i < chunks.length; i++) {
if (!uploadedChunks.includes(i)) {
await uploadChunk(chunks[i], i);
}
}
}
性能优化:
综上所述,大文件上传可以通过分块上传来实现,并结合续传和错误处理机制,进一步优化性能以提高上传效率和用户体验。
图片懒加载是一种延迟加载图片的方法,只有当图片即将出现在视口内时才进行加载,从而优化页面加载性能。下面介绍两种实现方式:原生 JavaScript 和使用库。
基本概念:
图片懒加载通过延迟加载图片,减少初始页面加载时间,降低服务器负载,提高用户体验。
原生 JavaScript 实现:
可以使用 IntersectionObserver
API 来检测图片是否进入视口,并在图片进入视口时加载图片。
示例代码:
<img data-src="path/to/image.jpg" alt="Lazy Loaded Image" class="lazy">
document.addEventListener("DOMContentLoaded", function() {
let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Fallback for browsers that do not support IntersectionObserver
let lazyLoad = function() {
let scrollTop = window.pageYOffset;
lazyImages.forEach(function(img) {
if (img.offsetTop < (window.innerHeight + scrollTop)) {
img.src = img.dataset.src;
img.classList.remove("lazy");
}
});
if (lazyImages.length == 0) {
document.removeEventListener("scroll", lazyLoad);
window.removeEventListener("resize", lazyLoad);
window.removeEventListener("orientationChange", lazyLoad);
}
};
document.addEventListener("scroll", lazyLoad);
window.addEventListener("resize", lazyLoad);
window.addEventListener("orientationChange", lazyLoad);
}
});
# 引入 lazysizes 库:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.2.2/lazysizes.min.js" async></script>
# 使用 lazysizes 类:
<img data-src="path/to/image.jpg" class="lazyload" alt="Lazy Loaded Image">
# 引入 Lozad.js 库:
# 使用 Lozad.js:
综上所述,图片懒加载是通过延迟加载图片来优化页面性能的一种技术,可以使用原生 JavaScript 或第三方库实现。
在 JavaScript 中,原型和原型链是理解对象继承和属性查找机制的关键概念。以下是对原型和原型链的详细解释。
基本概念:
原型:
每个 JavaScript 对象都有一个内部属性 [[Prototype]]
,指向另一个对象,这个对象被称为原型。原型对象可以包含该对象共享的属性和方法。
在现代 JavaScript 中,可以通过 __proto__
访问原型(不推荐),或者使用 Object.getPrototypeOf
和 Object.setPrototypeOf
方法。
示例:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`);
};
const alice = new Person('Alice');
alice.greet(); // 输出:Hello, my name is Alice
原型链:
原型链是由对象及其原型组成的链式结构。当访问一个对象的属性时,如果该属性不存在于对象本身,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或到达原型链的末端。
示例:
console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
作用与机制:
属性查找:
当访问一个对象的属性时,JavaScript 会先在对象自身的属性中查找。如果没有找到,则会沿着原型链向上查找,直到找到属性或到达原型链的末端(即 null
)。
示例:
console.log(alice.hasOwnProperty('name')); // true
console.log(alice.hasOwnProperty('greet')); // false, greet 在原型链上
console.log('greet' in alice); // true, 因为 greet 在原型链上
继承机制:
通过原型链,可以实现对象的继承,使得一个对象可以共享另一个对象的属性和方法。构造函数和原型是常见的实现方式。
示例:
function Employee(name, jobTitle) {
Person.call(this, name); // 调用父构造函数
this.jobTitle = jobTitle;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.work = function() {
console.log(`${this.name} is working as ${this.jobTitle}`);
};
const bob = new Employee('Bob', 'Engineer');
bob.greet(); // 输出:Hello, my name is Bob
bob.work(); // 输出:Bob is working as Engineer
实际应用:
使用构造函数和原型实现对象的继承,如上例所示。
通过 Object.create
创建具有特定原型的对象:
const personPrototype = {
greet() {
console.log(`Hello, my name is ${this.name}`);
}
};
const charlie = Object.create(personPrototype);
charlie.name = 'Charlie';
charlie.greet(); // 输出:Hello, my name is Charlie
理解原型链有助于调试和优化代码。例如,避免在对象本身和原型链上存在重名的属性,以减少不必要的查找开销。
综上所述,原型和原型链是 JavaScript 中对象继承和属性查找的基础。理解这些概念可以帮助你更好地掌握 JavaScript 的对象机制和编程技巧。
基本概念:
类:
类是一个模板或蓝图,用于定义对象的属性和方法。在类中,可以定义属性(成员变量)和方法(成员函数),这些属性和方法描述了对象的状态和行为。
示例:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
对象:
对象是类的实例。对象包含类定义的实际数据和行为(方法)。每个对象都有自己独立的属性,但它们共享类定义的结构和行为。
示例:
const alice = new Person('Alice', 30);
alice.greet(); // 输出:Hello, my name is Alice and I am 30 years old.
const bob = new Person('Bob', 25);
bob.greet(); // 输出:Hello, my name is Bob and I am 25 years old.
实例化:
new Person('Alice', 30)
和 new Person('Bob', 25)
实例化了两个 Person
对象。应用场景:
类:
类用于定义和创建具有相同属性和行为的多个对象。例如,定义一类学生,每个学生都有姓名、年龄、学号等属性和方法。
示例:
class Student {
constructor(name, id) {
this.name = name;
this.id = id;
}
study() {
console.log(`${this.name} is studying.`);
}
}
对象:
对象是实际使用类创建的实例,用于存储和操作数据。例如,创建具体的学生对象来表示具体的学生。
示例:
const student1 = new Student('John', 'S001');
const student2 = new Student('Jane', 'S002');
student1.study(); // 输出:John is studying.
student2.study(); // 输出:Jane is studying.
综上所述,类是用于定义对象的模板或蓝图,而对象是类的具体实例。类定义了对象的属性和方法,而对象则包含实际的数据和行为。通过类的实例化,可以创建多个具有相同结构和行为的对象,从而实现面向对象编程的抽象和复用。
同步任务:
同步任务在调用栈中按顺序执行,执行完毕后调用栈清空。
异步任务:
异步任务分为宏任务(macro task)和微任务(micro task)。宏任务包括 setTimeout
、setInterval
、I/O 操作等。微任务包括 Promise
的 then
、catch
、finally
回调和 MutationObserver
。
事件循环执行过程:
Promise
的回调。setTimeout
回调。示例:
console.log('Start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
解析:
1.console.log(‘Start’) 是同步任务,立即执行。
2.setTimeout 是宏任务,回调被放入宏任务队列。
3.Promise.resolve().then 是微任务,回调被放入微任务队列。
4.console.log(‘End’) 是同步任务,立即执行。
5.调用栈为空,执行微任务队列中的回调,输出 Promise。
6.执行宏任务队列中的回调,输出 setTimeout。
输出顺序为:
Start
End
Promise
setTimeout
function asyncOperation() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Operation Complete');
}, 1000);
});
}
asyncOperation().then((message) => {
console.log(message); // 输出:Operation Complete
});
console.log('Async operation started');
解析:
1、asyncOperation 返回一个 Promise,其回调被放入宏任务队列。
2、console.log(‘Async operation started’) 是同步任务,立即执行。
3、1 秒后,宏任务队列中的回调执行,输出 Operation Complete。
输出顺序为:
Async operation started
Operation Complete
综上所述,浏览器的事件循环通过调用栈和任务队列管理同步和异步任务的执行顺序,确保代码按照预期顺序运行。理解事件循环有助于编写和调试异步代码,提高代码的执行效率和可维护性。
Cookie 的基本概念:
Cookie 的作用:
安全性和限制:
HttpOnly
、Secure
、SameSite
属性可以提高安全性。// 设置 Cookie
document.cookie = "username=JohnDoe; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/; Secure; HttpOnly; SameSite=Strict";
// 读取 Cookie
const getCookie = (name) => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
};
console.log(getCookie("username")); // 输出: JohnDoe
Cookie 过期时间的设置:
Cookie 的携带机制:
示例代码:
设置 Cookie 过期时间
服务器端设置:
HTTP/1.1 200 OK
Set-Cookie: username=JohnDoe; Expires=Wed, 21 Oct 2024 07:28:00 GMT; Path=/;
客户端 JavaScript 设置:
document.cookie = "username=JohnDoe; expires=Wed, 21 Oct 2024 07:28:00 GMT; path=/;";
浏览器自动携带 Cookie
当用户访问匹配域名和路径的页面时,浏览器会自动携带相关的 Cookie:
GET /path/to/resource HTTP/1.1
Host: www.example.com
Cookie: username=JohnDoe
总结
Cookie 过期时间的设置:可以由服务器在 Set-Cookie 头中设置,也可以由客户端通过 JavaScript 设置。
Cookie 的携带:由浏览器在请求时自动完成,浏览器会根据请求的域名和路径,自动携带符合条件的 Cookie。
基本概念:
undefined
:表示变量声明了但未赋值,或对象属性不存在。null
:表示一个变量被明确赋值为空,没有对象值。使用场景:
undefined
:通常由 JavaScript 引擎自动分配,表示缺少值。
null
:通常由开发者手动赋值,表示没有对象。
类型和相等性判断:
typeof undefined
返回 "undefined"
。typeof null
返回 "object"
。undefined == null
返回 true
(抽象相等)。undefined === null
返回 false
(严格相等)。示例代码:
// undefined 示例
let a;
console.log(a); // 输出: undefined
let obj = {};
console.log(obj.prop); // 输出: undefined
function foo() {}
console.log(foo()); // 输出: undefined
// null 示例
let b = null;
console.log(b); // 输出: null
let obj2 = { prop: null };
console.log(obj2.prop); // 输出: null
// 类型判断
console.log(typeof undefined); // 输出: "undefined"
console.log(typeof null); // 输出: "object"
// 相等性判断
console.log(undefined == null); // 输出: true
console.log(undefined === null); // 输出: false
watch
的特点和使用场景:
computed
的特点和使用场景:
区别:
watch
在数据变化时立即触发相应的处理函数。computed
会根据其依赖的数据是否发生变化来决定是否重新计算。watch
不关心返回值,可以执行任何副作用操作。computed
必须有返回值,并且返回值会被缓存,多次访问时直接返回缓存的结果。区别:
// watch 的使用示例
watch: {
// 监听单个数据变化
counter(newValue, oldValue) {
console.log(`Counter changed from ${oldValue} to ${newValue}`);
},
// 监听对象深层次数据变化
'user.name': {
handler(newValue, oldValue) {
console.log(`User name changed from ${oldValue} to ${newValue}`);
},
deep: true
}
}
// computed 的使用示例
computed: {
// 根据原始数据计算过滤后的列表
filteredList() {
return this.list.filter(item => item.active);
},
// 计算和返回一个新的衍生数据
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
基本概念:
节流(Throttle):
节流是在一定时间内,只允许一个事件处理函数被调用。例如,如果设置的时间间隔是 100ms,则在这 100ms 内,无论事件触发多少次,事件处理函数只会执行一次。
实现代码:
function throttle(func, limit) {
let inThrottle;
return function() {
const context = this;
const args = arguments;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
}
}
防抖(Debounce):
防抖是在一定时间内,如果事件被再次触发,则重新计时。只有当事件在设定的时间内没有再次触发,事件处理函数才会被调用。例如,如果设置的时间间隔是 300ms,则只有当事件触发后 300ms 内没有再次触发,事件处理函数才会执行。
实现代码:
function debounce(func, delay) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), delay);
}
}
应用场景:
节流:
窗口滚动:
在用户滚动窗口时,频繁触发滚动事件会导致性能问题。使用节流技术可以限制滚动事件的触发频率,减少性能开销。
示例:
window.addEventListener('scroll', throttle(() => {
console.log('Scrolled!');
}, 100));
按钮点击:
防止用户快速多次点击按钮,导致多次请求发送。
示例:
const button = document.getElementById('myButton');
button.addEventListener('click', throttle(() => {
console.log('Button clicked!');
}, 2000));
鼠标移动:
在拖拽或绘制操作中,控制鼠标移动事件的触发频率。
示例:
document.addEventListener('mousemove', throttle((event) => {
console.log(`Mouse position: ${event.clientX}, ${event.clientY}`);
}, 50));
防抖:
输入框搜索:
在用户输入时,防止每次输入都触发搜索请求。只有用户停止输入一段时间后,才发送搜索请求。
示例:
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce(() => {
console.log('Search:', searchInput.value);
}, 300));
窗口调整:
在用户调整窗口大小时,频繁触发调整事件会导致性能问题。使用防抖技术可以确保调整事件在用户停止调整窗口一段时间后才触发。
示例:
window.addEventListener('resize', debounce(() => {
console.log('Window resized!');
}, 500));
表单验证:
在表单字段输入时,防止频繁触发验证请求。只有用户停止输入一段时间后,才进行表单验证。
示例:
const formInput = document.getElementById('formInput');
formInput.addEventListener('input', debounce(() => {
console.log('Validating form input:', formInput.value);
}, 300));
综上所述,节流和防抖是控制事件触发频率的有效方法。节流用于在一定时间内限制事件处理函数的执行次数,而防抖用于在事件频繁触发时,确保在指定时间内只执行一次事件处理函数。理解和合理使用这两种技术,可以显著提升应用的性能和用户体验。
Vite 为什么这么快:
即时热更新(HMR):
Vite 利用浏览器的原生 ES 模块支持,实现了快速的模块热更新(HMR)。当源代码发生变化时,Vite 只需要更新变化的模块,而无需重新打包整个应用,大幅减少了重新编译的时间。
按需编译:
在传统的构建工具中,每次修改代码后都需要重新打包整个项目。而 Vite 在开发环境中采用按需编译的方式,只有当模块被请求时才进行编译,这减少了项目启动时间和重新编译的时间。
依赖预构建:
Vite 在启动时会使用 esbuild 对第三方依赖进行预构建,这使得这些依赖可以快速加载和执行。esbuild 是一个高速构建工具,相比传统的 JavaScript 打包工具,构建速度非常快。
开发环境的优化:
即时热更新(HMR):
Vite 实现了高效的模块热更新,修改代码后可以立即看到变化,无需刷新整个页面。
按需编译:
Vite 在开发环境中不需要预先打包整个项目,只有在模块被请求时才进行编译,从而减少启动时间和重新编译的时间。
依赖预构建:
Vite 使用 esbuild 对第三方依赖进行预构建,提高了依赖的加载速度。
生产环境的优化:
静态资源处理:
在生产环境中,Vite 会对静态资源进行处理和优化,包括压缩、代码分割和缓存等,从而生成高性能的构建结果。
基于 Rollup 的构建:
Vite 在生产环境中使用 Rollup 进行构建。Rollup 是一个高效的 JavaScript 模块打包工具,具有强大的代码优化和打包能力。通过 Rollup 的 Tree Shaking 特性,Vite 可以去除未使用的代码,减少打包后的文件体积。
Tree Shaking:
Vite 利用 Rollup 的 Tree Shaking 特性,自动去除未使用的代码,从而减少打包后的文件体积,提升加载速度和运行效率。
实际应用:
在开发环境中,使用 Vite 可以显著提高开发效率。通过即时热更新和按需编译,开发者可以立即看到代码修改的效果,减少等待时间。
在生产环境中,通过 Vite 的优化策略,可以生成高性能的构建结果。静态资源的优化处理和基于 Rollup 的构建方式,确保了应用的快速加载和高效运行,提升了用户体验。
综上所述,Vite 通过即时热更新、按需编译和依赖预构建等技术,在开发环境中提供了快速的开发体验。同时,在生产环境中,Vite 利用静态资源优化、Rollup 构建和 Tree Shaking 等策略,生成高性能的构建结果,提升了应用的运行效率和用户体验。
基础概念:
响应式布局:
响应式布局是一种网页设计方法,使网页能够在不同设备和屏幕尺寸上自适应地调整布局和样式。其核心思想是通过流动网格、灵活图像和 CSS 媒体查询等技术,使网页在各种设备上显示效果一致,用户体验良好。
特点:
响应式布局具有以下特点:
实现方式:
流动网格(Fluid Grid):
使用百分比而不是固定像素来定义布局的宽度,使布局能够根据屏幕大小进行调整。例如:
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
.column {
float: left;
width: 50%; /* 使用百分比定义宽度 */
}
灵活图像(Flexible Images):
使用 CSS 设置图像的最大宽度为 100%,使图像能够根据容器的宽度自适应调整。例如:
img {
max-width: 100%;
height: auto;
}
媒体查询(Media Queries):
使用 CSS3 媒体查询,根据设备的不同特性(如宽度、高度、分辨率等)应用不同的样式。例如:
@media (max-width: 768px) {
.column {
width: 100%; /* 在小屏幕设备上,列宽度为 100% */
}
}
@media (min-width: 769px) {
.column {
width: 50%; /* 在大屏幕设备上,列宽度为 50% */
}
}
实际应用:
在开发过程中,通过响应式布局可以确保网页在各种设备(如手机、平板、桌面电脑)上都能提供良好的用户体验。例如,使用流行的前端框架(如 Bootstrap、Tailwind CSS 等),可以快速实现响应式布局,提升开发效率和代码复用性。
实际项目中,响应式布局的应用场景包括但不限于:
综上所述,响应式布局是一种重要的网页设计技术,通过流动网格、灵活图像和 CSS 媒体查询等方法,实现网页在不同设备上的自适应布局。理解和掌握响应式布局技术,可以显著提升网页的用户体验和跨设备兼容性。
基本概念:
媒体查询:
媒体查询是 CSS3 的一个特性,通过 @media
规则定义,根据设备的不同特性应用不同的样式,从而实现响应式设计。
语法:
媒体查询的基本语法包括媒体类型和一个或多个媒体特性。例如,根据屏幕宽度调整样式:
@media screen and (max-width: 768px) {
/* 在屏幕宽度不超过 768px 时应用的样式 */
.container {
width: 100%;
}
}
应用场景:
响应式布局:
使用媒体查询根据设备的屏幕大小调整布局,使网页在各种设备上都能良好展示。例如,在大屏幕设备上使用多列布局,在小屏幕设备上使用单列布局:
@media (min-width: 769px) {
.column {
width: 50%;
}
}
@media (max-width: 768px) {
.column {
width: 100%;
}
}
按需加载样式:
在不同设备上加载不同的样式文件,减少不必要的资源加载。例如,为移动设备加载轻量级的样式:
<link rel="stylesheet" media="screen and (max-width: 768px)" href="mobile.css">
<link rel="stylesheet" media="screen and (min-width: 769px)" href="desktop.css">
设备特性优化:
针对高分辨率屏幕、触摸屏等设备特性优化用户体验。例如,为 Retina 屏幕加载高分辨率的图片:
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.logo {
background-image: url('[email protected]');
}
}
实际应用:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Designtitle>
<style>
body {
font-family: Arial, sans-serif;
}
.container {
width: 80%;
margin: 0 auto;
}
.header, .footer {
background-color: #f1f1f1;
text-align: center;
padding: 1em 0;
}
.main {
display: flex;
flex-wrap: wrap;
}
.column {
flex: 1;
padding: 1em;
}
/* 大屏幕设备样式 */
@media (min-width: 769px) {
.column {
width: 50%;
}
}
/* 小屏幕设备样式 */
@media (max-width: 768px) {
.column {
width: 100%;
}
}
style>
head>
<body>
<div class="container">
<div class="header">Headerdiv>
<div class="main">
<div class="column">Column 1div>
<div class="column">Column 2div>
div>
<div class="footer">Footerdiv>
div>
body>
html>
在以上示例中,我们通过媒体查询实现了响应式布局:
通过媒体查询,可以根据不同设备的特性调整样式,使网页在各种设备上都能提供良好的用户体验。结合其他响应式技术(如流动网格、灵活图像等),可以进一步提升页面的跨设备兼容性和用户体验。
React:
Vue:
v-bind
、v-if
)来绑定数据和控制视图。模板语法:
const element = Hello, world!
;
<template>
<h1>{{ message }}h1>
template>
<script>
export default {
data() {
return {
message: 'Hello, world!'
};
}
};
script>
数据绑定:
v-model
指令可以实现数据和视图的双向绑定。组件体系:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return {this.state.count};
}
}
.vue
文件)。<template>
<div>{{ count }}div>
template>
<script>
export default {
data() {
return {
count: 0
};
}
};
script>
<style scoped>
div {
color: red;
}
style>
状态管理:
useState
和 useReducer
等 Hooks 管理状态。对于复杂的状态管理,可以使用外部库(如 Redux、MobX)。const [count, setCount] = useState(0);
reactive
和 ref
等响应式 API。Vuex 是 Vue 官方的状态管理库,用于集中式管理应用状态。const state = reactive({ count: 0 });
路由和生态系统:
React:
Vue:
综上所述,React 和 Vue 各有优点,选择合适的框架应根据项目需求和团队技术栈进行决定。
通过以上解释,可以清晰地理解进程和线程的区别,以及它们在操作系统和并发编程中的应用场景和特点。
通过以上解释,可以清晰地理解进程和线程的区别,以及它们在操作系统和并发编程中的应用场景和特点。
基本概念
快速排序是一种常见的排序算法,基于分治法的思想。其基本思想是选择一个基准元素,将数组分成两个子数组,小于基准元素的放在左边,大于基准元素的放在右边,然后对子数组进行递归排序。
算法步骤
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
const pivot = arr[0];
const left = [];
const right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
const arr = [5, 3, 7, 2, 8, 4, 1];
const sortedArr = quickSort(arr);
console.log(sortedArr); // 输出:[1, 2, 3, 4, 5, 7, 8]
1. 基本概念
二分查找算法是一种高效的查找算法,适用于有序数组。其基本思想是通过每一次比较将查找范围缩小一半,从而快速定位目标值。
2. 算法步骤
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const midValue = arr[mid];
if (midValue === target) {
return mid; // 找到目标值,返回索引
} else if (midValue < target) {
left = mid + 1; // 目标值在右半边
} else {
right = mid - 1; // 目标值在左半边
}
}
return -1; // 未找到目标值
}
// 示例
const array = [1, 3, 5, 7, 9, 11, 13, 15];
const target = 9;
const index = binarySearch(array, target);
console.log(index); // 输出:4
3. 应用场景
二分查找算法适用于有序数组,特别是在静态数据集中查找目标值的场景。例如,在排序好的数组中查找某个元素的位置,或者判断某个元素是否存在于数组中。