惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Exploit Database - CXSecurity.com
F
Fortinet All Blogs
U
Unit 42
F
Full Disclosure
雷峰网
雷峰网
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
罗磊的独立博客
D
DataBreaches.Net
C
Check Point Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
O
OpenAI News
C
CXSECURITY Database RSS Feed - CXSecurity.com
aimingoo的专栏
aimingoo的专栏
S
Security @ Cisco Blogs
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Hacker News
The Hacker News
Webroot Blog
Webroot Blog
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google DeepMind News
Google DeepMind News
酷 壳 – CoolShell
酷 壳 – CoolShell
N
News | PayPal Newsroom
P
Proofpoint News Feed
B
Blog RSS Feed
MongoDB | Blog
MongoDB | Blog
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
Google Online Security Blog
Google Online Security Blog
H
Help Net Security
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
M
MIT News - Artificial intelligence
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
IT之家
IT之家
MyScale Blog
MyScale Blog
腾讯CDC

轶哥博客

blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog blog
blog
2021-05-11 · via 轶哥博客

在某个JavaScript前端群里面,网友深圳-resolve发的一道关于JavaScript作用域与函数提升的思考题引发了众位大佬的讨论。

题目

console.log(a)
if (true) {
    a = 2
    function a () {}
    a = 3
    console.log('内部', a)
}
console.log('外部', a)

执行结果:

image.png

打印a在所有位置的值:

image.png

疑点

  1. 为什么第一个console没有报错?(为什么没有发生a is not defined报错?)
  2. 为什么外部a的值为2

答疑

拆解问题

我们先把这个复杂问题拆解成几个小问题,逐一解决。

  1. if语句里面的函数定义对全局有何影响?
  2. var声明的变量、无var声明的变量对函数定义分别有什么样的影响?

为什么第一个console没有报错?(为什么没有发生a is not defined报错?)

image.png

正常情况下打印一个未被定义的值是会直接报错的。但是如果存在函数定义,那么就会出现函数提升的情况。

这是所有JavaScript程序员都知道的常识

然而如果函数声明可能出现在一个 if 语句里,在不同的浏览器将会得到不同的结果。

image.png

详情参阅:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/function

也就是说,在Chrome浏览器中,

console.log(a)
if (true) {
    function a() {}
}

a被提升,可以看作是:

var a = undefined
console.log(a)
if (true) {
    a = function () {}
}

因此第一个console没有报错。而且函数只在程序执行到定义位置的时候赋给已被提升的变量a。因此在Chrome浏览器中,if判断条件能影响后续函数a是否能够被执行。

例如:

image.png

image.png

同样的代码在Safari中,是这样的:

image.png

为什么外部a的值为2

根据上一步得到的结论,我们将a=2加入到单元测试里再进行测试。

image.png

我们发现位于函数体之前的赋值操作修改了a的全局作用域。

这是因为不使用var定义的变量,在块级作用域视为定义全局变量window.a

此时我们将a=3放置回去。

image.png

根据第一个问题的结论,和我们熟知的函数提升原则,以及函数声明的提升优于变量提升的规则,可以视作如下代码:


window.a = undefined
console.log(1, a)
if (true) {
    window.a = function () {}
    console.log(2, window.a) // 非严格模式的函数提升也导致 a 在全局作用域被提升,此时window.a是function
    window.a = 2 // 由于没有添加var声明变量a,因此此时全局变量 a 变为了 2
    console.log(3, window.a)
    // function a () {} 被提升到全局最前方了
    let a = window.a // 由于Chrome的策略,此时函数声明处将创建局部变量a
    a = 3 // 此时再对a进行赋值,修改的是块级作用域的a
    console.log(4.1, window.a)
    console.log(4.2, a)
}
console.log(5, a)

由于浏览器差异,同样的代码在Safari浏览器得到不一致的结果:

image.png

结论

问题性质

这类问题不适合作为面试题或者笔试题,虽然能考察对函数、变量的理解,但是涉及的特殊情形太多。

如何避免类似问题的产生?

使用严格模式 + ES6

使用严格模式 + ES6,在不同浏览器都能得到一致的结果。

Chrome:

image.png

Safari: image.png

使用let定义局部函数

在结构体内定义函数尽量使用let,避免函数被提升到外部。

"use strict";
if (true) {
    let a = () => {
        console.log('ok')
    }
    a();
}
a()

感谢

感谢Johnson木马啊、清明雨上、imakan、幻☆精灵、Kapok、青山几重、田热、北城之阙等网友提供的解答思路。