简单理解
- var zm = function (x) {
- var code = 'bb'
- return code
- };
学过 JS 的老哥们都知道, 当这样简单的一个函数进入浏览器, 浏览器开始解释代码, 会将 Windows 分两个模块: 存储模块, 执行模块.
存储模块, 找到所有的 var 和 function 关键字, 给这些变量添加内存地址
执行模块, 代码从上到下执行, 遇到变量就回去存储模块查找是否有该变量
如果有该变量, 就看是否赋值, 如果赋值了就是后面的值, 没有赋值就是 undefined
如果没找到 结果就是 xxx is not defined
对于作用域可能对于部分人来说是难以理解的知识点, 今天呢就列举 5 个知识点, 只要记住这几句话, 作用域对你来说就是小意思
一,"JavaScript 中无块级作用域"
在 Java 或 C# 中存在块级作用域, 即: 大括号也是一个作用域.
- public static void main (){
- if(1==1){
- String name = "seven";
- }
- System.out.println(name);
- }
- // 报错
在 JavaScript 语言中无块级作用域
- function Zhoumao() {
- if(1 == 1){
- var name = '昼猫'
- }
- console.log(name);
- }
- Zhoumao()
- // 输出: 昼猫
补充: 标题之所以添加双引号是因为 JavaScript6 中新引入了 let 关键字, 用于指定变量属于块级作用域.
二, JavaScript 采用函数作用域
在 JavaScript 中每个函数作为一个作用域, 在外部无法访问内部作用域中的变量.
- function Zhoumao() {
- var name = '昼猫'
- }
- Zhoumao()
- console.log(name);
- // 报错: ReferenceError: name is not defined
三, JavaScript 的作用域链
由于 JavaScript 中的每个函数作为一个作用域, 如果出现函数嵌套函数, 则就会出现作用域链.
- name = '昼猫'
- function out() {
- var name = 'zhoumao'
- function inner() {
- var name = 'hello'
- console.log(name);
- }
- inner()
- }
- out()
如上述代码则出现三个作用域组成的作用域链, 如果出现作用域链后, 那么寻找变量时候就会出现顺序, 对于上述实例:
当执行 console.log(name) 时, 寻找顺序根据作用域链从内到外的优先级寻找, 如果内层没有就逐步向上找, 直到 Windows 为止, Windows 有就使用没有就 is not defined.
四, JavaScript 的作用域链执行前已创建
JavaScript 的作用域在被执行之前已经创建, 日后再去执行时只需要按照作用域链去寻找即可.
示例一:
- name = 'zhoumao'
- function out() {
- var name = '昼猫'
- function inner() {
- console.log(name);
- }
- return inner;
- }
- var code = out();
- code();
- // 输出结果: 昼猫
上述代码, 在函数被调用之前作用域链已经存在:
全局作用域 -> out 函数作用域 -> inner 函数作用域
当执行 code() 时, 由于其代指的是 inner 函数, 此函数的作用域链在执行之前已经被定义为: 全局作用域 -> out 函数作用域 -> inner 函数作用域, 所以, 在执行 code() 时, 会根据已经存在的作用域链去寻找变量.
示例二:
- name = 'zhoumao'
- function out() {
- var name = '昼猫'
- function inner() {
- console.log(name);
- }
- name = 'hello'
- return inner;
- }
- var code = out();
- code();
- // 输出结果: hello
上述代码和示例一的目的相同, 也是强调在函数被调用之前作用域链已经存在:
全局作用域 -> out 函数作用域 -> inner 函数作用域
不同的是, 在执行 var code = out() 时, out 作用域中的 name 变量的值已经由昼猫改变为 hello, 所以之后再执行 code() 时, 就只能找到 hello
示例三:
- name = '昼猫';
- function one(){
- console.log(name);
- }
- function two(){
- var name = "zhoumao";
- return one;
- }
- var code = two();
- code();
- // 输出结果: 昼猫
上述代码, 在函数被执行之前已经创建了两条作用域链:
全局作用域 -> one 函数作用域
全局作用域 -> two 函数作用域
当执行 code() 时, code 代指的 one 函数, 而 one 函数的作用域链已经存在: 全局作用域 -> one 函数作用域, 所以, 执行时会根据已经存在的作用域链去寻找.
五, 声明提前
在 JavaScript 中如果不创建变量, 直接去使用, 则报错:
- console.log(name)
- // 报错: ReferenceError: name is not defined
如果只创建不赋值, 则返回 undefined
- var name;
- console.log(name)
- // 输出: undefined
在函数中如果这么写:
- function Test() {
- console.log(name);
- var name = '昼猫';
- }
- Test()
- // 输出: undefined
在上述代码中不报错而是输出 undefined, 是因为 JavaScript 的函数在被执行之前, 会将其中的变量全部声明, 而不赋值. 所以, 相当于上述示例中, 函数在 "预编译" 时, 已经执行了 var name; 所以上述代码中输出的是 undefined.
类似这种写法
- function Test() {
- var name
- console.log(name);
- name = '昼猫';
- }
- Test()
文章来自公众号 昼猫笔记
来源: https://www.cnblogs.com/zhoumao/p/10276649.html