JS函数和对象全解析
一、垃圾回收机制
JS作为高级语言,无需开发者手动分配和释放内存,核心依赖“垃圾回收机制(GC)”自动清理不再使用的内存空间,避免内存泄漏。
1. 核心原则
垃圾回收的核心是“识别并回收不可达对象”——当一个对象无法通过任何引用链访问到时,就会被标记为“垃圾”,等待GC清理。
2. 两种常见回收算法
标记-清除算法(主流):这是现代浏览器最常用的算法,分两步执行:
- 标记阶段:GC从全局对象(如window、global)出发,遍历所有可访问的对象并做标记;
- 清除阶段:遍历内存中所有对象,清除未被标记的对象,释放其占用的内存。
引用计数算法(淘汰):通过统计对象被引用的次数判断是否回收——引用次数为0则标记为垃圾。但存在致命缺陷:循环引用时(A引用B,B引用A,且两者均无其他引用),引用次数永远不为0,导致内存泄漏。
3. 实用注意事项
虽然GC自动执行,但不良代码仍可能导致内存泄漏,需注意:
- 避免意外全局变量:未声明的变量会挂载到window上,长期无法回收,建议用let/const替代var,减少全局变量使用;
- 及时清除无用引用:如DOM元素删除后,需清空对应的JS引用(例:elem = null);
- 慎用闭包:闭包会保留外层函数的变量引用,过度使用可能导致内存占用过高(下文详细说)。
二、闭包
闭包是JS的核心特性,也是面试高频考点,理解它能轻松实现模块化、状态保存等功能。
1. 定义与本质
当一个函数嵌套另一个函数,且内层函数引用了外层函数的变量/参数,同时内层函数被外部访问时,就形成了闭包。本质是“内层函数携带了外层函数的作用域上下文”,让外层变量在函数执行结束后仍不被回收。
2. 经典案例
实现计数器(保存状态,避免全局污染)
function createCounter() {
let count = 0; // 外层函数变量,被内层引用
return function() {
count++; // 内层函数访问外层变量
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3解析:createCounter执行后,按常理count会被GC回收,但因内层函数被counter引用,闭包保留了count的引用,使其能持续累加,且count无法被外部直接修改,实现了“私有变量”的效果。
3. 应用场景与坑点
常用场景:
- 模块化封装:隐藏内部变量,只暴露需要的方法(类似JS模块的雏形);
- 延迟执行:如定时器、事件回调中访问外层变量;
- 状态缓存:如上述计数器、防抖节流中的状态保存。
注意坑点:
闭包会延长外层变量的生命周期,过度使用会增加内存负担,甚至导致内存泄漏。建议:仅在需要保存状态/封装变量时使用,使用完毕后可通过“清空引用”(如counter = null)释放内存。
三、函数参数
JS函数参数看似简单,实则有诸多进阶技巧,能大幅提升代码灵活性,覆盖默认值、剩余参数、解构传参等场景。
1. 默认参数(ES6+)
ES6前需通过逻辑判断设置默认值(如param = param || 'default'),但存在缺陷(若参数为0、false等假值,会被误判为无参)。ES6新增默认参数语法,简洁且精准:
// 基础用法
function greet(name = 'Guest', age = 18) {
console.log(`Hello ${name}, you are ${age} years old`);
}
greet(); // Hello Guest, you are 18 years old
greet('Tom'); // Hello Tom, you are 18 years old
// 注意:默认参数需放在参数列表末尾
function fn(a, b = 2) {} // 合法
function fn(b = 2, a) {} // 不推荐,易混淆2. 剩余参数(…rest)
当函数参数数量不确定时,用剩余参数(…+变量名)接收多余参数,返回一个数组(区别于arguments的类数组),便于遍历操作:
// 求和函数(支持任意数量参数)
function sum(...nums) {
return nums.reduce((total, num) => total + num, 0);
}
console.log(sum(1,2,3)); // 6
console.log(sum(4,5,6,7)); // 22
// 剩余参数与普通参数结合(剩余参数必须在最后)
function fn(a, b, ...rest) {
console.log(a, b); // 1 2
console.log(rest); // [3,4,5]
}3. 参数解构(ES6+)
当参数为对象/数组时,可通过解构直接提取属性/元素,简化代码:
// 对象解构传参
function getUserInfo({ name, age, gender = 'male' }) {
console.log(`姓名:${name},年龄:${age},性别:${gender}`);
}
getUserInfo({ name: 'Tom', age: 20 }); // 姓名:Tom,年龄:20,性别:male
// 数组解构传参
function getSum([a, b, c]) {
return a + b + c;
}
console.log(getSum([1,2,3])); // 6四、ES6箭头函数:简洁与this的重塑
箭头函数是ES6最常用的语法糖,不仅简化函数写法,更重塑了this的指向规则,解决了传统函数this指向混乱的问题。
1. 基础语法
// 无参数,单语句
const fn1 = () => console.log('Hello');
// 单参数,单语句(可省略括号)
const fn2 = num => num * 2;
// 多参数,多语句(需加括号和大括号,return不可省略)
const fn3 = (a, b) => {
const total = a + b;
return total;
};
// 返回对象(需加小括号,避免大括号被解析为代码块)
const fn4 = () => ({ name: 'Tom', age: 20 });2. 核心特性:this指向
传统函数(普通函数、构造函数)的this指向“调用者”,而箭头函数没有自己的this,其this继承自外层执行上下文的this,且一旦绑定无法修改(call、apply、bind也无效)。
// 传统函数this指向问题
const obj = {
name: 'Tom',
sayHi: function() {
setTimeout(function() {
console.log(this.name); // undefined(this指向window)
}, 1000);
}
};
// 箭头函数解决this问题
const obj2 = {
name: 'Tom',
sayHi: function() {
setTimeout(() => {
console.log(this.name); // Tom(this继承自sayHi的this,即obj2)
}, 1000);
}
};3. 适用与不适用场景
适用场景:
- 回调函数(定时器、数组方法forEach/map等),避免this指向混乱;
- 简单的工具函数(无复杂this需求),简化语法。
不适用场景:
- 构造函数:箭头函数无法作为构造函数(无prototype,不能用new关键字);
- 对象方法:若方法需访问对象自身的this,建议用普通函数;
- 需要arguments对象的场景:箭头函数没有arguments,需用剩余参数替代。
到此这篇关于JS函数和对象全解析的文章就介绍到这了,更多相关js函数和对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


最新评论