【面试进阶】JavaScript 函数与对象进阶知识总结(重难点+记忆模板)
本文面向前端面试,系统总结 JS 函数与对象的高级知识点,涵盖 arguments、柯里化、纯函数、严格模式、属性描述符等重点内容,附记忆口诀与典型面试题,助你快速突破 JS 核心考点。
一、函数对象的属性
JavaScript 函数本质上是对象,可以有属性
| 属性 | 说明 |
|---|---|
| name | 函数名称 |
| length | 函数参数长度(不含 rest 参数) |
记忆点:
length !== arguments.length,rest 参数不计算在 length 内
二、arguments 与 Rest 参数
1)arguments 特性
| 特点 | 说明 |
|---|---|
| 类数组对象 | 有 length、有索引,无 array 方法 |
| 一般用于 ES5 函数内部 | function fn(){} 才能获取 |
| 不推荐 ES6 及以后使用 | rest 参数更优雅 |
转 Array 方法
Array.from(arguments) [...arguments]2)箭头函数无 arguments
箭头函数 不会创建 arguments,会向上查找作用域。
const fn = () => console.log(arguments) // ❌3)Rest 参数 ✅ 推荐
function sum(...args){ console.log(args); }| 维度 | arguments | rest |
|---|---|---|
| 类型 | 类数组 | 真数组 ✅ |
| 箭头函数 | ❌ | ✅ |
| 建议程度 | 不推荐 | 强烈推荐 ✅ |
三、纯函数(React/函数式编程核心)
定义(必须同时满足)
| 条件 | 含义 |
|---|---|
| 固定输入 → 固定输出 | 输出只由输入决定 |
| 无副作用 | 不修改外部变量,不产生 I/O 影响 |
副作用示例
| ✅ 纯函数 | ❌ 非纯函数 |
|---|---|
slice() | splice() |
map() | 改动外部变量 |
| 返回新对象 | 修改全局/参数 |
纯函数为什么重要?
可测试性高
结果可预测
React reducer 必须是纯函数(重要面试点)
四、柯里化(Currying)
定义:将多参函数转为链式调用,每次接收一个参数。
const add = a => b => c => a + b + c;为什么需要柯里化?
| 好处 | 场景 |
|---|---|
| 参数复用 | logger('INFO')('msg') |
| 拆分逻辑 | 单一职责 |
| 延迟执行 | fn(a)(b)(c) |
自动柯里化(掌握写法=面试加分)
function curry(fn) { return function curried(...args) { if(args.length >= fn.length) return fn(...args) return (...rest) => curried(...args, ...rest) }
}五、组合函数(Compose)
定义:让多个函数依次执行,类似链式函数管道。
const compose = (f, g) => x => f(g(x));前端框架应用:Redux、函数式工具库(Ramda、lodash/fp)
六、with & eval(面试常问:为什么不用?)
with 不推荐使用
修改作用域链 → 难定位变量来源 → 性能差 / 易错
eval 不推荐使用
| 问题 | 原因 |
|---|---|
| 安全风险 | 可注入攻击 |
| 性能差 | 无法被 JS 引擎优化 |
| 可读性差 | 难调试 |
面试答案:with 和 eval 都破坏作用域、风险大、性能差,因此禁用
七、严格模式(use strict)
开启方式
"use strict"; // 文件或函数顶部作用
| 优点 | 说明 |
|---|---|
| 异常更明确 | 抛出错误避免 silent fail |
| 禁止意外全局变量 | x = 10 // error |
| this 不自动绑定 window | 函数 this = undefined |
| 禁止 with / 限制 eval | 更安全 |
默认开启位置
classmodule
面试口诀:“严格模式 = 更安全 + 更规范 + 更利于引擎优化”
对象增强:Object.defineProperty & 属性描述符
一、defineProperty 基本用法
Object.defineProperty(
obj,
"name",
{ value: "coder", writable: false, enumerable: false, configurable: false }
)二、属性描述符分类
1)数据属性(value)
| 属性 | 说明 | 默认值 |
|---|---|---|
| value | 属性值 | undefined |
| writable | 是否可修改 | false |
| enumerable | 是否可枚举 | false |
| configurable | 是否可配置 | false |
普通赋值方式默认全部为 true
2)存取属性(get/set)
Object.defineProperty(obj,"age",{ get(){...}, set(val){...} })| 特性 | 说明 |
|---|---|
| get | 获取属性触发 |
| set | 设置属性触发 |
面试点:不能同时定义 value/writable 与 get/set
三、defineProperties(批量)
Object.defineProperties(obj, { name:{value:'a'}, age:{value:18} })四、对象操作方法对比
| 方法 | 作用 |
|---|---|
| getOwnPropertyDescriptor | 获取单个属性描述符 |
| getOwnPropertyDescriptors | 获取全部属性描述符 |
| preventExtensions | 禁止添加新属性 |
| seal | 禁止新增/删除属性 |
| freeze | seal + 不允许修改属性值 |
面试速记清单 ✅
| 主题 | 记忆点 |
|---|---|
| arguments vs rest | rest 真数组 ✅ arguments 旧时代 ❌ |
| 纯函数 | 确定输入 → 确定输出,无副作用 |
| 副作用例子 | 修改外部变量、I/O、DOM、Math.random |
| 柯里化 | fn(a)(b)(c),参数复用+函数拆分 |
| compose | 函数组合执行 pipeline |
| 严格模式 | 禁全局变量、禁 with、优化引擎 |
| 属性描述符 | value/writable/enumerable/configurable |
| 存取属性 | get/set,不与 value 同时存在 |
| freeze/seal/preventExtensions | 限制对象可扩展性层级 |
JavaScript 函数 & 对象高频面试 标准答案模板(可直接背诵)
1)arguments vs rest 参数
标准回答:
arguments是类数组对象,用于获取所有实参,但缺乏数组方法,且箭头函数无法访问。
ES6 引入rest 参数(...args),是真数组、更语义化、更推荐使用。
总结口诀:
能用 rest,不用 arguments。
2)什么是纯函数?为什么重要?
标准回答:
纯函数是指给定相同输入永远返回相同输出,且无副作用的函数。
它不修改外部状态,不依赖全局环境。
在 React/Redux 中,reducer 必须是纯函数以保证可预测性和可测试性。
副作用例子:
修改全局变量、DOM 操作、I/O、setTimeout、Date、Math.random
3)解释柯里化及应用场景
标准回答:
柯里化是将多参函数转为单参链式调用的技术,可复用参数、拆分逻辑、实现函数组合与延迟计算。
例:sum(1)(2)(3)
场景:日志封装、权限校验、定制请求器
4)compose 函数作用
标准回答:
compose 实现函数组合,将多个函数从右向左依次执行,常用于数据管道与 Redux 中间件构建。
5)为什么不推荐 with & eval
with修改词法作用域链 → 难维护 + 性能差
eval执行字符串为代码 → 安全风险 + 性能差 + 无法 JIT 优化
6)严格模式的意义
防止隐式全局变量
禁止 with
this 不再指向 window(函数中为 undefined)
更利于 JS 引擎优化
7)Object.defineProperty 的作用
自定义属性行为,包括可写性(writable)、可枚举性(enumerable)、可配置性(configurable),以及 getter/setter。
8)对象冻结与扩展性方法
| 方法 | 新增 | 删除 | 改值 |
|---|---|---|---|
| preventExtensions | ❌ | ✅ | ✅ |
| seal | ❌ | ❌ | ✅ |
| freeze | ❌ | ❌ | ❌ ✅只读 |
口诀:
freeze > seal > preventExtensions
✅ 二、手写代码大题答案(面试必考)
1)手写 call
Function.prototype.myCall = function(ctx, ...args) {ctx = ctx || window;const key = Symbol();ctx[key] = this;const res = ctx[key](...args);delete ctx[key];return res;
}
2)手写 bind
Function.prototype.myBind = function(ctx, ...args) {const fn = this;return function(...rest) {return fn.apply(ctx, [...args, ...rest]);};
}
3)手写柯里化 curry
function curry(fn){return function curried(...args){if(args.length >= fn.length) return fn(...args);return (...rest) => curried(...args, ...rest);}
}
4)compose 函数
const compose = (...fns) => arg =>fns.reduceRight((prev, fn) => fn(prev), arg);
