Day20 JavaScript 进阶核心:IIFE、代码规范、调试与对象模型
接续上文:Day19(前端:JavaScript基础阶段)-CSDN博客
点关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!!
主页:一位搞嵌入式的 genius-CSDN博客
系列文章专栏:
https://blog.csdn.net/m0_73589512/category_13011829.html
目录
Day20 JavaScript 进阶核心:IIFE、代码规范、调试与对象模型
一、立即执行函数(IIFE):独立作用域的 “隔离神器”
1.1 IIFE 的基本语法与原理
形式 1:括号包裹函数表达式(推荐)
形式 2:函数表达式前加运算符(了解即可)
1.2 IIFE 的关键注意事项
(1)分号的必要性
(2)作用域隔离特性
1.3 IIFE 的经典应用场景
场景 1:解决全局变量命名冲突
场景 2:处理 DOM 事件监听的循环变量问题
二、JavaScript 代码规范:写出可维护的 “优雅代码”
2.1 基础格式规范
(1)空格与缩进
(2)分号与空行
(3)单行代码长度
2.2 命名规范
(1)变量与函数
(2)常量
2.3 模板字符串(ES6 特性)
(1)多行字符串
(2)变量插入
三、Chrome Debug 调试:定位问题的 “利器”
3.1 断点设置:让代码 “暂停” 在关键位置
方式 1:通过行号设置(可视化操作)
方式 2:通过 debugger 语句设置(代码级操作)
3.2 执行控制:分步调试代码
3.3 变量监控:实时查看数据变化
1. 查看「Scope」面板(作用域变量)
2. 查看「Watch」面板(自定义监控)
3. 通过控制台查看(临时打印)
3.4 特殊断点:应对复杂场景
1. DOM 断点:监控 DOM 元素变化
2. XHR 断点:拦截网络请求
四、JavaScript 对象:面向对象编程的基础
4.1 对象的基本概念与创建方式
(1)对象的核心特征
(2)常见创建方式
(2)修改属性
(3)添加属性
(4)删除属性
4.3 对象的遍历方式
(1)for-in 循环
(2)Object.keys() 方法
(3)for-of 直接遍历对象(不支持)
4.4 对象的内存模型:栈与堆、值类型与引用类型
(1)内存区域划分
(2)值类型与引用类型的区别
五、函数中的 this 指向:动态绑定的 “上下文”
5.1 this 的两种基础指向规则
规则 1:普通函数调用(默认绑定)
规则 2:对象方法调用(隐式绑定)
5.2 this 的开发实践价值
场景 1:简化对象方法的代码
场景 2:复用函数作为多个对象的方法
5.3 特殊注意:箭头函数没有 this
六、总结
Day20 JavaScript 进阶核心:IIFE、代码规范、调试与对象模型
在 JavaScript 学习路径中,函数的高级用法、代码规范、调试技巧与对象模型是连接基础语法与实际开发的关键桥梁。本文将围绕立即执行函数(IIFE)、代码规范、Chrome 调试工具、对象操作及 this
指向展开,结合实战场景解析核心知识点,帮助读者构建系统化的 JavaScript 进阶能力。
一、立即执行函数(IIFE):独立作用域的 “隔离神器”
立即执行函数(Immediately Invoked Function Expression,简称 IIFE)是一种特殊的函数表达式,定义后会立即执行。它的核心价值在于创建独立作用域,避免变量污染全局命名空间,是 ES6 模块普及前前端模块化的重要实现方式。
1.1 IIFE 的基本语法与原理
IIFE 的本质是 “将函数转换为表达式 + 立即调用”,核心语法有两种常见形式:
形式 1:括号包裹函数表达式(推荐)
// 基础语法:(函数表达式)() (function() {console.log("IIFE 立即执行"); })(); // 带参数的 IIFE:传递参数如同普通函数 (function(name, age) {console.log(`我是${name},今年${age}岁`); })("小明", 18); // 输出:我是小明,今年18岁
形式 2:函数表达式前加运算符(了解即可)
除了括号,+
、!
、-
等一元运算符也能将函数声明转换为表达式,进而实现立即执行:
// 逻辑非运算符转换 !function() {console.log("通过 ! 转换为表达式"); }(); // 加号转换 +function(score) {console.log(`分数:${score}`); }(95); // 输出:分数:95
核心原理: JavaScript 引擎会区分 “函数声明” 和 “函数表达式”—— 仅函数表达式可以被立即调用(加 ()
执行)。IIFE 通过括号或运算符,强制将函数声明转换为表达式,从而满足 “立即调用” 的语法要求。
1.2 IIFE 的关键注意事项
(1)分号的必要性
若 IIFE 前有未加分号的语句,JavaScript 可能会将其解析为函数调用,导致语法错误。例如:
// 错误示例:前一行未加分号,引擎会解析为 123(function(){})() const num = 123 (function() {console.log("可能报错"); })(); // 正确示例:添加分号分隔语句 const num = 123; (function() {console.log("正常执行"); })();
(2)作用域隔离特性
IIFE 内部声明的变量和函数,仅在 IIFE 作用域内有效,不会污染全局作用域:
(function() {// 局部变量:外部无法访问const message = "IIFE 内部变量";console.log(message); // 输出:IIFE 内部变量 })(); console.log(message); // 报错:ReferenceError: message is not defined
1.3 IIFE 的经典应用场景
场景 1:解决全局变量命名冲突
多人协作开发时,不同开发者可能定义同名全局变量,导致后引入的变量覆盖先引入的变量。IIFE 可将每个模块的代码包裹在独立作用域中,仅通过全局对象暴露必要接口:
// 小明开发的模块:通过 IIFE 隔离作用域 (function(window) {const moduleA = {sayHi() {console.log("小明模块:Hi!");}};// 仅暴露 moduleA 到全局window.moduleA = moduleA; })(window); // 李雷开发的模块:同样使用 IIFE (function(window) {const moduleB = {sayHi() {console.log("李雷模块:Hello!");}};window.moduleB = moduleB; })(window); // 调用两个模块的方法,无命名冲突 moduleA.sayHi(); // 输出:小明模块:Hi! moduleB.sayHi(); // 输出:李雷模块:Hello!
注意:ES6 普及后,import
/export
已成为模块化的标准方案,但 IIFE 的作用域隔离思想仍适用于老旧项目或无模块环境的场景。
场景 2:处理 DOM 事件监听的循环变量问题
在循环中绑定事件监听时,若直接使用循环变量,可能因闭包特性导致所有事件回调共享同一变量值。IIFE 可通过参数传递,为每个循环迭代创建独立作用域:
<!-- HTML:3 个按钮 --> <button class="btn">按钮 1</button> <button class="btn">按钮 2</button> <button class="btn">按钮 3</button> <script>const buttons = document.querySelectorAll(".btn");// 循环绑定点击事件for (var i = 0; i < buttons.length; i++) {// IIFE 为每个 i 创建独立作用域(function(index) {buttons[index].addEventListener("click", function() {console.log(`点击了按钮 ${index + 1}`);});})(i); // 传递当前循环的 i 值} </script>
原理:var
声明的变量无块级作用域,循环结束后 i
会变成 3。IIFE 通过参数 index
捕获每次循环的 i
值,确保每个事件回调访问的是当前迭代的 index
。
二、JavaScript 代码规范:写出可维护的 “优雅代码”
代码规范是团队协作的 “语言共识”—— 统一的格式、命名和逻辑风格,能大幅降低代码维护成本,减少因格式问题导致的 bug。以下是前端开发中必须遵守的核心规范,部分参考 Airbnb JavaScript 规范(业界主流规范之一)。
2.1 基础格式规范
(1)空格与缩进
-
缩进:使用 2 个空格(而非 Tab),确保不同编辑器显示一致;
-
等号 / 运算符:等号(
=
)、加号(+
)、减号(-
)等运算符前后必须加空格; -
函数声明:函数名与括号之间不加空格,括号与花括号之间加 1 个空格;
-
for 循环:
for
关键字与括号之间加 1 个空格,分号后加 1 个空格。
// 错误示例:格式混乱 function add(a,b){return a+b;} for(var i=0;i<5;i++){console.log(i);} // 正确示例:符合规范 function add(a, b) { // 参数逗号后加空格,括号与花括号间加空格return a + b; // 运算符前后加空格 } for (var i = 0; i < 5; i++) { // for 后加空格,分号后加空格console.log(i); }
(2)分号与空行
-
分号:每个语句结束必须加半角分号(避免 ASI 自动分号插入机制导致的意外);
-
空行:不同逻辑块(如函数定义、条件判断)之间加 1 个空行,提升可读性。
// 正确示例:分号与空行规范 const name = "张三"; // 语句结束加分号 const age = 20; // 空行分隔不同逻辑块 function greet() {console.log(`我是${name},${age}岁`); } greet();
(3)单行代码长度
单行代码长度不超过 80 个字符(部分团队放宽到 120 个),过长时需换行,换行位置优先在运算符后:
// 错误示例:单行过长 const result = 123 + 456 + 789 + 1011 + 1213 + 1415 + 1617; // 正确示例:换行拆分 const result = 123 + 456 + 789 + 1011 + 1213 + 1415 + 1617;
2.2 命名规范
(1)变量与函数
-
变量:使用小驼峰命名法(首字母小写,后续单词首字母大写),命名需体现变量含义;
-
函数:使用小驼峰命名法,命名以动词开头(如
getUser
、formatDate
),明确函数功能。
// 错误示例:命名模糊 const x = "2024-09-03"; function f(a) { return a * 2; } // 正确示例:命名清晰 const currentDate = "2024-09-03"; // 体现“当前日期” function doubleNumber(num) { return num * 2; } // 动词开头,明确“翻倍数字”
(2)常量
使用全大写命名法,单词间用下划线分隔(如 MAX_SIZE
、API_BASE_URL
):
const MAX_AGE = 120; // 最大年龄常量 const API_BASE_URL = "https://api.example.com"; // API 基础地址常量
2.3 模板字符串(ES6 特性)
模板字符串使用反引号(`)包裹,支持多行书写和变量插入,是替代传统字符串拼接的最佳方案:
(1)多行字符串
无需使用 \n
转义,直接换行即可:
// 传统字符串(繁琐) const poem = "床前明月光,\n疑是地上霜。\n举头望明月,\n低头思故乡。"; // 模板字符串(简洁) const poem = `床前明月光, 疑是地上霜。 举头望明月, 低头思故乡。`;
(2)变量插入
通过 ${变量/表达式}
语法插入值,避免字符串拼接的 +
运算符:
const name = "李白"; const age = 61; // 传统拼接(易出错) const intro = "我是" + name + ",今年" + age + "岁。"; // 模板字符串(清晰) const intro = `我是${name},今年${age}岁。`; // 支持表达式计算 const total = `1+2+3=${1+2+3}`; // 结果:"1+2+3=6"
三、Chrome Debug 调试:定位问题的 “利器”
Chrome 开发者工具(F12 或 Ctrl+Shift+I)的 Debug 功能,是前端开发中定位代码错误、跟踪变量变化的核心工具。掌握断点设置、变量监控和执行控制,能大幅提升问题排查效率。
3.1 断点设置:让代码 “暂停” 在关键位置
断点是调试的起点,用于让代码执行到指定行时暂停,方便观察变量状态和执行流程。常见设置方式有两种:
方式 1:通过行号设置(可视化操作)
-
打开 Chrome 开发者工具,切换到「Sources」面板;
-
在左侧文件树中找到需要调试的 JS 文件;
-
点击代码行号(如第 10 行),行号前会出现蓝色箭头,代表断点已设置;
-
刷新页面或触发代码执行(如点击按钮),代码会在断点行暂停。
方式 2:通过 debugger
语句设置(代码级操作)
在代码中插入 debugger;
语句,执行到该语句时会自动暂停,适用于需要精准定位的场景:
function calculate(a, b) {debugger; // 插入调试语句const sum = a + b;const product = a * b;return { sum, product }; } calculate(3, 5); // 执行到 debugger 时暂停
3.2 执行控制:分步调试代码
代码暂停后,可通过「Sources」面板的控制按钮,分步执行代码,观察每一步的变量变化。核心按钮功能如下:
按钮图标 | 名称(中文) | 功能说明 |
---|---|---|
▶️ | 继续执行 | 从当前暂停位置继续执行,直到遇到下一个断点或代码结束 |
⬇️ | 单步执行 | 执行当前行代码,不进入函数内部(跳过函数调用) |
⬇️➡️ | 步入函数 | 执行当前行代码,若有函数调用则进入函数内部 |
⬆️➡️ | 步出函数 | 从当前函数内部退出,回到函数调用位置 |
⬜️ | 暂停执行 | 暂停当前正在执行的代码(通常用于捕获快速执行的代码) |
实战示例:调试函数执行流程
function add(a, b) {return a + b; } function calculateTotal(x, y) {const temp = add(x, y); // 断点设置在此行return temp * 2; } calculateTotal(2, 4);
-
在
const temp = add(x, y);
行设置断点; -
点击「步入函数」按钮,进入
add
函数内部,观察a
和b
的值; -
再次点击「继续执行」,回到
calculateTotal
函数,观察temp
的值; -
重复操作,直到代码执行完毕,验证结果是否符合预期。
3.3 变量监控:实时查看数据变化
调试过程中,需要实时查看变量值,常见方式有三种:
1. 查看「Scope」面板(作用域变量)
代码暂停时,「Scope」面板会显示当前作用域的变量,分为「Local」(局部变量)、「Closure」(闭包变量)、「Global」(全局变量)三类。例如:
-
局部变量:函数内部声明的变量(如
a
、b
、temp
); -
全局变量:
window
对象下的变量(如document
、console
)。
2. 查看「Watch」面板(自定义监控)
若需要重点关注某个变量或表达式,可在「Watch」面板添加监控:
-
点击「Watch」面板的「+」号;
-
输入变量名(如
temp
)或表达式(如temp * 2
); -
按下回车,面板会实时显示变量或表达式的值,变化时会高亮显示。
3. 通过控制台查看(临时打印)
代码暂停时,可在下方「Console」面板直接输入变量名(如 a
、b
),按下回车即可查看当前值,适用于临时验证。
3.4 特殊断点:应对复杂场景
除了普通代码断点,Chrome 还支持特殊断点,用于解决 DOM 变化、网络请求等场景的调试:
1. DOM 断点:监控 DOM 元素变化
当需要跟踪某个 DOM 元素的修改(如样式变化、子元素增减)时,可设置 DOM 断点:
-
打开「Elements」面板,找到目标 DOM 元素;
-
右键点击元素,选择「Break on」→ 「Subtree modifications」(子树修改)/「Attribute modifications」(属性修改)/「Node removal」(节点删除);
-
当 DOM 发生对应变化时,代码会自动暂停。
2. XHR 断点:拦截网络请求
当需要调试 AJAX 请求(如接口参数、响应数据)时,可设置 XHR 断点:
-
在「Sources」面板,找到右侧「XHR/fetch Breakpoints」;
-
点击「+」号,输入接口 URL 包含的关键词(如
/api/user
); -
当发起包含该关键词的请求时,代码会在请求发送前暂停,方便查看请求参数。
四、JavaScript 对象:面向对象编程的基础
对象是 JavaScript 中存储复杂数据的核心结构,本质是 “键值对(key-value)的集合”。它不仅能存储数据,还能封装方法,是实现面向对象编程的基础。
4.1 对象的基本概念与创建方式
(1)对象的核心特征
-
键(key):也称属性名,通常是字符串(可省略引号,若包含特殊字符则必须加引号);
-
值(value):也称属性值,支持任意数据类型(数字、字符串、函数、对象等);
-
方法:若属性值是函数,则该属性称为方法(如
obj.sayHi()
),用于封装对象的行为。
(2)常见创建方式
开发中最常用的是 “对象字面量” 方式({}
),语法简洁且直观:
// 基础对象:包含普通属性和方法 const person = {name: "张三", // 字符串属性age: 25, // 数字属性isStudent: false, // 布尔属性// 方法(函数作为属性值)sayHi: function() {console.log(`你好,我是${this.name}`);},// 嵌套对象:属性值为另一个对象 address: { city: "北京", street: "朝阳路" } };// 其他创建方式(了解即可)// 1. new Object ():通过构造函数创建 const car = new Object (); car.brand = "Tesla"; car.model = "Model 3"; // 2. Object.create ():基于原型创建 const animal = Object.create (null); animal.type = "Dog";
### 4.2 对象的核心操作:增删改查 对象的操作围绕“属性”展开,核心包括**访问、修改、添加、删除**四种操作,支持两种属性访问语法:点语法(`.`)和方括号语法(`[]`)。 #### (1)访问属性 - **点语法**:适用于属性名符合变量命名规则(无空格、特殊字符,非数字开头),简洁常用; - **方括号语法**:适用于属性名含特殊字符(如空格、`-`)、数字开头,或属性名存储在变量中(动态访问)。 ```javascript const person = {name: "李四","phone number": "13800138000", // 含空格的属性名123: "数字开头的属性" }; // 1. 点语法访问 console.log(person.name); // 输出:李四 // 2. 方括号语法访问(特殊属性名) console.log(person["phone number"]); // 输出:13800138000 console.log(person[123]); // 输出:数字开头的属性(数字会自动转为字符串) // 3. 动态访问(属性名存储在变量中) const key = "name"; console.log(person[key]); // 输出:李四(等价于 person["name"])
(2)修改属性
通过 “属性名赋值” 修改已有属性的值,语法与访问类似:
const person = {name: "王五",age: 30 }; // 修改属性值 person.age = 31; // 点语法修改 person["name"] = "王五六"; // 方括号语法修改 console.log(person.age); // 输出:31 console.log(person.name); // 输出:王五六
(3)添加属性
JavaScript 对象支持动态添加属性 —— 直接对不存在的属性赋值即可:
const person = {name: "赵六" }; // 添加普通属性 person.gender = "男"; // 添加方法 person.introduce = function() {console.log(`我是${this.name},性别${this.gender}`); }; console.log(person.gender); // 输出:男 person.introduce(); // 输出:我是赵六,性别男
(4)删除属性
通过 delete
关键字删除对象的属性,删除后属性会从对象中移除(区别于赋值为 undefined
):
const person = {name: "孙七",age: 28,hobby: "篮球" }; // 删除 age 属性 delete person.age; // 删除 hobby 属性 delete person["hobby"]; console.log(person.age); // 输出:undefined(属性已删除) console.log("age" in person); // 输出:false(检查属性是否存在)
4.3 对象的遍历方式
当需要批量处理对象的属性时,需通过遍历实现。JavaScript 提供多种对象遍历方法,核心常用的有 for-in
循环和 Object.keys()
方法。
(1)for-in
循环
for-in
循环会遍历对象的所有可枚举属性(包括继承的属性),语法如下:
const person = {name: "周八",age: 22,gender: "女" }; // for-in 遍历对象 for (const key in person) {// 可选:过滤继承的属性(仅遍历自身属性)if (person.hasOwnProperty(key)) {const value = person[key]; // 注意:此处需用方括号语法console.log(`${key}: ${value}`);} } // 输出: // name: 周八 // age: 22 // gender: 女
注意:for-in
遍历顺序不固定(尤其是数字属性会优先按升序排列),且会遍历继承属性,因此建议用 hasOwnProperty(key)
过滤自身属性。
(2)Object.keys()
方法
Object.keys(obj)
会返回对象自身所有可枚举属性的数组,再结合 for-of
或 forEach
循环遍历,是更现代的遍历方式:
const person = {name: "吴九",age: 35,job: "工程师" }; // 1. 获取属性名数组 const keys = Object.keys(person); console.log(keys); // 输出:["name", "age", "job"] // 2. for-of 遍历 for (const key of keys) {console.log(`${key}: ${person[key]}`); } // 3. forEach 遍历 keys.forEach(key => {console.log(`${key}: ${person[key]}`); });
(3)for-of
直接遍历对象(不支持)
需注意:for-of
循环默认不支持遍历普通对象(仅支持数组、字符串等 “可迭代对象”),直接使用会报错:
const person = { name: "郑十" }; for (const value of person) {console.log(value); // 报错:TypeError: person is not iterable }
若需用 for-of
遍历对象,需先通过 Object.values()
或 Object.entries()
转换为可迭代对象:
// Object.values():获取属性值数组 for (const value of Object.values(person)) {console.log(value); // 输出:郑十 } // Object.entries():获取 [key, value] 数组 for (const [key, value] of Object.entries(person)) {console.log(`${key}: ${value}`); // 输出:name: 郑十 }
4.4 对象的内存模型:栈与堆、值类型与引用类型
理解对象的内存存储机制,是解决 “对象赋值后修改同步” 等问题的关键。JavaScript 内存分为栈内存和堆内存,不同类型的数据存储方式不同。
(1)内存区域划分
-
栈内存:存储值类型(原始类型) 数据(如
Number
、String
、Boolean
、undefined
、null
、Symbol
),特点是容量小、访问速度快,数据直接存储在栈中; -
堆内存:存储引用类型数据(如
Object
、Array
、Function
),特点是容量大、可动态分配,数据存储在堆中,栈中仅保存指向堆内存的 “引用地址”(类似指针)。
(2)值类型与引用类型的区别
类型分类 | 包含类型 | 存储位置 | 赋值行为 | 示例 |
---|---|---|---|---|
值类型 | Number、String 等 | 栈内存 | 赋值的是值的副本,修改互不影响 | let a = 10; let b = a; b = 20; (a 仍为 10) |
引用类型 | Object、Array 等 | 堆内存(栈存引用) | 赋值的是引用地址,修改共享对象 | let obj1 = {x:1}; let obj2 = obj1; obj2.x=2; (obj1.x 变为 2) |
实战示例:验证引用类型的共享特性
// 1. 值类型赋值:独立副本 let num1 = 5; let num2 = num1; num2 = 10; console.log(num1); // 输出:5(num1 不受影响) // 2. 引用类型赋值:共享引用 const obj1 = { name: "A" }; const obj2 = obj1; // obj2 保存的是 obj1 的引用地址 obj2.name = "B"; // 修改共享对象的属性 console.log(obj1.name); // 输出:B(obj1 也被修改) // 3. 函数参数传递(同赋值规则) function changeValue(num, obj) {num = 100; // 值类型参数:修改副本,不影响外部obj.age = 20; // 引用类型参数:修改共享对象,影响外部 } let x = 10; const user = { age: 18 }; changeValue(x, user); console.log(x); // 输出:10(无变化) console.log(user.age); // 输出:20(被修改)
五、函数中的 this
指向:动态绑定的 “上下文”
this
是 JavaScript 函数中的特殊变量,代表 “函数的执行上下文”(即函数被谁调用)。它的指向并非固定,而是由函数的调用方式决定,这是理解 this
的核心。
5.1 this
的两种基础指向规则
规则 1:普通函数调用(默认绑定)
当函数独立调用(不依附于任何对象)时,非严格模式下 this
指向全局对象(浏览器中为 window
,Node.js 中为 global
);严格模式下 this
为 undefined
。
// 非严格模式(默认) function sayGlobal() {console.log(this === window); // 输出:true } sayGlobal(); // 普通函数调用,this 指向 window // 严格模式 function sayStrict() {"use strict"; // 启用严格模式console.log(this); // 输出:undefined } sayStrict(); // 普通函数调用,this 为 undefined // 注意:对象方法赋值后独立调用,仍属于普通调用 const obj = {fn: function() {console.log(this === window);} }; const independentFn = obj.fn; independentFn(); // 输出:true(this 指向 window,非 obj)
规则 2:对象方法调用(隐式绑定)
当函数作为对象的方法调用时(如 obj.fn()
),this
指向调用该方法的对象(即 obj
)。这是开发中最常见的 this
指向场景。
const user = {name: "小明",age: 18,introduce: function() {// this 指向调用 introduce 方法的对象(user)console.log(`我是${this.name},今年${this.age}岁`);},address: {city: "上海",showCity: function() {// this 指向调用 showCity 方法的对象(address)console.log(`我在${this.city}`);}} }; user.introduce(); // 输出:我是小明,今年18岁(this 指向 user) user.address.showCity(); // 输出:我在上海(this 指向 address)
5.2 this
的开发实践价值
this
的核心价值在于动态绑定调用者,避免因对象变量名变更导致的代码修改,提升函数的复用性。
场景 1:简化对象方法的代码
若不使用 this
,方法需硬编码对象名,当对象名变更时,所有方法都需修改:
// 无 this:硬编码对象名,灵活性差 const user1 = {name: "小红",introduce: function() {console.log(`我是${user1.name}`); // 硬编码 user1} }; // 有 this:动态绑定,灵活性高 const user2 = {name: "小红",introduce: function() {console.log(`我是${this.name}`); // 无需关心对象名} }; // 对象名变更时,无 this 的代码需修改,有 this 的代码无需修改 const newUser = user2; newUser.introduce(); // 输出:我是小红(this 自动指向 newUser)
场景 2:复用函数作为多个对象的方法
同一函数可作为多个对象的方法,this
会自动指向当前调用对象:
// 定义通用的 introduce 函数 function introduce() {console.log(`我是${this.name},职业是${this.job}`); } // 多个对象复用该函数 const teacher = {name: "张老师",job: "教师",introduce: introduce // 赋值函数作为方法 }; const doctor = {name: "李医生",job: "医生",introduce: introduce // 复用同一函数 }; teacher.introduce(); // 输出:我是张老师,职业是教师(this 指向 teacher) doctor.introduce(); // 输出:我是李医生,职业是医生(this 指向 doctor)
5.3 特殊注意:箭头函数没有 this
箭头函数是 ES6 新增的函数形式,它没有自己的 this
,而是继承外层作用域的 this
。这是箭头函数与普通函数的核心区别之一,需特别注意。
const obj = {name: "箭头函数测试",// 普通函数:this 指向 objnormalFn: function() {console.log(this.name); // 输出:箭头函数测试},// 箭头函数:无自己的 this,继承外层 this(此处外层是全局作用域,this 指向 window)arrowFn: () => {console.log(this.name); // 输出:undefined(window 无 name 属性)} }; obj.normalFn(); // 正常输出 obj.arrowFn(); // 输出 undefined
开发建议:
-
定义对象方法时,优先使用普通函数(确保
this
指向对象); -
定义回调函数(如
setTimeout
、数组方法回调)时,可使用箭头函数(继承外层this
,避免绑定问题)。
六、总结
本文围绕 JavaScript 进阶核心知识点展开,从 IIFE 的作用域隔离、代码规范的工程价值,到 Chrome Debug 的实战技巧,再到对象的操作与内存模型、this
的动态指向,覆盖了前端开发中高频使用的技术点。
核心要点回顾:
-
IIFE:通过独立作用域解决全局变量污染,是模块化的早期方案;
-
代码规范:统一格式、命名和逻辑,提升代码可维护性,是团队协作的基础;
-
Chrome Debug:断点设置、分步执行和变量监控,是定位问题的核心工具;
-
对象模型:键值对存储、栈堆内存差异、值类型与引用类型的赋值规则,是理解数据存储的关键;
-
this
指向:由函数调用方式决定,普通调用指向全局,对象方法调用指向对象,箭头函数无自身this
。
这些知识点不仅是面试高频考点,更是实际开发中解决问题的基础。建议结合代码练习(如封装工具函数、调试实际项目),逐步深化理解,将理论转化为实战能力。