js + 函数/变量提升 + 作用域 + 上下文 + this 应用

news2024/11/12 21:33:23

文章目录

    • js 基础应用(变量/函数/作用域)
    • 函数提升与变量提升的差异
    • this 应用
    • js 中的上下文
    • 作用域 参数 共享

js 基础应用(变量/函数/作用域)

  1. 变量声明提升实例

    • 在JavaScript中,变量声明会被提升到当前作用域的顶部。例如:
    console.log(a); // 输出:undefined
    var a = 10;
    
    • 解释:虽然变量a的赋值语句在console.log之后,但是变量声明var a会被提升到作用域的顶部。在代码执行之前,JavaScript引擎会先处理变量声明。此时a已经被声明,但是还没有赋值,所以输出undefined
  2. 函数提升实例

    • 函数声明也会被提升。例如:
    sayHello(); // 输出:Hello!
    function sayHello() {
        console.log("Hello!");
    }
    
    • 解释:函数sayHello的声明被提升到了当前作用域的顶部,所以在调用它之前,JavaScript引擎已经知道了这个函数的存在,因此可以正常调用。
    • 注意,如果是函数表达式,情况会有所不同:
    sayHi(); // 报错:Uncaught TypeError: sayHi is not a function
    var sayHi = function() {
        console.log("Hi!");
    };
    
    • 解释:这里var sayHi这个变量声明被提升了,但是赋值(也就是函数表达式部分)没有被提升。所以在调用sayHi的时候,它的值是undefined,并不是一个函数,从而导致报错。
  3. 作用域实例应用

    • 全局作用域和局部作用域
      • 全局作用域:
        var globalVar = 100;
        function globalFunction() {
            console.log(globalVar);
        }
        globalFunction(); // 输出:100
        
        • 解释:变量globalVar在全局作用域中声明,函数globalFunction也在全局作用域中定义。函数globalFunction可以访问全局变量globalVar
      • 局部作用域:
        function outerFunction() {
            var outerVar = 200;
            function innerFunction() {
                console.log(outerVar);
            }
            innerFunction(); // 输出:200
        }
        outerFunction();
        
        • 解释:outerFunction创建了一个局部作用域,变量outerVar在这个局部作用域中声明。innerFunctionouterFunction的内部,它可以访问outerFunction作用域中的变量outerVar,这是因为JavaScript的词法作用域规则,内部函数可以访问其外部函数的变量。
    • 块级作用域(ES6中的letconst
      • 例如:
        {
            let blockVar = 300;
            console.log(blockVar); // 输出:300
        }
        console.log(blockVar); 
        // 报错:Uncaught ReferenceError: blockVar is not a defined
        
        • 解释:使用let声明的变量具有块级作用域。在这个例子中,变量blockVar只在花括号{}内部的块级作用域中有效。在块级作用域外部访问它会导致报错。const也有类似的块级作用域特性,并且const声明的变量一旦赋值就不能重新赋值(对于基本数据类型)。例如:
        const PI = 3.14;
        // PI = 3.14159;
         // 报错:Uncaught TypeError: Assignment to constant variable.
        

函数提升与变量提升的差异

  1. 提升机制的本质差异
    • 变量提升
      • 对于使用var关键字声明的变量,JavaScript引擎会在执行代码之前,将变量声明提升到当前作用域的顶部。但变量的初始化不会被提升。例如:
      console.log(a); // 输出:undefined
      var a = 10;
      
      • 在这里,变量a的声明被提升了,所以在console.log语句执行时,JavaScript知道有变量a这个标识符,但是此时a还没有被赋值,所以它的值是undefined
    • 函数提升
      • 函数声明会被整个提升到当前作用域的顶部。这意味着函数的定义在代码执行之前就已经被处理好了,包括函数体内部的逻辑。例如:
      sayHello(); // 输出:Hello!
      function sayHello() {
          console.log("Hello!");
      }
      
      • 函数sayHello的整个定义被提升,所以在调用它之前,JavaScript引擎就已经知道这个函数的存在,并且可以正确地执行函数内部的代码。
  2. 提升后的行为差异
    • 变量提升后访问行为
      • 变量提升后,如果在变量初始化之前访问它,会得到undefined。而且如果在一个作用域内重复使用var声明同一个变量,JavaScript会忽略后面的声明,例如:
      var a = 10;
      var a;
      console.log(a); // 输出:10
      
      • 这是因为变量声明被提升后,后面的var a声明就相当于被忽略了,变量a的值还是最初赋值的10
    • 函数提升后访问行为
      • 函数提升后,可以在函数声明之前调用函数。而且如果在同一个作用域内重复定义同名函数,后面的函数定义会覆盖前面的函数定义。例如:
      function add(a, b) {
          return a + b;
      }
      function add(a, b) {
          return a * b;
      }
      console.log(add(2, 3)); // 输出:6
      
      • 这里后面定义的add函数覆盖了前面的add函数,所以调用add函数时,执行的是后面的乘法运算逻辑。
  3. 对作用域的影响差异
    • 变量提升对作用域的影响
      • 变量提升是基于当前作用域的。在全局作用域中声明的变量可以在全局范围内访问,在函数作用域中声明的变量只能在函数内部访问(不考虑闭包等特殊情况)。例如:
      var globalVar = 100;
      function testFunction() {
          var localVar = 200;
          console.log(globalVar); // 输出:100
          console.log(localVar); // 输出:200
      }
      testFunction();
      console.log(globalVar); // 输出:100
      console.log(localVar); // 报错:Uncaught ReferenceError: localVar is not defined
      
      • 变量globalVar在全局作用域声明,localVar在函数testFunction的局部作用域声明,它们的访问受到作用域的限制。
    • 函数提升对作用域的影响
      • 函数提升同样基于作用域。函数内部可以访问外部作用域的变量,但是外部作用域通常不能直接访问函数内部的变量(除非通过返回值等方式)。例如:
      var globalVar = 100;
      function outerFunction() {
          var outerVar = 200;
          function innerFunction() {
              console.log(outerVar); // 输出:200
              console.log(globalVar); // 输出:100
          }
          innerFunction();
      }
      outerFunction();
      
      • 函数innerFunction可以访问其外部函数outerFunction中的变量outerVar以及全局变量globalVar,体现了函数提升后的作用域访问规则。

this 应用

  1. 全局应用中的this
    • 在全局环境下(浏览器中的window对象或者Node.js中的global对象),this通常指向全局对象。
    • 例如在浏览器环境中:
    console.log(this === window); // true
    var globalVariable = 10;
    this.globalVariable; // 可以通过this访问全局变量,等同于window.globalVariable
    function globalFunction() {
        console.log(this); // 这里的this指向window
    }
    globalFunction();
    
    • 解释:在全局函数内部,this同样指向全局对象。因为全局函数实际上是作为全局对象(window)的方法来执行的。当在全局范围内定义变量时,这些变量实际上是全局对象的属性,所以可以通过this来访问。
  2. 局部函数应用中的this
    • 对象方法中的this
      • 当函数作为对象的方法被调用时,this指向该对象。例如:
      let person = {
          name: "Alice",
          sayHello: function() {
              console.log("Hello, my name is " + this.name);
          }
      };
      person.sayHello(); // 输出:Hello, my name is Alice
      
      • 解释:在sayHello方法中,this指向person对象。所以可以通过this.name访问person对象的name属性。
    • 事件处理函数中的this
      • 在HTML事件处理函数中(在浏览器环境下),this通常指向触发事件的元素。例如:
      <button onclick="console.log(this.textContent)">Click me</button>
      
      • 解释:当按钮被点击时,this指向按钮元素本身,this.textContent可以获取按钮上显示的文本内容。
    • 构造函数中的this
      • 在构造函数中,this指向新创建的对象实例。例如:
      function Person(name) {
          this.name = name;
          this.sayHello = function() {
              console.log("Hello, my name is " + this.name);
          };
      }
      let person1 = new Person("Bob");
      person1.sayHello(); // 输出:Hello, my name is Bob
      
      • 解释:当使用new关键字调用Person构造函数时,this在构造函数内部指向新创建的person1对象。通过this.name为对象添加属性,并且this.sayHello为对象添加方法。
  3. 调用this的方法
    • 作为对象方法调用
      • 如前面提到的person.sayHello()这种方式,通过对象来调用方法,this就会指向该对象。
    • 使用callapply方法
      • call方法:可以用来调用一个函数,并且指定函数内部this的指向,同时传递参数。例如:
      function greet(greeting) {
          console.log(greeting + ", " + this.name);
      }
      let person = {name: "Charlie"};
      greet.call(person, "Hi"); // 输出:Hi, Charlie
      
      • 解释:call方法的第一个参数是要绑定this的对象,后面的参数是函数greet需要的参数。在这里,thisgreet函数内部就指向了person对象。
      • apply方法:和call类似,但是传递参数的方式不同。apply接收两个参数,第一个是要绑定this的对象,第二个是一个包含函数参数的数组。例如:
      function addNumbers(a, b) {
          return this.sum + a + b;
      }
      let calculator = {sum: 10};
      let args = [2, 3];
      addNumbers.apply(calculator, args); // 输出:15
      
    • 使用bind方法
      • bind方法会创建一个新的函数,新函数内部的this会永久地绑定到指定的对象上。例如:
      function multiply(a, b) {
          console.log(this.value * a * b);
      }
      let multiplier = {value: 2};
      let boundMultiply = multiply.bind(multiplier);
      boundMultiply(3, 4); // 输出:24
      
      • 解释:bind方法返回一个新的函数boundMultiply,这个新函数内部的this永远指向multiplier对象。即使将boundMultiply作为其他对象的方法调用或者在其他作用域中调用,this的指向也不会改变。
  4. 改变this指向的方法总结
    • 主要有三种方式:callapplybind。它们的区别在于参数传递方式和是否立即执行函数。callapply会立即执行函数,而bind返回一个新的函数,需要再次调用这个新函数才会执行。这些方法在处理面向对象编程、函数复用以及事件处理等场景中非常有用,可以灵活地控制this的指向,以满足不同的业务需求。

js 中的上下文

  1. 全局上下文(Global Context)

    • 定义
      • 全局上下文是JavaScript代码执行的最外层环境。在浏览器环境中,全局上下文对应的对象是window,在Node.js环境中是global。它包含了所有全局变量和函数,这些变量和函数可以在代码的任何位置访问(除非被函数作用域或块级作用域隐藏)。
    • 实例
      • 在浏览器环境下,我们可以这样理解全局上下文:
      var globalVariable = 10;
      function globalFunction() {
          console.log("This is a global function");
      }
      console.log(window.globalVariable);
       // 输出:10,说明globalVariable是window对象的属性
      console.log(window.globalFunction);
       // 输出:function globalFunction() {...},说明globalFunction是window对象的属性
      
      • 解释:这里的globalVariableglobalFunction都是在全局上下文中定义的,它们实际上是window对象的属性和方法。当我们在浏览器中访问window对象时,可以看到这些定义的变量和函数。
      • 在Node.js环境下:
      global.sharedVariable = 20;
      function sharedFunction() {
          console.log("This is a global function in Node.js");
      }
      console.log(global.sharedVariable); // 输出:20
      console.log(global.sharedFunction); // 输出:function sharedFunction() {...}
      
      • 解释:在Node.js中,全局对象是global,和浏览器中的window类似,全局变量和函数是作为global对象的属性和方法存在的。
  2. 作用域上下文(Scope Context)

    • 定义
      • 作用域上下文决定了变量和函数的可访问性和生命周期。JavaScript有函数作用域和块级作用域。函数作用域是指在函数内部定义的变量只能在函数内部访问,块级作用域(通过letconst声明变量)是指在{}大括号内定义的变量只能在这个块内访问。
    • 函数作用域实例
      • 例如:
      function outerFunction() {
          var outerVariable = 30;
          function innerFunction() {
              console.log(outerVariable); // 输出:30,可以访问outerVariable
          }
          innerFunction();
      }
      outerFunction();
      console.log(outerVariable); 
      // 报错:Uncaught ReferenceError: outerVariable is not defined,无法在函数外部访问outerVariable
      
      • 解释:outerVariable是在outerFunction的函数作用域内定义的,innerFunction可以访问outerVariable是因为JavaScript的词法作用域规则,内部函数可以访问其外部函数定义的变量。但是在outerFunction外部,outerVariable是不可访问的。
    • 块级作用域实例
      {
          let blockVariable = 40;
          console.log(blockVariable); // 输出:40
      }
      console.log(blockVariable); 
      // 报错:Uncaught ReferenceError: blockVariable is not defined,无法在块外部访问blockVariable
      
      • 解释:这里使用let声明了blockVariable,它具有块级作用域,只能在{}这个块内部被访问。当代码执行到块外部时,变量blockVariable就不再可访问了。

作用域 参数 共享

  1. 通过返回值共享变量(函数作用域)

    • 原理
      • 在函数内部定义的变量,默认情况下其作用域仅限于函数内部。但是可以通过将变量作为函数的返回值,使得外部代码能够访问函数内部的变量。
    • 示例
      function createCounter() {
          let count = 0;
          return function() {
              count++;
              return count;
          };
      }
      let counter = createCounter();
      console.log(counter()); // 输出:1
      console.log(counter()); // 输出:2
      
      • 解释:createCounter函数内部定义了变量count,它的初始值为0。然后createCounter返回一个内部函数,这个内部函数可以访问count变量。当我们调用counter(也就是createCounter返回的内部函数)时,count变量的值会递增并返回。这样就通过返回值的方式,让外部代码能够访问和修改函数内部定义的变量。
  2. 全局变量共享(全局作用域与函数作用域交互)

    • 原理
      • 全局变量可以在函数内部访问和修改,从而实现作用域之间的交互。不过,过度使用全局变量可能会导致代码的可维护性变差。
    • 示例
      let globalVariable = 10;
      function modifyGlobalVariable() {
          globalVariable += 5;
          console.log(globalVariable); // 输出:15
      }
      modifyGlobalVariable();
      console.log(globalVariable); // 输出:15
      
      • 解释:在这个例子中,globalVariable是一个全局变量。modifyGlobalVariable函数可以访问并修改这个全局变量。在函数内部修改globalVariable后,其值在函数外部也会发生改变,因为它们引用的是同一个变量。
  3. 闭包共享变量(函数作用域与外部作用域)

    • 原理
      • 闭包是指有权访问另一个函数内部变量的函数。一个函数返回了另一个函数,并且这个返回的函数引用了外部函数的局部变量,就形成了闭包。通过闭包可以在不同的函数调用之间保持变量的状态,并且可以从外部访问内部变量。
    • 示例
      function outerFunction() {
          let privateVariable = 20;
          function innerFunction() {
              console.log(privateVariable);
          }
          return innerFunction;
      }
      let closureFunction = outerFunction();
      closureFunction(); // 输出:20
      
      • 解释:outerFunction返回innerFunctioninnerFunction可以访问outerFunction内部的privateVariable,形成了闭包。当我们调用closureFunction(也就是innerFunction)时,它能够访问privateVariable,即使outerFunction已经执行完毕。这样就实现了从外部访问函数内部变量的效果,并且通过闭包可以在不同的时间点访问和操作这些变量。
  4. 通过对象属性共享(函数作用域与对象作用域)

    • 原理
      • 可以将函数内部的变量作为对象的属性,通过返回对象或者修改对象属性的方式,让外部代码能够访问这些变量。
    • 示例
      function createObjectWithVariable() {
          let internalVariable = 30;
          return {
              getVariable: function() {
                  return internalVariable;
              },
              setVariable: function(newValue) {
                  internalVariable = newValue;
              }
          };
      }
      let objectWithVariable = createObjectWithVariable();
      console.log(objectWithVariable.getVariable()); // 输出:30
      objectWithVariable.setVariable(40);
      console.log(objectWithVariable.getVariable()); // 输出:40
      
      • 解释:createObjectWithVariable函数内部定义了internalVariable,然后返回一个对象,这个对象有两个方法getVariablesetVariablegetVariable方法可以获取internalVariable的值,setVariable方法可以修改它的值。通过这种方式,外部代码可以通过对象的方法来访问和修改函数内部的变量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2237273.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【R78/G15 开发板测评】串口打印 DHT11 温湿度传感器、DS18B20 温度传感器数据,LabVIEW 上位机绘制演化曲线

【R78/G15 开发板测评】串口打印 DHT11 温湿度传感器、DS18B20 温度传感器数据&#xff0c;LabVIEW 上位机绘制演化曲线 主要介绍了 R78/G15 开发板基于 Arduino IDE 环境串口打印温湿度传感器 DHT11 和温度传感器 DS18B20 传感器的数据&#xff0c;并通过LabVIEW上位机绘制演…

基于MFC实现的赛车游戏

一、问题描述 游戏背景为一环形车道图&#xff0c;选择菜单选项“开始游戏”则可开始游戏。游戏的任务是使用键盘上的方向键操纵赛道上的蓝色赛车追赶红色赛车&#xff0c;红色赛车沿车道顺时针行驶&#xff0c;出发点和终点均位于车道左上方。任一赛车先达到终点则比赛结束。…

实验三 JDBC数据库操作编程(设计性)

实验三 JDBC数据库操作编程&#xff08;设计性&#xff09; 实验目的 掌握JDBC的数据库编程方法。掌握采用JDBC完成数据库链接、增删改查&#xff0c;以及操作封装的综合应用。实验要求 本实验要求每个同学单独完成&#xff1b;调试程序要记录调试过程中出现的问题及解决办法…

Java期末复习暨学校第二次上机课作业

Java期末复习暨学校第二次上机课作业&#xff1a;了解程序的控制结构&#xff0c;掌握顺序结构程序的设计方法&#xff0c;掌握分支程序设计方法。 第一题&#xff1a; 闰年有两种判断方式&#xff1a; &#xff08;1&#xff09;&#xff1a;能被4整除但不能被100整除 &…

Windows配置NTP时间同步

Windows下实现NTP时间同步 1、Windows时间服务(W32Time)2、Windows 时间同步的工作原理3、配置和管理 Windows 时间同步3.1 命令行工具&#xff1a;w32tm3.2 控制面板中的设置 4. 高级设置&#xff08;Windows Server 环境&#xff09;5.调整时间同步的间隔5.1 通过组策略调整时…

Go八股(Ⅳ)***slice,string,defer***

***slice&#xff0c;string&#xff0c;defer*** 1.slice和arry的区别 arry&#xff1a; Go语言中arry即为数据的一种集合&#xff0c;需要在声明时指定容量和初值&#xff0c;且一旦声明就长度固定&#xff0c;访问时按照索引访问。通过内置函数len可以获取数组中的元素个…

STM32H503开发(2)----STM32CubeProgrammer烧录

STM32H503开发----2.STM32CubeProgrammer烧录 概述硬件准备视频教学样品申请源码下载参考程序自举模式BOOT0设置UART烧录USB烧录 概述 STM32CubeProgrammer (STM32CubeProg) 是一款用于编程STM32产品的全功能多操作系统软件工具。 它提供了一个易用高效的环境&#xff0c;通过…

ubuntu下aarch64-linux-gnu(交叉编译) gdb/gdbserver(二)

ubuntu下aarch64-linux-gnu(交叉编译) gdb/gdbserver&#xff08;二&#xff09; 本教程作为gdb/gdbserver编译安装教程的一个补充&#xff0c;教会大家如何使用gdb/gdbserver进行远程调试。 如上图所示&#xff0c;我们需要将编译后的gdbserver上传至目标设备&#xff0c;其上…

架构零散知识点

1 数据库 1.1 数据库范式 有一个学生表&#xff0c;主键是学号&#xff0c;含有学生号、学生名、班级、班级名&#xff0c;违反了数据库第几范式&#xff1f; --非主属性不依赖于主键&#xff0c;不满足第二范式 有一个订单表&#xff0c;包含以下字段&#xff1a;订单ID&…

【时间之外】IT人求职和创业应知【31】

目录 新闻一&#xff1a;2024年“秦创原沣东杯”陕西省科技工作者创新创业大赛颁奖仪式暨沣东新城机器人产业发展大会盛大启幕 新闻二&#xff1a;声网CEO赵斌&#xff1a;RTE将成为生成式AI时代AI Infra的关键部分 新闻三&#xff1a;“5G工业互联网”融合应用试点城市名单…

移动开发(七):.NET MAUI使用RESTAPI实现查询天气笔记

目录 一、接口准备 二、实体部分 三、页面部分 四、后台代码逻辑 五、总结 在移动开发过程中,第三方对接是非常常见的。今天给大家分享.NET MAUI如何使用REST API实现输入城市名称查询天气的示例,希望对大家学习.NET MAUI可以提供一些帮助! 一、接口准备 首先我们需要…

Javascript中如何实现函数缓存?函数缓存有哪些应用场景?

#一、是什么 函数缓存&#xff0c;就是将函数运算过的结果进行缓存 本质上就是用空间&#xff08;缓存存储&#xff09;换时间&#xff08;计算过程&#xff09; 常用于缓存数据计算结果和缓存对象 解释 const add (a,b) > ab; const calc memoize(add); // 函数缓存…

【网络安全 | 并发问题】Nginx重试机制与幂等性问题分析

未经许可,不得转载。 文章目录 业务背景Nginx的错误重试机制proxy_next_upstream指令配置重试500状态码非幂等请求的重试问题幂等性和非幂等性请求non_idempotent选项的使用解决方案业务背景 在现代互联网应用中,高可用性(HA)是确保系统稳定性的关键要求之一。为了应对服务…

利用游戏引擎的优势

大家好&#xff0c;我是小蜗牛。 在当今快速发展的游戏产业中&#xff0c;选择合适的游戏引擎对开发者来说至关重要。Cocos Creator作为一款功能强大且灵活的游戏引擎&#xff0c;为开发者提供了丰富的工具和资源&#xff0c;使他们能够高效地开发出优秀的游戏。本文将探讨如何…

财务源码 财务软件 SaaS 云财务

&#x1f50d; 专业财务源码&#xff0c;助您快速开展财务管理&#xff01;&#x1f4c8; &#x1f3af; 我们提供一系列高质量、可定制、易于使用的财务源码&#xff0c;帮助您快速搭建强大的财务管理系统。无论是小型企业、中型企业&#xff0c;还是个人用户&#xff0c;我们…

数据流图,学习笔记

目录 一、数据流图的基本元素 外部实体&#xff08;External Entity&#xff09; 加工&#xff08;Process&#xff09; 数据存储&#xff08;Data Store&#xff09; 数据流&#xff08;Data Flow&#xff09; 二、数据流图的层次结构 顶层数据流图 中层数据流图 底层…

docker镜像文件导出导入

1. 导出容器&#xff08;包含内部服务&#xff09;为镜像文件&#xff08;docker commit方法&#xff09; 原理&#xff1a;docker commit命令允许你将一个容器的当前状态保存为一个新的镜像。这个新镜像将包含容器内所有的文件系统更改&#xff0c;包括安装的软件、配置文件等…

Android中桌面小部件framework层使用到的设计模式

在Android中&#xff0c;桌面小部件&#xff08;App Widget&#xff09;的Framework层采用了多种设计模式&#xff0c;以实现模块化、可维护性和高效的交互。 以下是Android桌面小部件Framework层中常用的设计模式及其具体应用&#xff1a; 1. 观察者模式&#xff08;Observe…

7.《双指针篇》---⑦三数之和(中等偏难)

题目传送门 方法一&#xff1a;双指针 1.新建一个顺序表用来返回结果。并排序数组。 2.for循环 i 从第一个数组元素遍历到倒数第三个数。 3.如果遍历过程中有值大于0的则break&#xff1b; 4.定义左右指针,以及target。int left i 1, right n - 1; int target -nums[i];…

DBeaver工具连接Hive

DBeaver工具连接Hive 首先解压安装包dbeaver-ce-latest-x86_64-setup.zip,并安装dbeaver-ce-latest-x86_64-setup.exe; 安装Kerberos客户端4.1-amd64.msi; 查看集群节点/etc/hosts文件内容,并追加到C:\Windows\System32\drivers\etc\hosts; 下载集群用户keytab文件,并解压…