JavaScript 高频面试题精讲:var、let、const 与类型系统全解析

🔍var,let,const 有什么区别

🛠️ var 的核心特性

bash 复制代码
#️⃣ 作用域:函数作用域 或 全局作用域  
#️⃣ 变量提升:声明被提升到全局作用域的顶部,初始化为undefined  
#️⃣ 重复声明:允许重复声明,可能会导致覆盖  
#️⃣ 可变性:可以重新

⚠️var的痛点

  1. 变量提升
  • 在声明前访问,可以在一个变量被正式声明之前就访问到他,不会报错,值为undefined。、
javascript 复制代码
// 🚨 违反直觉的变量提升
console.log(b)  // undefined → 不会报错但值异常
var b = 10
  1. 缺乏块级作用域
  • if 块内的变量会'泄露'到外部
  • for 循环中的变量会'泄露'到外部
js 复制代码
function varExample(){
    // console.log(b)  // undefined
    
    var b = 10
    console.log(b)  // 输出 10
    
    if(true){
        var b = 20    // 覆盖了外层的 a
        console.log(b)   // 输出 20
     }
     
     console.log(b)  // 值被 if 块修改,输出 20
}
csharp 复制代码
为了解决 var 的这些遗留问题, ES6 带来了两个全新的变量声明关键字: let 和 const ,它们从根本上改变了 javascript 中变量声明的规则。

✨ let 的优势

shell 复制代码
#️⃣ 块级作用域:if/for等代码块内有效  
#️⃣ 暂时性死区(TDZ):声明前访问会报错  
#️⃣ 禁止重复声明:同一作用域内不可重复定义  
#️⃣ 可变性:允许重新
js 复制代码
function letExample(){
    // console.log(b)  // ReferenceError: TDZ
    
    let b = 10
    console.log(b)  // 输出 10
    
    if(true){
        let b = 20    // 这是 if 块内的新变量 
        console.log(b)   // 输出 20
     }
     
     console.log(b)  // 不受 if 块影响,输出 10
}

🔒 const 的特性

  • 作用域:块级作用域
  • 暂时性死区:声明前无法访问
  • 可变性:不能重新赋值(Must be initialized)
  • 核心:保证变量**引用的不变性 **

const 与引用类型

  1. 允许修改内部

const 保证的是 object 这个变量永远会指向这个地址,不关心地址上存放的数据内容本身。

js 复制代码
const obj = { name: ' React '}
//  允许修改对象内部的属性
obj.name = ' vue '
console.log(obj.name)  // 'vue'

//  不能将 obj 指向一个新对象
obj = {}  // TypeError

关键点1:暂时性死区(TDZ)

  • 适用于 let 和 const
  • 区域:从作用域 { 开始,到变量声明 let/const 结束
  • 行为:在此区域内访问变量,抛出 ReferenceError

关键点2:全局对象

  • var:在全局作用域声明,会成为 window 的属性
js 复制代码
var globalVar = 'var'
console.log(window.globalVar) // 'var'
  • let/const:不会成为 window 的属性
js 复制代码
var globalLetr = 'let'
console.log(window.globalLet) // undefined

关键点3:开发最佳实践

  1. 优先使用 const
  • 增强代码可预测性、健壮性,编码意外修改
  1. 当变量需要重新赋值时,使用 let
  • 例如:循环计数器、状态切换
  1. 告别var
  • 在现代 JS 项目中,应避免使用 var

面试:请谈谈 var,let,const 的区别

1.最主要的区别在于作用域规则不同

  • var:函数作用域
  • let/const:块级作用域
    • 块级作用域的引入解决了 var 在 if/for 中变量泄露的问题
  1. 提升与 TDZ
  • var: 变量提升
    • 声明前可访问,值为 undefined
  • let/const:存在暂时性死区(TDZ)
    • 声明前访问,直接抛出 ReferenceError
  1. 赋值与声明
  • var:可重复声明,可重新赋值
  • let:不可重复声明,可重新赋值
  • const:不可重复声明,不可重新赋值
    • 加分点:解释 const 对引用类型(对象/数组)的含义

数据类型和 typeof 的陷阱

JavaScript 数据类型:两大阵营:原始类型和对象类型

  • 7 种原始类型
    • string,number,boolean,null,undefined,symbol,bigint
  • 1 种对象类型
    • object(包含Array,Function等)

🧐 typeof 的陷阱与类型检测

  • 一个一元操作符
  • 返回操作数类型的字符串表示
js 复制代码
typeof 'hello'  // "string"
typeof 123   // "number"
typeof true   // "boolean"
typeof Symbol('id')   // "symbol"
typeof 123n   // "bigint"
typeof undefined   // "undefined"

// 对象与函数
typeof {a:1}   // "object"
typeof [1,2,3]   // "object"
typeof new Date()   // "object"
typeof function() {}   // "function"(特殊)

陷阱1:typeof null console.log(typeof null) // "object"

  • 是一个历史bug
  • JS 早期实现中,null 的底层表示是 NULL 指针(0x00)
  • 对象的类型标签恰好也是0
  • typeof 错误地将其识别为对象

陷阱2:无法区分具体对象类型

js 复制代码
console.log(typeof [])  // "object"
console.log(typeof {})  // "object"
  • typeof 无法分辨 Array 和 Object

如何判断 null

  • 使用严格相等运算法 ===

如何判断数组 Array

  • 使用 Array.isArray()

类型判断的终极武器

  • 如果想判断的类型更复杂呢,比如区分普通对象还是日期对象还是正则表达式
  • Object.prototype.toString.call()
js 复制代码
Object.prototype.toString.call(null)  // "[object Null]"
Object.prototype.toString.call([)  // "[object Array]"
Object.prototype.toString.call({})  // "[object Object]"
Object.prototype.toString.call('')  // "[object String]"
Object.prototype.toString.call(123)  // "[object Number]"

typeof 有一个特点/优点:对于一个根本没有声明过的变量,使用 typeof 程序是不会报错的,在某些场景下,可以用typeof 安全的检查某个全局变量是否存在。

js 复制代码
console.log(typeof a)  // "undefined"
console.log(a)  // "Uncaught ReferenceError"

💾 值类型 vs 引用类型

  • let a = b; 为何有时同步变化,有时又各自独立
  • 揭秘 JavaScript 函数传参的底层真相
  • const 声明的对象,为何还能被修改

JavaScript 是如何在内存中存储数据的

从内存的角度来看,JavaScript 中所有的数据都会被归为两大类

bash 复制代码
#️⃣ 值类型:直接存储值 → 栈内存  
#️⃣ 引用类型:存储内存地址 → 堆内

📜 函数参数传递

  • JavaScript 永远是 按值传递
  • 值类型:传递 值的副本
  • 引用类型:传递 引用的副本
js 复制代码
 function modify(obj){
     // 修改属性会影响外部,因为都是一个内存
     obj.name = 'Modified'
     // 重新赋值,不会影响外部
     obj = {name:'New Object'}
 }
 let myObj = {name:'Initial'}
 modify(myObj)
 console.log(muObj.name) //  'Modified'

obj.name = 'Modified'

  • 函数内的 obj 和外部的 myObj 指向同一个地址
  • 通过该地址修改堆内存中的对象,会影响外部

obj = {name:'New Object'}

  • 将函数内的 obj 变量指向一个新的内存地址
  • 这与 myObj 断开了连接,后续修改互不相干

⚖️ 相等性判断

  • 值类型:比较 值 是否相等
  • 引用类型:比较 引用/地址 是否相等
ini 复制代码
100 === 100            // ✅ true
{} === {}              // ❌ false(不同地址)
const a = {}
const b = a
a === b                // ✅ true(相同地址)
相关推荐
月阳羊2 小时前
【硬件-笔试面试题】硬件/电子工程师,笔试面试题-26,(知识点:硬件电路的调试方法:信号追踪,替换,分段调试)
笔记·嵌入式硬件·面试·职场和发展
爷_2 小时前
字节跳动震撼开源Coze平台!手把手教你本地搭建AI智能体开发环境
前端·人工智能·后端
lemonth3 小时前
个人发展之路
面试
charlee443 小时前
行业思考:不是前端不行,是只会前端不行
前端·ai
Amodoro4 小时前
nuxt更改页面渲染的html,去除自定义属性、
前端·html·nuxt3·nuxt2·nuxtjs
Wcowin4 小时前
Mkdocs相关插件推荐(原创+合作)
前端·mkdocs
伍哥的传说5 小时前
CSS+JavaScript 禁用浏览器复制功能的几种方法
前端·javascript·css·vue.js·vue·css3·禁用浏览器复制
lichenyang4535 小时前
Axios封装以及添加拦截器
前端·javascript·react.js·typescript
Trust yourself2435 小时前
想把一个easyui的表格<th>改成下拉怎么做
前端·深度学习·easyui
三口吃掉你5 小时前
Web服务器(Tomcat、项目部署)
服务器·前端·tomcat