基础 | 🔥6种声明方式全解⚠️

一、ES6 的6种变量声明方式(含实战陷阱与追问链)

ES6(ECMAScript 2015)带来了语言层面的巨大升级,其中变量声明机制 是开发者最直观感受到的变革。它不再局限于 var,而是引入了更安全、更语义化的多种声明方式。

我们先看一张核心对比图:

graph LR A[变量声明] --> B[var] A --> C[let] A --> D[const] A --> E[function] A --> F[class] A --> G[import] style B fill:#f96,stroke:#333 style C fill:#6f9,stroke:#333 style D fill:#69f,stroke:#333 style E fill:#ff6,stroke:#333 style F fill:#6ff,stroke:#333 style G fill:#f6f,stroke:#333

⚡图:ES6 六大声明方式全景图

⚠️这一步暗藏变量提升陷阱?

1. var ------ 被时代淘汰但仍在运行时存在

  • 函数作用域
  • 存在变量提升(hoisting)
  • 可重复声明
  • 不推荐在现代项目中使用
js 复制代码
console.log(a); // undefined(不是报错!)
var a = 1;

2. let ------ 块级作用域新标准

  • 块级作用域({} 内有效)
  • 不存在"暂时性死区"外的访问
  • 不可重复声明
  • 推荐用于可变变量
js 复制代码
{
  let b = 2;
  console.log(b); // 2
}
console.log(b); // ReferenceError!

3. const ------ 常量声明(但≠不可变!)

  • 块级作用域
  • 必须初始化
  • 引用地址不可变,但对象属性可改
js 复制代码
const obj = { name: 'ES6' };
obj.name = 'Modern JS'; // ✅ 合法
obj = {}; // ❌ 报错:Assignment to constant variable.

4. function ------ 函数声明(也是ES6前就有的)

  • 提升且初始化
  • 仅在定义的作用域内有效
  • let/const 冲突时会抛错
js 复制代码
function foo() { return 1; }

5. class ------ 语法糖,本质仍是函数

  • 必须先定义后使用(TDZ)
  • 不会被提升
  • class 声明创建一个不可重新赋值的常量
js 复制代码
class Person {
  constructor(name) {
    this.name = name;
  }
}
const p = new Person('Tom');

6. import ------ 模块导入即声明

  • 静态分析,编译时绑定
  • 所有 import 自动提升到文件顶部
  • 绑定是只读引用,不是拷贝
js 复制代码
import { useState } from 'react'; // 声明了一个只读绑定

📌 主流共识:letconst 是现代 JS 开发的默认选择,优先使用 const,仅当需要重新赋值时用 let


「Q1: varlet 最大区别是什么?💥」

A1: var 是函数作用域且会变量提升并初始化为 undefinedlet 是块级作用域,存在暂时性死区(TDZ),在声明前访问会报错。

「Q2: const 定义的对象为什么还能修改属性?🤯」

A2: const 保证的是引用地址不变 ,而非对象内部不可变。要真正冻结对象需用 Object.freeze(obj)

「Q3: import 是不是也受 TDZ 影响?」

A3: 是的!虽然 import 会被提升,但在模块执行前无法访问,属于 TDZ 范畴。比如动态 import() 可以规避。

「Q4: class 声明会不会被提升?」

A4: class 声明存在 TDZ,不会被提升。typeof MyClass 在声明前会报错,不像 function 可以提前使用。

「Q5: 为什么 ES6 不直接废弃 var?」

A5: 为了向后兼容。大量旧代码依赖 var 行为,直接移除会导致生态崩溃。但规范鼓励使用 let/const


二、CSS 元素垂直居中的 7 种方式

垂直居中是前端永恒话题。我们先上一张布局方式演进图:

sql 复制代码
+------------------+     +------------------+
|   display:table  |     | position + 负margin |
+------------------+     +------------------+
          |                        |
          v                        v
+------------------+     +------------------+
|  flex: center!!  |<--->|    grid layout   |
+------------------+     +------------------+
          |
          v
+------------------+
| transform:translate(-50%) |
+------------------+

⚡图:CSS 垂直居中方法演进路径

⚠️移动端慎用负 margin?

方法 1:Flexbox(推荐 ✅)

css 复制代码
.container {
  display: flex;
  justify-content: center; /* 水平 */
  align-items: center;     /* 垂直 */
  height: 100vh;
}

方法 2:Grid(现代方案)

css 复制代码
.container {
  display: grid;
  place-items: center; /* 水平+垂直 */
  height: 100vh;
}

方法 3:绝对定位 + transform

css 复制代码
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

方法 4:绝对定位 + margin auto

css 复制代码
.container {
  position: relative;
  height: 300px;
}
.child {
  position: absolute;
  top: 0; bottom: 0;
  left: 0; right: 0;
  margin: auto;
  width: 100px;
  height: 50px;
}

方法 5:table-cell(老派但兼容好)

css 复制代码
.container {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
  width: 300px;
  height: 300px;
}

方法 6:line-height(仅限单行文本)

css 复制代码
.box {
  height: 100px;
  line-height: 100px;
  text-align: center;
}

方法 7:padding 调整(固定高度可用)

css 复制代码
.box {
  padding: 50px 0;
  height: 200px;
  box-sizing: border-box;
}

「Q1: Flex 居中会不会影响子元素布局?💥」

A1: 不会,align-itemsjustify-content 只控制主轴和交叉轴对齐,子元素仍可自由布局。

「Q2: transform: translate 会导致模糊?🤯」

A2: 是的!非整数像素位移可能引起亚像素渲染模糊。可加 will-change: transform 或强制 GPU 加速缓解。

「Q3: Grid 和 Flex 如何选?」

A3: Grid 适合二维布局(行列都复杂),Flex 适合一维(主轴方向)。居中场景两者皆可,优先 Flex。

「Q4: 为什么不用 vertical-align: middle?」

A4: 因为它只对 inlinetable-cell 元素有效,块级元素无效,常被误解。

「Q5: 移动端适配居中要注意什么?🌍」

A5: 避免固定高度,优先使用 Flex 或 Grid,结合 vh 单位,并处理 iOS 安全区(env() 函数)。


三、Flex 布局的三大伸缩属性详解(含权重计算)

Flex 布局核心在于三个伸缩属性:

classDiagram class FlexItem { +flex-grow: number +flex-shrink: number +flex-basis: length | auto } FlexItem : flex = grow shrink basis

⚡图:Flex 伸缩三属性关系

⚠️默认值组合有坑?

1. flex-grow:剩余空间分配权重

  • 默认 0
  • 数值越大,分得越多
  • 不会缩小已有内容
css 复制代码
.item1 { flex-grow: 1; }
.item2 { flex-grow: 2; } /* 分得两倍空间 */

2. flex-shrink:溢出时压缩比例

  • 默认 1
  • 设为 0 表示不压缩
  • 压缩量 = (自身大小 × shrink) / 总权重
css 复制代码
.item { flex-shrink: 0; } /* 固定宽度,不压缩 */

3. flex-basis:分配前的初始大小

  • 类似 width,但优先级更高
  • 可设为 auto(按内容)、0、具体值
css 复制代码
.item { flex-basis: 100px; }

简写 flex 的常见组合:

写法 等价于
flex: 1 1 1 0%1 1 0px(浏览器差异)
flex: auto 1 1 auto
flex: none 0 0 auto(不伸不缩)

💡 实战建议:用 flex: 1 快速填满剩余空间,flex: none 防止压缩。

「Q1: flex: 1 到底等于 1 1 0 还是 1 1 auto?💥」

A1: 规范定义为 1 1 0,但某些浏览器实现为 0%。为明确行为,建议写全。

「Q2: flex-basis: 0width: 0 一样吗?🤯」

A2: 不同!flex-basis 参与 flex 计算,width 在 flex 中可能被覆盖。flex-basis: 0 更适合均分。

「Q3: 多个 item 设置 flex-grow 如何计算?」

A3: 按权重比例分配剩余空间。如 grow=1 和 grow=2,则比例 1:2。

「Q4: flex-shrink 为负数会怎样?」

A4: 无效!flex-shrink 必须是非负数,负值会被忽略或转为 0。

「Q5: 为什么有时 flex: 1 不占满?」

A5: 可能父容器无固定宽度,或子元素有 min-width 限制。检查 min-width: auto 是否干扰。


四、LeetCode 开平方根题(第69题)------ 二分法 vs 牛顿法

题目:实现 int sqrt(int x),返回 √x 的整数部分。

例:输入 8 → 输出 2(√8 ≈ 2.828)

方法 1:二分查找(推荐 ✅)

js 复制代码
function mySqrt(x) {
  if (x < 2) return x;
  let left = 1, right = Math.floor(x / 2);
  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    const square = mid * mid;
    if (square === x) return mid;
    if (square < x) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }
  return right; // 最大满足 mid*mid <= x 的值
}

方法 2:牛顿迭代法(更快收敛)

js 复制代码
function mySqrt(x) {
  if (x < 2) return x;
  let r = x;
  while (r * r > x) {
    r = Math.floor((r + x / r) / 2); // 牛顿公式: r = (r + x/r)/2
  }
  return r;
}

时间复杂度:二分 O(log x),牛顿法接近 O(log log x)

「Q1: 为什么 right = x / 2?💥」

A1: 因为当 x ≥ 4 时,√x ≤ x/2。边界优化可减少搜索范围。

「Q2: 牛顿法会不会死循环?🤯」

A2: 不会,因为每次迭代都更接近真实值,且整数除法会收敛。但需用 Math.floor 控制精度。

「Q3: 能不能用 Math.sqrt?」

A3: 面试中通常要求手动实现。若允许,直接 Math.floor(Math.sqrt(x))

「Q4: 浮点数开方怎么做?」

A4: 扩展牛顿法,设置精度阈值(如 1e-6),直到 |r*r - x| < eps

「Q5: 大数溢出怎么处理?」

A5: 用 BigInt 或将比较改为 mid <= x / mid 避免乘法溢出。

相关推荐
A5rZ21 分钟前
缓存投毒进阶 -- justctf 2025 Busy Traffic
前端·javascript·缓存
未来之窗软件服务1 小时前
浏览器CEFSharp133+X86+win7 之多页面展示(三)
前端·javascript·浏览器开发·东方仙盟
胡斌附体1 小时前
elementui cascader 远程加载请求使用 选择单项等
前端·javascript·elementui·cascader·可独立选中单节点
烛阴1 小时前
Vector Normaliztion -- 向量归一化
前端·webgl
追梦人物3 小时前
Uniswap 手续费和协议费机制剖析
前端·后端·区块链
小沈同学呀3 小时前
阿里巴巴高级Java工程师面试算法真题解析:LRU Cache实现
java·算法·面试
朱程5 小时前
AI 编程时代手工匠人代码打造 React 项目实战(四):使用路由参数 & mock 接口数据
前端
PineappleCoder5 小时前
深入浅出React状态提升:告别组件间的"鸡同鸭讲"!
前端·react.js
wycode5 小时前
Vue2源码笔记(1)编译时-模板代码如何生效之生成AST树
前端·vue.js