这个专栏的第二篇写完啦!和作用域有关的内容都在里面阿里
今天复习一下有关作用域的内容,可能每个人划分内容不一样,我这里主要讲了let,const,var之间的区别,变量提升,和闭包
代码如下(示例):
var tmp = 123
if (true) {
tmp = 'abc'; // 报错ReferenceError
let tmp
}
// 在这段代码中,有全局变量tmp,也有局部变量tmp。导致后者绑定在这个块级作用域中,所以在let声明中,对tmp赋值不会报错
注意:其实const保证的不是变量值不变,而是保证变量指向的内存地址所保存的数据不允许改变。基本数据类型和引用数据类型存储值的方式不同。基本数据类型直接保存在内存地址中,因此const声明的基本类型就相当于常量。对应引用类型,变量指向的是内存地址,实际上是一个数据指针,所以const只要保证指针是固定的,指针指向的数据结构式无法控制的。
作用域就是变量与函数可以访问范围,即作用域控制着变量与函数的可见性和生命周期。
作用域又分为全局作用域和局部作用域:
代码如下(示例):
var num="100";
function scope(){
var num="10";
function innerScope(){
var num = "1";
console.log(scope);//输出:1
}
innerScope();
console.log(num);//输出:10
}
scope();
console.log(num);//输出:100
如果你看懂了上面的代码那你一定知道局部作用域是怎么用的。
局部作用域有两个特点:
那也许你会见过这样的代码,代码如下(示例):
var num="100";
function scope(){
var num="10";
function innerScope(){
num = "1";
console.log(scope);//输出:1
}
innerScope();
console.log(num);//输出:1
}
scope();
console.log(num);//输出:100
在这里你可能会纳闷,第二条特点不是说局部变量仅仅在函数内部有效么,为什么第二次输出是1??
这是因为不使用关键字声明,而直接创建的变量是全局变量,所以第二次输出1!!
在使用var来声明变量时,会发生变量提升,提升到作用域的顶端去执行。
代码如下(示例):
function test () {
console.log (a) // undefined
var a =123
}
test ()
// 实际执行顺序
fucntion test () {
var a;
console.log(a)
a = 123
}
test()
代码如下(示例):
console.log(v1)
var v1 = 100;
function foo () {
console.log(v1)
var v1 = 200
console.log(v1)
}
foo ()
console.log(v1)
实际输出结果
undefined undefined 200 100
函数声明有两种方式:1.函数声明式 2.函数表达式
提示:我以前这个地方就分不清
代码如下(示例):
// 函数表达式
var foo = function() {
}
// 函数声明
function foo (){
}
函数声明的提升和变量声明提升略有不同:函数提升是将这个代码提升到他所在的作用域的最开始执行,而函数表达式只是提升声明!这里一定要分清楚哦!
console.log(bar)
function bar (){
console.log(1)
}
// 执行顺序相当于
function bar() {
console.log(1)
}
console.log(bar)
再比较一下!!
foo () //1
var foo;
// 函数声明,将整段代码提升到顶部
function foo() {
console.log(1)
}
// 函数表达式,只是将声明提升上去,所以最后输出是1
foo = function() {
console.log(2)
}
// 可以试一下把函数声明和函数表达式两种方法单独写一下,个人理解函数表达式实际上和声明变量提升方式是一样的。
变量赋值> 函数声明 > 变量声明
var foo = function(x , y) {
return x - y
}
function foo(x , y) {
return x + y
}
foo(1 , 2)
// 实际执行顺序
function foo (x , y) {
return x + y
}
var foo
foo = function(x , y){
return x - y
}
闭包的定义:函数和对其周围状态的引用绑定在一起构成闭包。也就是说,闭包可以让你从内部函数访问外部函数作用域——MDN
形成条件:函数嵌套,内部函数引用外部函数的局部变量。
优点:延长外部函数局部变量的生命周期
缺点:容易造成内存泄漏
使用场景:函数作为返回值,函数作为参数传递
function F1() {
var a = 100
// 函数作为返回值
return function () {
console.log(a)
}
}
var f1 = F1() // 接收到函数返回值
function F2(fn) {
var a = 200
fn()
}
F2(f1) // 将函数作为参数传递进来,输出100
// 这里就体现了闭包的优点,延长了函数局部变量的生命周期
读下面代码,这个题大家应该看过很多次了
function Foo() {
var i = 0;
return function() {
console.log(i++)
}
}
var f1 = Foo()
var f2 = Foo()
f1()
f1()
f2()
这题考察的是闭包和引用类型的知识点
function是引用类型,保存在堆中,变量f1,f2保存在栈中,所以f1,f2是两个不同函数对象。
Foo函数返回一个匿名函数,f1指向堆中匿名函数作用域,可以使用局部变量i。
这里面写了一些简单的闭包,还准备深入一下闭包,这部分是JS很重要的内容!