当前位置: 首页 > article >正文

JavaScript 进阶A(作用域、闭包、变量和函数提升、函数相关只是、数组解构、对象解构、构造函数

1.作用域

作用域主要分为:局部作用域和全局作用域。

局部作用域又分为:函数作用域和块作用域

  • 函数作用域:在函数中定义的变量只能在函数内部使用,外部无法访问
  • 块作用域:被大括号{}包起来的代码块,在这个代码块中定义的变量就生存在块作用域内

 全局作用域:全局作用域可以被其他作用域访问

作用域链

作用域链就是变量查找的机制,在函数被调用的时候,会优先查找函数作用域内的变量。如果找不到再去依次向外层父作用域查找。
如果被调函数作用域内有与外部变量同名变量,优先调用函数作用域内部变量

作用域之间的关系:子作用域可以访问父作用域,父作用域不可访问子作用域

 2.闭包

我们来看一个简单的闭包实例

function outter(){
    let a =0
    function inner(){
        a++
        console.log(a)
    }
    return inner
}

let key = outter()
//实际上就是 key = inner 因为outter()最后的返回值是inner

key()
//调用一次就执行一次inner()函数

这是一个简单的计数器的例子,当我们使用key接收inner的值的时候。其实就是执行了一边outter函数然后把函数的返回值赋给key。此时的key()就是执行inner函数。

按理来说变量a会随着outter函数的结束而销毁。但是实际情况却并不是,我们通过key依旧可以访问这个变量。这里就要讲一下js的垃圾回收机制了。

js的垃圾回收机制是根据变量是否被引用而决定是否销毁它。因为在inner函数中我们通过console.log引用了外部的变量a。这就导致了内部的函数引用了外部的变量。虽然外部函数结束了,但是由于这个变量依旧被引用,所以变量得以保存

详细的过程:

1.当outter函数被执行的时候,会创建一个局部变量a
2.当inner函数被定义的时候会捕获其所在的词法环境
3.outter函数执行结束,正常情况是a被销毁,但是由于inner函数的引用,让a得以保存
4.当inner函数执行的时候仍然可以访问a,因为闭包保留了a

至于为什么不直接使用变量去实现计数器的效果,是因为闭包会把一个变量变为私有变量。正常的访问是无法访问这个变量的。只有我们通过inner函数调用的时候才会访问这个变量。这在一定程度上提升了安全性。

3.变量和函数提升

变量提升

变量的声明有三种方式:1.let  2.const  3.var

前两种我们经常会用到,但是第三种我们在之前都没怎么用过。

其实let和var本质上都是差不多的,唯一的区别在一var会存在“变量提升”

变量提升就是允许变量在声明之前被访问

我们来看两个案例:

案例1

console.log(num)

let num =10


案例2

console.log(num)

var num =10



结果:案例一报错。案例二打印undefined

通过这个我们可以很明确的发现,使用var声明的变量可以在声明语句前被访问,但是访问的结果是undefined,变量在未赋值之前访问结果是undefined。这也就说明了:变量提升只会提升变量的定义,不会提升赋值。

案例2的代码就相当于:

let a
console.log(a)
a=10

我们应该避免使用var

函数提升

函数提升和变量提升基本上一样,函数提升就是把当前作用域的函数声明提升到最前面。

我们在之前的代码中可能都会把函数声明来写到后面。

按照之前的思路来说,应该是先声明在使用。但是函数自己的定义就会有提升的效果。也就是说函数默认有函数提升。

我们之后在编写代码的时候如果有函数需要单独的写出来,我们建议写在最前面。

4.函数相关(参数、箭头函数

函数参数

动态参数

我们在编写代码的时候一般都会提前写好有几个参数,但是有的时候我们并不能确定我这个函数需要几个参数。这个时候再编写函数的代码的时候就会非常困难。

但是我们有一种方法可以解决这个问题:函数内置的arguments伪数组。

 function fn(){
      console.log(arguments.length)
      console.log(arguments[0], arguments[1], arguments[2])
      console.log(typeof arguments[0], typeof arguments[1], typeof arguments[2])
  }
  fn(1,1.1,"Www")

看这段代码,我的函数在定义的时候是没有声明形参的,但是我调用的时候却传入了三个实参。

然后我通过arguments这个函数内置的伪数组得到了这三个参数。

arguments是函数内置的伪数组,它可以存储调用函数的时候传入的参数。即使函数定义了形参我依旧可以通过arguments获取这个形参

剩余参数

剩余参数:我们在传参的时候一般都是一一对应的传参,如果传递的实参数量大于形参,剩余的没有对应的参数就是剩余参数。我们可以把这些剩余参数表示为一个真数组

下面看案例来理解:

 function fn(num,...arr){
      console.log(num);
      console.log(arr);
  }
  fn(1,2,3,4,5,7)

最后输出num是1,arr是一个数组,数组包含2,3,4,5,7

这个arr就是一个剩余参数构成的数组,一般来说我们都会把剩余参数写在参数列表的后面。

展开运算符:...数组 -》可以把数组的所有元素拿出来
假设有一个数组arr=[1,2,3,4,5,6,7] 则 ...arr = 1,2,3,4,5,6,7(不是字符串

箭头函数

箭头函数基本形式

(参数)=>{函数体}

1.如果参数只有一个 :可以省略参数的小括号: 参数=>{函数体}

2.如果函数体只有一条语句:可以省略大括号: (参数)=>语句  并且这个语句的结果就是返回值

箭头函数的this --- 继承父级的this

一个对象里面的方法(普通函数)的this指向这个对象,因为是对象调用了这个方法
一个对象里面的方法(箭头函数)的this指向window,因为箭头函数的this继承了对象的this

数组解构

作用:快速批量赋值

用法1.

arr = [1,2,3,4,5,6,7]

const [a,b,c,d,e,f,g] = arr 或者 const [a,b,c,d,e,f,g] = [1,2,3,4,5,6,7]

用法2.

有两个变量,想交换它们的值:[a,b] = [b,a]

注:const [a,b,c] = [1,2]这种情况下c是undefined
const [a,b] = [1,2,3]这种情况下会舍去多余的
可以使用剩余参数防止丢失:const[a,b,...c] = [1,2,3,4,5,6,7,8,9] 这种情况a1,b2,c是数组

对象解构

对象解构的作用和数组结构一样

我们先来回顾以下对象

对象名={对象内容},对象内容可以是变量或者函数,变量称为属性,函数称为方法。中间使用逗号隔开

obj ={
    name:"www",
    age:11,
    outer:function(){
        console.log(this.name,this.age)    
        }
}

const {name,age,outer} = obj

console.log(name,age)
outer()

具体使用没什么区别,然后要注意的是数组使用[]对象使用{}
方法结构出来可以调用它。但是outer是不能输出我们想要的内容的,主要是解构出来之后调用者从obj变成了window。

变量的名字要和属性名一致

如果想让变量不和属性一个名字可以使用const{name:aaa}这种方式让name变成aaa

 构造函数

我们首先讲一下new关键字,这个关键字用于创建一个空对象
这个空的对象里面什么都没有,我们有两个方法为这个对象添加属性或者方法
1.new 构造函数()
2.new一个对象,通过对象.属性/方法添加

这是通过构造函数:

构造函数的作用就是创建和初始化对象。他需要使用new关键字调用

下面是一个简单的构造函数:

function student(stu_name,stu_age,stu_gender,stu_add){
        this.name=stu_name;
        this.age=stu_age;
        this.gender=stu_gender;
        this.add=stu_add;
    }
    const stu1= new student("xxx",11,"nan","001100")
    console.log(stu1)

我们首先声明了一个构造函数,这个函数接收四个参数

通常来说,这个函数里面的this指的是window。但是通过new关键字调用构造函数会把new关键字创建的新对象绑定到构造函数的this。
我们只需要记住通过new关键字调用构造函数,构造函数的this指向new创建的对象
其实我们在构造函数里面打印一下this就能知道this指向的是谁了。上面的例子里打印this会打印出student

这是通过手动添加:

我们需要先创建一个空的对象:const obj = new Object()

然后obj.属性/方法    添加属性或者方法


http://www.kler.cn/a/569360.html

相关文章:

  • 2025年2月最新SCI-鹰鱼优化算法HawkFish Optimization Algorithm-附Matlab免费代码
  • 合合信息在视觉内容安全领域的创新与应用-应对伪造挑战的前沿进展
  • 游戏分组王者荣耀
  • DeepSeek-R1 论文笔记:通过强化学习提升大语言模型的推理能力
  • 【Zinx】Day1:初始 Zinx 框架
  • Pandas使用教程 - Pandas 与 Web API 交互
  • 什么是 MGX:MetaGPT
  • C++11特性(笔记一)
  • C++:vector的push_back时间复杂度分析
  • Qt的坐标
  • 手机打电话时如何识别对方按下的DTMF按键的字符-安卓AI电话机器人
  • Java中用Map<String,Object>存储层次结构
  • 力扣1584. 连接所有点的最小费用
  • 使用Docker Compose部署 MySQL8
  • Win32 C++ 电源计划操作
  • Java+Vue+uniapp微信小程序校园自助打印系统(程序+论文+讲解+安装+调试+售后)
  • 阿里管理三板斧课程和管理工具包(视频精讲+工具文档).zip
  • vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局
  • 矩阵的奇异值(SVD)分解和线性变换
  • 11.24 SpringMVC(1)@RequestMapping、@RestController、@RequestParam