Web前端之路让前端飞JavaScript 进阶营

函数作用域 | 块作用域 | 提升

2017-09-08  本文已影响54人  姚屹晨

一.函数作用域和块作用域

1.1函数中的作用域
1.2隐藏内部实现

①是什么?

②对原始函数带来的影响?

③那么,为什么要选择将一个函数作用域隐藏于另一个函数作用域内部呢?

function doSomething(a){
    b = a + doSomethingElse( a * 2 );
    console.log( b * 3 );
}
function doSomethingElse(a){
    return a-1;
}
var b = 0;
doSomething(2);
>>>15


function doSomething(a){
    function doSomethingElse(a){
        return a - 1;
    }
    var b = 0;
    b = a + doSomethingElse(a * 2);
    console.log(b * 3);
}
doSomething(2);
>>>15

④规避冲突

function foo(){
    function bar(a){
        i = 3;//修改了for循环所属属性的i
        console.log(a + i);
    }
    for(var i = 0;i < 10;i++){
        bar(i*2);//因为i的值被修改为3,所以永远都小于10,导致无限循环!
    }
}
foo();


//解决
function foo(){
    function bar(a){
        var i = 3;//"遮蔽变量"
        console.log(a + i);
    }
    for(var i = 0;i < 10;i++){
        bar(i*2);
    }
}
foo();

另外两种解决方式:

1.全局命名空间

var MyReallyCoolLibrary = {
    awesome: 'stuff',
    doSomething: function(){
        // ...
    },
    doAnotherThing: function(){
        //...
    }
};

2.模块管理

1.3函数作用域

var a = 2;
var a = 3;
console.log(a);
>>>3

②使用所学内容:"隐藏内部实现",将最后两行代码"隐藏"掉:

var a = 2;
function foo(){
    var a = 3;
    console.log(a);//3
}
foo();
console.log(a);//2

③虽然解决了部分问题,但也带来了其他的麻烦:

④解决之法:

var a = 2;
(function foo(){
    var a = 3;
    console.log(a);//3
})();
console.log(a);//2

⑤如何区分函数声明和函数表达式?

function foo(){...}  //函数声明

var test = function(){...}   //函数表达式

(function foo(){...})()  //函数表达式

⑥函数声明和函数表达式的区别?

1.4块作用域

let关键字,除了var以外的另一种变量声明的方式,用于在任何代码块中声明变量。

②存在的原因?

if(true){
    var b = 20;
}
console.log(b);
>>>20


if(true){
    let b = 20;
}
console.log(b);
>>>Uncaught ReferenceError: b is not defined

③提升是什么?

console.log(a);
var a = 2;
>>>2

{
    console.log(a);
    let a = 2;
}
>>>Uncaught ReferenceError: a is not defined

for(let i = 0;i < 5;i++){

}
console.log(i);
>>>Uncaught ReferenceError: i is not defined

⑤除了let之外,ES6还引入了const,同样可以用来创建块作用域变量,但其值是固定的(常量)。之后任何试图修改其值的操作都会导致错误(Uncaught TypeError:Assignment to constant variable)

if(true){
    const b = 5;
}
console.log(b);
>>>Uncaught ReferenceError: b is not defined


if(true){
    const b = 5;
    b = 10;
}
>>>Uncaught TypeError: Assignment to constant variable.

二.提升

1.所有声明(变量+函数)都会在任何代码被执行前,首先被编译器处理。
var a = 2;
//分解为两个阶段
//第一个声明:定义声明,在编译阶段进行
var a;

//第二个声明:赋值声明,原地待命,等待执行阶段
a = 2;
2.先有鸡还是先有蛋?
3.只有定义声明会被提升,而赋值声明或其他运行逻辑则会原地待命。
4.每个作用域都会进行提升操作:
foo();
function foo(){
    console.log(a);//undefined
    var a = 2;
}

//其实是这样的
function foo(){
    var a;
    console.log(a);//undefined
    a = 2;
}
foo();
5.
foo();// 不是ReferenceError,而是TypeError!
var foo = function bar(){
    // do something
};

//还原一下
var foo;
foo();//TypeError
foo = function bar(){
    // do something
};

//More
var foo;
foo();//Uncaught TypeError: foo is not a function
foo = function bar(){
    // do something
};

ReferenceErrorTypeError的区别?

6.即使是具名的函数表达式,名称标识符在被赋值之前也无法在所在作用域中使用:
foo();//TypeError
bar();//ReferenceError
var foo = function bar(){
    // do something
};

//还原一下
var foo;
foo();//TypeError
bar();//ReferenceError,压根在全局作用没有找个这个函数
foo = function(){
    var bar = ...self...
    // do something
};
7.函数优先

①函数声明和变量声明都会被提升,但是有个先来后到,函数会首被提升,然后才是变量。

foo();
var foo;
function foo(){
    console.log('我是foo的函数声明');
}
foo = function(){
    console.log('我是foo的函数表达式');
};
>>>我是foo的函数声明

//还原一下
function foo(){
    console.log('我是foo的函数声明');
}
foo();
//var foo;被同名的foo函数声明"覆盖"掉
foo = function(){
    console.log('我是foo的函数表达式');
};
>>>我是foo的函数声明

②尽管重复的var声明会被忽略掉,但出现在后面的函数声明还是可以覆盖掉前面:

//后面的函数声明
foo();
function foo(){
    console.log('我是foo的函数声明');
}
function foo(){
    console.log('我是后面的函数声明');
}
>>>我是后面的函数声明

③杜绝在同一个作用域进行重复定义。

上一篇 下一篇

猜你喜欢

热点阅读