JavaScript 基础语法下篇:函数与对象核心要点深度解析
(2025–2026 面试/生产视角,最常被问、最容易混淆的部分)
一、函数部分(最核心、最常考)
1. 函数创建方式对比(面试必问)
| 创建方式 | 语法示例 | 函数提升(Hoisting) | this 绑定时机 | arguments 对象 | 推荐场景 | 现代趋势(2025+) |
|---|---|---|---|---|---|---|
| 函数声明 | function fn(){} | 整体提升 | 调用时动态绑定 | 有 | 普通函数、需要提升的场景 | 较少单独使用 |
| 函数表达式(匿名) | const fn = function(){} | 只提升变量声明 | 调用时动态绑定 | 有 | 回调函数、立即执行函数 | 常见 |
| 命名函数表达式 | const fn = function abc(){} | 只提升变量声明 | 调用时动态绑定 | 有 | 调试栈追踪更有意义 | 推荐(调试友好) |
| 箭头函数 | const fn = () => {} | 无提升 | 词法绑定(外层this) | 无 | 回调、简洁写法、React hooks | 最常用 |
| Function 构造函数 | new Function('a','b','return a+b') | 无提升 | 全局对象 | 有 | 极少用(安全问题) | 几乎不用 |
| IIFE(立即执行函数表达式) | (function(){}()) / ( () => {} )() | — | — | — | 创建私有作用域(旧时代) | 现在少用(let/const+块级) |
箭头函数 vs 普通函数(最常被问的 8 点区别)
- this 绑定:箭头函数没有自己的 this,捕获外层上下文
- 不能用作构造函数(没有 prototype,不能 new)
- 没有 arguments 对象
- 没有 super / new.target
- 不能用作 Generator 函数(不能 yield)
- 不能改变 this(call/apply/bind 无效)
- 语法更简洁(单行可省 return 和 {})
- 不适合:需要动态 this 的地方(如事件处理、对象方法)
2. this 指向规则(2025 年面试 Top 1 问题)
| 调用方式 | this 指向 | 代码示例 |
|---|---|---|
| 普通函数调用 | 非严格模式 → window/globalThis 严格模式 → undefined | fn() |
| 对象方法调用 | 调用该方法的对象 | obj.method() → this = obj |
| 构造函数(new) | 新创建的实例对象 | new Person() → this = 新实例 |
| 显式绑定(call/apply/bind) | 第一个参数(若为 null/undefined → window/globalThis) | fn.call(obj) → this = obj |
| 箭头函数 | 外层作用域的 this(词法作用域) | 永远不看调用方式,只看定义位置 |
| 事件监听器(addEventListener) | 绑定事件的 DOM 元素(除非用箭头函数) | btn.onclick = fn → this = btn |
经典面试题变形(按出现频率排序):
- setTimeout 回调里的 this 为什么是 window?
- 为什么 class 中的普通方法要 bind(this)?
- 箭头函数写在 class 里作为方法会有什么问题?
- obj.fn() 和 fn = obj.fn; fn() 的 this 区别?
- 连续调用 .call().call() 最终 this 指向谁?
3. 闭包(Closure)高频考点总结
- 定义:函数 + 函数能访问到的词法环境(即使外层函数已销毁)
- 三大经典用途(背下来):
- 数据私有化(模块模式)
- 记忆函数(memoize)
- 延迟执行 / 回调函数保存状态
- 常见闭包写法(2025 年仍常用):
// 1. 经典计数器模块
function createCounter() {
let count = 0;
return {
increment: () => ++count,
getCount: () => count
};
}
// 2. 防抖(debounce)
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
闭包最常被问的两个问题:
- 下面代码输出什么?为什么?
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0);
}
// 输出 5 5 5 5 5
// 改成 let 就正常了(块级作用域)
// 或用闭包立即执行函数保存每次的 i
- 闭包会造成内存泄漏吗?(现代 V8 垃圾回收很强,但仍需注意循环引用)
二、对象部分(核心特性深度)
1. 对象创建方式对比(2025 年视角)
| 方式 | 语法示例 | prototype 链 | this 绑定 | 枚举性(for…in) | 性能 | 现代推荐度 |
|---|---|---|---|---|---|---|
| 对象字面量 | { name: "张三" } | Object.prototype | — | 可枚举 | 高 | ★★★★★ |
| Object.create() | Object.create(proto, descriptors) | 自定义原型 | — | 可控制 | 中 | ★★★★ |
| 构造函数 + new | new Person() | Person.prototype | 实例 | 可枚举 | 中 | ★★★ |
| class 语法糖 | class Person {} | Person.prototype | 实例 | 可枚举 | 中 | ★★★★★ |
| Object.assign() | 浅拷贝 | — | — | — | — | 浅拷贝工具 |
| 展开运算符 … | { ...obj, name: "李四" } | — | — | — | 高 | 最常用 |
2. 属性描述符(Property Descriptor)——面试高频
Object.defineProperty(obj, "key", {
value: 42, // 数据描述符
writable: true, // 是否可写
enumerable: false, // 是否 for...in 枚举
configurable: false // 是否可删除/重新定义
});
// 访问器描述符(最常用于 Vue2 响应式原理)
Object.defineProperty(obj, "fullName", {
get() { return this.first + " " + this.last; },
set(val) { /* 拆分赋值 */ },
enumerable: true,
configurable: true
});
高频问题:
enumerable: false的属性还能被访问吗?(能,只是 for…in / Object.keys 看不到)configurable: false之后还能改 writable 吗?(部分可以,严格模式更严格)Object.freeze()、Object.seal()、Object.preventExtensions()区别?
3. 原型链与继承(2025 年仍重要)
// 现代最推荐:组合寄生式继承(class + extends)
class Animal {
constructor(name) { this.name = name; }
speak() { console.log(`${this.name} makes a sound.`); }
}
class Dog extends Animal {
constructor(name) { super(name); }
speak() { console.log(`${this.name} barks.`); }
}
经典面试题:
obj.__proto__和Object.getPrototypeOf(obj)区别?Function.__proto__是什么?(Function.prototype)- 原型链终点是什么?(null)
- hasOwnProperty vs in 操作符区别?
三、2025–2026 高频面试题 Top 15(建议准备)
- 箭头函数和普通函数的 this 区别?举 3 个场景说明箭头函数不适合的情况。
- 实现一个简易的 call/apply/bind。
- 手写防抖/节流(考虑 this 和 参数透传)。
- 闭包在实际项目中的 3 个典型应用。
- class 中的 static、public、private、#private 区别(# 是真正私有)。
- Object.create(null) 有什么用?(纯字典对象)
- 浅拷贝 vs 深拷贝有哪些实现方式?(JSON.parse(JSON.stringify) 的局限性)
- for…in / for…of / Object.keys / Object.getOwnPropertyNames 区别?
- new 一个函数时发生了哪 4 件事?
- 为什么说 JS 是基于原型的语言而不是基于类的?
需要针对上面某个题目写详细代码解析、或者想看“this 指向终极口诀”、或者“现代 JS 对象新特性(Proxy、Reflect、Symbol)”的补充,都可以直接告诉我~