IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> JS作用域和变量提升 -> 正文阅读

[游戏开发]JS作用域和变量提升

一、作用域

JS是静态作用域

let x = 10;

function f() {
? return x;
}

function g() {
? let x = 20;
? return f();
}

console.log(g()); ?// 10


解释:我们调用一个函数时,如果这个函数的变量没有在函数中定义,就去定义该函数的地方查找
相似题:
?

var n=123;
function f1(n){
? ? console.log(n);
}
function f2(){
? ? var n=456;
? ? f1(n);//456
}
f2();
console.log(n)//123


?

声明提前问题

变量声明提前

在ES6之前,我们声明变量都是使用var,使用var声明的变量都是函数作用域,即在函数体内可见,这会带来的一个问题就是声明提前。

var x = 1;
function f() {
  console.log(x);//undefined
  var x = 2;
}

f();
//等价于
var x = 1;
function f() {
  var x
  console.log(x);
  x = 2;
}

f();

函数声明提前

//成功调用
function f() {
  x();
  
  function x() {
    console.log(1);
  }
}

f();
//等价于
function f() {
  function x() {
    console.log(1);
  }
  
  x();
}

f();

但是,如果x函数如果换成函数表达式就不一样了

function f() {
  x();
  
  var x = function() {
    console.log(1);
  }
}

f();

解释:报错Uncaught TypeError: x is not a function,x其实就是一个普通变量,只是它的值是一个函数,它虽然会提前到当前函数的最顶部声明,但是这时候他的值是undefined,将undefined当成函数调用,肯定就是TypeError

变量声明和函数声明提前的优先级

函数申明的优先级更高

(function e(num){
    console.log(num);//f num(){}
    var num=10;
    function num(){}
})(100)
function b(){
    //预编译:a=undefined——>a=f a(){}
    console.log(a);//f a(){}
    var a=10;
    function a(){}
    a=100;
    console.log(a);//100
}
b()

if判断语句的变量提升解答

function m(){
    console.log(a1)//undefined
    console.log(a2)//undefined
    console.log(b1)//undefined
    console.log(b2)//undefined
    if(false){
        function b1(){}
        var a1=100;
    }
    if(true){
        function b2(){}
        var a2=10;
    }
    console.log(a1)//undefined
    console.log(a2)//10
    console.log(b1)//undefined
    console.log(b2)//f b2(){}
}
m()

预解析步骤:

1、变量:代码运行之前,先扫描有没有带var关键字的变量名,有的话,为这个变量名,在内存里开一个空间;预解释是发生在代码执行前的,所以if根本阻挡不了预解析;?

2、函数声明:在新版本的浏览器中,写在逻辑判断语句块中的函数会当作表达式来处理,不会当作函数声明,所以也不存在函数提升的问题

块级作用域

ES6引入了块级作用域。块级作用域就是指变量在指定的代码块里面才能访问,也就是一对{}中可以访问,在外面无法访问。为了区分之前的var,块级作用域使用letconst声明,let声明变量,const声明常量。

function f() {
  let y = 1;
  
  if(true) {
    var x = 2;
    let y = 2;
  }
  
  console.log(x);   // 2
  console.log(y);   // 1
}

f();

解释:x(var声明的 变量是函数作用域,即在函数f内)、y(let声明的是块级作用域,第二个y作用域在if的{}内)

不允许重复申明

块级作用域在同一个块中是不允许重复声明的

//直接报错Uncaught SyntaxError: Identifier 'a' has already been declared
var a = 1;
let a = 2;
//var可以重复声明,不报错
//var a = 1;
//var a = 2;

变量提升说明

letconst申明的变量不会提升这种说法是不准确的

var x = 1;
if(true) {
  console.log(x);
  
  let x = 2;
}

解释:报错Uncaught ReferenceError: Cannot access 'x' before initialization。如果let申明的x没有变量提升,那我们在他前面console应该拿到外层var定义的x才对。但是现在却报错了,说明执行器在if这个块里面其实是提前知道了下面有一个let申明的x的,所以说变量完全不提升是不准确的。只是提升后的行为跟var不一样,var是读到一个undefined而块级作用域的提升行为是会制造一个暂时性死区(temporal dead zone, TDZ)。暂时性死区的现象就是在块级顶部到变量正式声明这块区域去访问这个变量的话,直接报错,这个是ES6规范规定的。

循环语句中的问题

for(var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  })
}

依次输出:3 3 3

解释:因为setTimeout是异步代码,会在下次事件循环执行,而i++却是同步代码,而全部执行完,等到setTimeout执行时,i++已经执行完了,此时i已经是3了

for(let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  })
}

依次输出:0 1 2

这种写法也适用于for...infor...of循环:

let obj = {
  x: 1,
  y: 2,
  z: 3
}

for(let k in obj){
  setTimeout(() => {
    console.log(obj[k])
  })
}

link:for(const i = 0; i < 3; i++)来说,const i = 0是没问题的,但是i++肯定就报错了,所以这个循环会运行一次,然后就报错了。对于for...infor...of循环,这里换成使用const声明也是没问题的。

作用域链

作用域链其实是一个很简单的概念,当我们使用一个变量时,先在当前作用域查找,如果没找到就去他外层作用域查找,如果还没有,就再继续往外找,一直找到全局作用域,如果最终都没找到,就报错。比如如下代码:

let x = 1;

function f() {
  function f1() {
    console.log(x);
  }
  
  f1();
}

f();

这段代码在f1中输出了x,所以他会在f1中查找这个变量,当然没找到,然后去f中找,还是没找到,再往上去全局作用域找,这下找到了。这个查找链条就是作用域链。

作用域链延长

前面那个例子的作用域链上其实有三个对象:

f1作用域 -> f作用域 -> 全局作用域

大部分情况都是这样的,作用域链有多长主要看它当前嵌套的层数,但是有些语句可以在作用域链的前端临时增加一个变量对象,这个变量对象在代码执行完后移除,这就是作用域延长了。能够导致作用域延长的语句有两种:try...catchcatch块和with语句。

try...catch

这其实是我们一直在用的一个特殊情况:

let x = 1;
try {
  x = x + y;
} catch(e) {
  console.log(e);
}

上述代码try里面我们用到了一个没有声明的变量y,所以会报错,然后走到catchcatch会往作用域链最前面添加一个变量e,这是当前的错误对象,我们可以通过这个变量来访问到错误对象,这其实就相当于作用域链延长了。这个变量e会在catch块执行完后被销毁。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:58:26  更:2022-03-15 22:58:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 15:48:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码