学习JS的一天:早上被变量提升搞懵,中午被this指向气哭,晚上被闭包和原型链折磨得睡不着觉。
今天在探索JavaScript的奇妙(诡异)世界时,我发现函数声明和this指向简直就像薛定谔的猫------你不执行代码永远不知道会发生什么。下面是我的血泪史总结,附带大量代码实验和灵魂图解!
🔥 第一章:函数名的"防篡改装甲"
情况一
在以下代码中,我发现函数名在函数体内居然是个"只读VIP":
javascript
function b() {
b = 20 // 试图篡位?门都没有!
console.log(b) // 输出函数本体而非20
}
b() // 输出:[Function: b]
原理揭秘:
- 函数声明会在当前作用域创建同名的不可写绑定
- 在严格模式下直接报错:
TypeError: Assignment to constant variable
- 函数表达式中的命名函数同样受此保护
- 非严格模式下,会在函数题内部创建一个只读的局部变量,会忽略重新赋值
情况二
我们再来看以下代码:
js
var b = 10;
function b() { // 函数声明
b = 20 // 不生效的
console.log(b)
}
b()//TypeError: b is not a function,b是一个变量不是函数,不能被调用
为什么会输出b不是一个函数呢?
var 和 函数声明都会进行变量提升,函数是第一等对象,会提升至最顶部其实就是变成了下面的代码
jsfunction b() { // 函数声明 b = 20 // 不生效的 console.log(b) } var b//重新赋值 b变成一个变量,而不是函数 b=10
所以输出TypeError: b is not a function,b是一个变量不是函数,不能被调用
情况三
具名函数表达式
js
var b = 10;
const func = function b() { // 函数表达式
b = 20 // 不生效的
console.log(b)
}
func()// [Function: b]
原理:
和情况一
是类似的函数内部会产生一个局部变量b,是只读的,在非严格模式下,在函数体内容重新赋值会被忽略
在严格模式下,这是一种潜在的错误,会抛出错误,TypeError: Assignment to constant variable
情况四
js
var b = 10;
(function b() {
b = 20 // 不生效的
console.log(b)
})()
这是一个立即执行函数,和情况二
就就不同了,虽然函数会被提升,但是其直接立即执行了,所以不会等到下面的对b赋值为一个变量,所以不会显示b不是一个函数,而是和情况一相同,会输出函数b
在严格模式下,同样的,这是一个潜在的错误,会直接抛出错误TypeError: Assignment to constant variable
情况五
匿名函数表达式
js
var b=10
const func = function () { // 函数表达式
b = 20 // 不生效的
console.log(b)
}
由于是匿名函数,在函数内部是没有具体变量名来访问的,suoyb就指向外面声明的b,会修改b为20,所以最后输出的是20
✅ 总结对比表:
写法 | 是否是 IIFE | 是否具名 | 是否可修改 b = 20 |
输出 | 严格模式输出 |
---|---|---|---|---|---|
var b = 10 (function b() { b = 20; console.log(b); })() |
✅ 是 | ✅ 具名 | ❌ 不可修改(严格模式) | 函数自身 | 报错:TypeError: Assignment to constant variable |
var b = 10 function b() { b = 20; console.log(b); } |
❌ 否 | ✅ 具名 | ✅ 可修改(函数名不遮蔽) | b is not a function | 报错:b is not a function |
var b = 10 var myFunc = function b() { b = 20; console.log(b); } |
❌ 否 | ✅ 具名 | ❌ 不可修改 | 函数自身 | 报错:TypeError: Assignment to constant variable |
var b = 10 var myFunc = function () { b = 20; console.log(b); } |
❌ 否 | ❌ 不具名 | ✅ 可修改 | 20 | 20 |
function b() { b = 20; console.log(b); } |
❌ 否 | ✅ 具名 | ✅ 可修改 | 20 | 20 |
🌍 第二章:全局变量的"双面人生"
在2.html
和2.js
中,我发现了全局变量的精分现场:
2.html
<script>
"use strict";
var a = 1;
console.log(window.a); // 1 → 挂载到window
let aa = 1;
console.log(window.aa); // undefined → 拒绝污染window
</script>
2.js
var a = 1;
// console.log(window.a);// ReferenceError: window is not defined 因为在后端运行,没有window
console.log(global.a); // global 是 node 里面的顶层对象
跨环境差异表:
声明方式 | 浏览器环境 | Node环境 | 是否污染顶层对象 |
---|---|---|---|
var | 挂载到window | 不挂载到global | ✅ |
let/const | 不挂载 | 不挂载 | ❌ |
函数声明 | 挂载 | 挂载 | ✅ |
💡 冷知识:ES6的let/const变量存在于"Script作用域"这个隐形结界中,完美避开了顶层对象污染问题!
🎯 第三章:this指向的"川剧变脸"
在3.html
到6.html
中,我见证了this的七十二变:
场景1:普通函数调用(3.html
)
3.html
var name = '张三';
function func() {
console.log(this.name); // 张三(非严格模式)
}
func(); // 等价于window.func()
场景2:对象方法调用(4.html
)
4.html
var a = {
name: '娄老板',
fn: function() {
console.log(this.name); // '娄老板'
}
}
a.fn(); // this秒变对象
场景3:构造函数调用(5.html
)
5.html
function Person(name) {
this.name = name; // this指向新生的婴儿
}
const p = new Person('labubu');
场景4:DOM事件处理(6.html
)
6.html
input.addEventListener('keydown', function() {
console.log(this); // 触发事件的input元素
});
场景5:严格模式大乱斗(4.html
)
4.html
"use strict";
var b = a.fn;
b(); // 普通调用 → this变成undefined(非严格模式才是window)
⚠️ 血泪教训 :this的值取决于函数被调用的方式,而不是定义的位置!它就像婚礼上的捧花------谁接到就是谁的。
🏹 第四章:箭头函数的"定海神针"
在7.html
中,箭头函数用this的稳定性治愈了我的精神内耗:
7.html
var a = {
name: 'Tom',
func2: function() {
setTimeout(() => {
console.log(this.name); // 'Tom'(锁定父级this) ye就是对象a
}, 1000)
}
}
a.func2();
传统函数 vs 箭头函数:

💎 核心知识总结(含泪整理版)
-
函数名保护机制:
- 函数声明/表达式的名称在函数体内不可重写
- 严格模式直接报错,非严格模式静默失败
-
全局变量管理:
ini// 污染顶层对象 ❌ var legacyVar = 1; // 清洁能源 ✅ let modernLet = 2; const MODERN_CONST = 3;
-
this指向五大门派:
调用方式 this指向 典型场景 普通调用 window/undefined func()
对象方法调用 调用对象 obj.method()
构造函数调用 新创建实例 new Person()
DOM事件处理 触发元素 elem.onclick
箭头函数 定义时的外层this () => {...}
-
箭头函数三大铁律:
- 没有自己的this、arguments、super
- 不能作为构造函数使用
- 没有prototype属性
🌈 后记:从被绑架到掌控全局
今天在JavaScript的迷宫中探险时,我经历了从"这代码见鬼了吧?"到"原来如此!"的顿悟时刻。记住这些黄金法则:
函数名是只读贵族 👑 → 别试图在函数内部篡位
var是污染源 ☣️ → let/const才是环保卫士
this是墙头草 🌾 → 谁调用它就跟谁走
箭头函数是定海神针 🏹 → 定义时锁定this结界
最后送上我的灵魂感悟:
"学习JavaScript就像和傲娇猫主子相处------你以为你在控制它,其实是它在训练你。" 😼
(本篇笔记由被this折磨了8小时的客户端记者为您发回现场报道)