笔记:《JavaScript学习指南》-第7章作用域

第7章 作用域
作用域决定了变量、常量和参数被定义的时间和位置。
函数参数的作用域仅限于函数体中。
函数的形参只有在函数被调用的时候才存在(变成实参)。
一个函数可能会被调用多次:每次函数调用开始时,参数才是真实存在的,在函数返回后参数就失去作用域了。
变量和常量只有在创建后才存在。

7.1 作用域和存在
作用域(或者可见性)指的是当前可见并且可以被正在执行的代码块访问的标识符。
存在也指的是标识符。

7.2 静态作用域和动态作用域
JavaScript中,作用域是静态的。
静态作用域指的是,在某个作用域内定义了某个函数(而不是调用函数),该作用域包含的所有变量也在该函数的作用域内。
const x = 3;
function f(){
    console.log(x);
    console.log(y);
}

const y = 5;
f();    
//3
//5

7.3 全局作用域
在全局作用域中声明的变量叫全局变量,应谨慎使用!因为全局变量在所有作用域中都可用。
避免依赖全局作用域!
let name = "liang";    //全局变量
let age = 26;    //全局变量
改写为:
let user = {
    name: "liang",
    age: "26"
}

function greet(){
    console.log(`Hellos,${user.name}`);
}

greet();     //Hellos,liang
greet依赖于全局变量user,而user还是可以被任意改变。
可以对函数做优化,让它不再依赖全局作用域:
function greet(user){
    console.log(`Hello,${user.name}`);
}
//此时函数greet可以在任何作用域被调用了,需要显式传入一个user

greet({name:"liang"});     //Hello,liang

7.4 块作用域
块作用域指的是仅仅在代码块有效的变量。

7.5 变量屏蔽
不同作用域存在相同名字或常量,容易混淆。
作用域嵌套时:
{
    let x = "blue";
    console.log(x);    //blue
    {
        let x = 3;
        console.log(x);    //3
    }
    console.log(x);    //blue
}
这个例子演示了变量屏蔽。在内部块中,x 是一个独立于外部快的(同名)变量。
变量屏蔽,指的是一个相同名字的变量会将外部作用域的变量屏蔽起来。只要没有被屏蔽,都是可以被访问的。
如果是ES6之前,var 声明则没有变量屏蔽:
{
     var x = "blue";
    console.log(x);    //blue
    {
         var x = 3;
        console.log(x);    //3
    }
    console.log(x);     //3
}

7.6 函数、闭包和静态作用域
在“传统”程序中,可能会把所有函数都定义在全局作用域内,而在函数中避免去访问全局变量,甚至不用思考函数可以访问什么作用域。
在现在的JavaScript中开发中,通常会把函数定义在需要使用的地方。将它们赋给变量和对象属性、添加到数组中、当做参数传给函数、作为函数的返回值,甚至有时候连名字也省掉了。
闭包,故意将某个函数定义在一个指定的作用域中,并明确地指出他对该作用域所具备访问权限。
闭包的例子:
let globalFunc;

{
    let blockVar = "a";
    globalFunc = function(){
        console.log(blockVar);
    };
}

globalFunc();    //a
globalFunc构成了一个闭包。不论在哪里调用globalFunc,它都有权限访问闭包内的变量。
通常情况下,某个作用域退出后,该作用域中声明的变量会安全的消亡。上例中,JavaScript会注意到闭包,从而改变了它的生命期。

在闭包内定义的函数不但可以影响闭包的声明周期,它还允许访问一些正常情况下无法访问到的信息。如下面例子:
let f;
{
    let o = { note: "safe"};
    f = function(){
        return o;
    };
}

let oRef = f();
oRef.note;    //safe

7.7 即时调用函数表达式
函数允许创建即时调用函数表达式( IIFE )。IIFE 声明了一个函数,并立即执行该函数。
形式如下:
(function(){
    //...
})();

IIFE的好处是,任何内部信息都有自己的作用域,并且因为它本身就是函数,它还可以向作用域外传递信息。

一个可以打印出自己被调用次数的函数,且次数不会被篡改:
const f = (function(){
    let count = 0;
    return function(){
        return `I have been called ${++count} time(s).`;
    }
})();

f();    //"I have been called 1 time(s)."
f();    //"I have been called 2 time(s)."
因为变量count 被安全的隐藏在IIFE中,外界没法篡改它,所以函数f 始终能准确计算出自己被调用的次数。

7.8 函数作用域和提升
使用 let 声明变量,变量只在声明之后才存在。
而使用 var 声明,变量在当前作用域中任意地方都可用,甚至可在声明前使用。
变量提升,指任何使用var 声明的变量都会被提升至作用域的顶部(是声明的变量而不是赋值)。

未声明的变量和值为undefined 的变量不是同一个概念。使用未声明的变量会报错,但可以安全使用值为undefined的变量。

7.9 函数提升
函数声明也会被提升至它们作用域的顶部,允许在函数声明之前调用。
f();    //hi

function(){
    console.log("hi");
}

//赋给变量的函数表达式不会被提升
func();    // func is not defined

let func = function(){
    console.log("liang");
}

7.10 临时死区
ES6之前,因为使用var 声明变量,变量会提升。而ES6中,使用 let和const声明,有临时死区。
临时死区( TDZ ),是指在给定作用域内,变量被声明之前的代码。

typeof 常被用来检测变量是否已被声明。由于临时死区,将不能正确判断。ES6 中,也没有必要使用typeof 来检测变量是否被定义。因为 let 和const 不能重复声明已有的变量。

7.11 严格模式
严格模式,能阻止隐式全局变量。
ES5的语法允许存在隐式全局变量,当忘记使用var 来声明某个变量时,JavaScript会把要声明的变量自动创建为全局变量。

在开始编写代码前,插入一行字符串 "use strict" (单引号或双引号都可以)就可以启用严格模式。
不建议在全局作用域中使用严格模式,可以将所有代码封装在一个IIFE中:
(function(){
    'use strict';
    //所有代码从这个开始,将按严格模式执行
})();
推荐使用严格模式。

猜你喜欢

转载自blog.csdn.net/kjhz_liang/article/details/80526812