变量声明,作用域,闭包!!

这个专栏的第二篇写完啦!和作用域有关的内容都在里面阿里

文章目录

  • 前言
  • 一、let const var之间的区别?
    • let关键字的特点
    • const关键字的特点
  • 二、作用域,变量提升,函数提升
    • 1.作用域
    • 2.变量提升
    • 3.函数提升
  • 三,闭包
  • 总结


前言

今天复习一下有关作用域的内容,可能每个人划分内容不一样,我这里主要讲了let,const,var之间的区别,变量提升,和闭包


一、let const var之间的区别?

let关键字的特点

  1. 使用let关键字定义的变量,只有在它的块级作用域内有效({}),var定义的变量在函数范围内有效
  2. let定义的变量不存在变量提升,var变量的声明存在变量提升
  3. let不允许在一个作用域内重复定义变量,var可以重复定义
  4. 暂时性死区:只要区块存在let和const命令,它所声明的变量,就绑定在这个区域,不受外部影响

代码如下(示例):

var tmp = 123
if (true) {
     
	tmp = 'abc'; // 报错ReferenceError
	let tmp
}
// 在这段代码中,有全局变量tmp,也有局部变量tmp。导致后者绑定在这个块级作用域中,所以在let声明中,对tmp赋值不会报错

const关键字的特点

  1. 和let一样有块级作用域
  2. 声明常量时,必须赋值,一旦定义不能修改。
  3. 声明变量为复杂数据类型时,可以修改里面的值。

注意:其实const保证的不是变量值不变,而是保证变量指向的内存地址所保存的数据不允许改变。基本数据类型和引用数据类型存储值的方式不同。基本数据类型直接保存在内存地址中,因此const声明的基本类型就相当于常量。对应引用类型,变量指向的是内存地址,实际上是一个数据指针,所以const只要保证指针是固定的,指针指向的数据结构式无法控制的。

二、作用域,变量提升,函数提升

1.作用域

作用域就是变量与函数可以访问范围,即作用域控制着变量与函数的可见性和生命周期。

作用域又分为全局作用域局部作用域

  1. 全局作用域:任何地方都可以访问到的对象拥有全局作用域
  2. 局部作用域:局部作用域一般只在固定的代码片段中可以访问到,最常见的例如函数内部。

代码如下(示例):

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

如果你看懂了上面的代码那你一定知道局部作用域是怎么用的。

局部作用域有两个特点:

  1. 在函数体内,局部变量的优先级高于全局变量。如果函数体内部声明了一个与局部变量重名的变量,局部变量会覆盖全局变量。
  2. 局部变量作用域仅仅在函数内部,除了函数体之后,局部变量就会被销毁!

那也许你会见过这样的代码,代码如下(示例):

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!!

2.变量提升

在使用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

3.函数提升

函数声明有两种方式: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很重要的内容!

你可能感兴趣的:(前端面试准备,javascript,js,函数闭包)