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高级--

执行上下文与执行上下文栈

变量提升与函数提升

1.变量的声明提升
通过var定义(声明)的变量,在定义语句之前就可以访问到,值为:undefined

2.函数声明提升
通过function声明的函数,在之前就可以直接调用,值为:函数定义(对象)
3.问题:变量提升和函数提升是如何产生的?

在这里插入图片描述

<script>
         /* 面试题 */
        //  输出a的值
        var a = 3;
        function fn(){
            console.log(a);
            var a = 4;
        }

        fn(); //a=undefined


        console.log(b); //undefined 变量提升
        fn2(); //可调用   函数提升
        // fn3(); 不能调用,变量提升

        var b = 3;
        function fn2(){
            console.log("fn2()");
        }

        var fn3 = function(){
            console.log("fn3()");
        };
    </script>

执行上下文

1.代码分类(位置)

  • 全局代码
  • 函数(局部)代码

2.全局执行上下文

  • 在执行全局代码前将window确定为全局执行上下文
    -对全局数据进行预处理
  • var 定义的全局变量–>undefined,添加为window的属性
  • function声明的全局函数–>赋值(fun),添加为window的方法
  • this–>赋值(window)
    开始执行全局代码

函数执行上下文

在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)

对局部数据进行预处理

  • 形参变量–>赋值(实参)–>添加为执行上下文的属性
  • arguments–>赋值(实参列表),添加为执行上下文属性
  • var 定义的局部变量–>undefined,添加为执行上下文的属性
  • function声明的函数–>赋值(fun),添加为执行上下文的方法
  • this–>赋值(调用函数的对象)
  • 开始执行函数体代码

在这里插入图片描述
执行的时候,如果设置断点,可以看到,functiona2()是被跳过的,因为一开始执行了,就不会再执行了。
在这里插入图片描述

执行上下文栈

1.在全局代码执行前,js引擎就会创建一个栈来存储管理所有的执行上下文对象
2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
3.在函数执行上下文创建后,将其添加到栈中(压栈)
4.在当前函数执行完后,将栈顶的对象移除(出栈)
5.当所有的代码执行完之后,栈中只剩下window

执行上下文对象,是在调用bar的时候产生的,一共n+1次,是指n次调用function+1次调用window
在这里插入图片描述
当前执行的一定是栈顶的,执行完栈顶,再向下执行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

执行上下文测试题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

作用域与作用域链

作用域

在这里插入图片描述
在这里插入图片描述

作用域与执行上下文

作用域与执行上下文的区别:
区别1

  • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时,
  • 全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建
  • 函数执行上下文是在调用函数时,函数体代码执行之前创建

区别2

  • 作用域是静态的,只要函数定义好了就一直存在,且不会再变化
  • 执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放

联系

  • 上下文环境(对象)是从属于所在的作用域
  • 全局上下文环境–>全局作用域
  • 函数上下文环境–>对应的函数作用域

在这里插入图片描述
在这里插入图片描述

作用域链

在这里插入图片描述
在这里插入图片描述

作用域测试题

在这里插入图片描述
在这里插入图片描述

闭包

引入

在这里插入图片描述

闭包的概念

在这里插入图片描述
在这里插入图片描述

闭包的作用

在这里插入图片描述

闭包的生命周期

在这里插入图片描述

闭包的应用

需要先创建一个js模块,把这些代码放在js中
通过return 向外暴露,前面的doSomething 是一个字符串(就是外面调用方法的时候的方法名) 后面的doSomething是一个函数名
在这里插入图片描述
通过匿名函数自定义向外暴露
Q:怎么样向外暴露,把想暴露的属性添加到window中
这个“实参”写window,实现代码的压缩(比如我可以直接写一个w)
****

闭包的应用

在这里插入图片描述
在这里插入图片描述

闭包的缺点

在这里插入图片描述

内存溢出与内存泄露

在这里插入图片描述

闭包测试题

测试题1:
()()代表访问里面的函数体
在这里插入图片描述
测试题2:
在这里插入图片描述

函数高级总结

原型与原型链:

所有函数都有一个特别的属性:

prototype : 显式原型属性

所有实例对象都有一个特别的属性:

__proto__ : 隐式原型属性

显式原型与隐式原型的关系

函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值

原型对象即为当前实例对象的父对象

原型链

所有的实例对象都有__proto__属性, 它指向的就是原型对象
这样通过__proto__属性就形成了一个链的结构---->原型链
当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找
当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作

执行上下文与执行上下文栈:

变量提升与函数提升

变量提升: 在变量定义语句之前, 就可以访问到这个变量(undefined)
函数提升: 在函数定义语句之前, 就执行该函数
先有变量提升, 再有函数提升

理解

执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性
执行上下文栈: 用来管理产生的多个执行上下文

分类:

全局: window
函数: 对程序员来说是透明的

生命周期

全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡
函数 : 调用函数时产生, 函数执行完时死亡

包含哪些属性:
全局 :

用var定义的全局变量 ==> undefined
使用function声明的函数 ==> function
this ==> window

函数

用var定义的局部变量 ==> undefined
使用function声明的函数 ==> function
this ==> 调用函数的对象, 如果没有指定就是window
形参变量 ==> 对应实参值
arguments ==> 实参列表的伪数组

执行上下文创建和初始化的过程

全局:
	在全局代码执行前最先创建一个全局执行上下文(window)
	收集一些全局变量, 并初始化
	将这些变量设置为window的属性
函数:
	在调用函数时, 在执行函数体之前先创建一个函数执行上下文
	收集一些局部变量, 并初始化
	将这些变量设置为执行上下文的属性

作用域与作用域链:

理解:

作用域: 一块代码区域, 在编码时就确定了, 不会再变化
作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量

分类:

全局
函数
js没有块作用域(在ES6之前)

作用

作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突
作用域链: 查找变量

区别作用域与执行上下文

作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了
执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失
联系: 执行上下文环境是在对应的作用域中的

闭包:

理解:

当嵌套的内部函数引用了外部函数的变量时就产生了闭包
通过chrome工具得知: 闭包本质是内部函数中的一个对象, 这个对象中包含引用的变量属性

闭包作用:

延长局部变量的生命周期
让函数外部能操作内部的局部变量
function fn1() {
  var a = 2;
  function fn2() {
    a++;
    console.log(a);
  }
  return fn2;
}
var f = fn1();
f();
f();

闭包应用:


模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为
循环遍历加监听
JS框架(jQuery)大量使用了闭包


缺点:

变量占用内存的时间可能会过长
可能导致内存泄露

解决:
及时释放:f = null; //让内部函数对象成为垃圾对象

内存溢出与内存泄露:

内存溢出

一种程序运行出现的错误
当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误

内存泄露

占用的内存没有及时释放
内存泄露积累多了就容易导致内存溢出
  常见的内存泄露:
		意外的全局变量
		没有及时清理的计时器或回调函数
		闭包

对象高级

对象创建模式

Object构造函数模式

在这里插入图片描述

对象字面量

在这里插入图片描述

工厂模式

在这里插入图片描述

自定义构造函数模式

在这里插入图片描述

构造函数+原型的组合模式

在这里插入图片描述

继承模式

原型链继承

在这里插入图片描述

在这里插入图片描述

借用构造函数继承

在这里插入图片描述

组合继承

  • call方法那个地方相当于this.person(name,age){…}但是这句话是不能写的, 因为一开始是没有person函数的、、
  • call代表的是借用执行,this来执行person,this指的是new出来的S

在这里插入图片描述

对象高级总结

对象的创建模式:

  • Object构造函数模式
var obj = {};
obj.name = 'Tom'
obj.setName = function(name){this.name=name}

  • 对象字面量模式
var obj = {
  name : 'Tom',
  setName : function(name){this.name = name}
}

  • Object构造函数模式
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.setName = function(name){this.name=name;};
}
new Person('tom', 12);

  • 构造函数+原型的组合模式
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.setName = function(name){this.name=name;};
new Person('tom', 12);

继承模式:

原型链继承

  • 原型链继承 得到方法
function Parent(){}
Parent.prototype.test = function(){};
function Child(){}
Child.prototype = new Parent(); // 子类型的原型指向父类型实例
Child.prototype.constructor = Child
var child = new Child(); //有test()

  • 借用构造函数 : 得到属性
function Parent(xxx){this.xxx = xxx}
Parent.prototype.test = function(){};
function Child(xxx,yyy){
    Parent.call(this, xxx);//借用构造函数   this.Parent(xxx)
}
var child = new Child('a', 'b');  //child.xxx为'a', 但child没有test()

  • 组合
function Parent(xxx){this.xxx = xxx}
Parent.prototype.test = function(){};
function Child(xxx,yyy){
    Parent.call(this, xxx);//借用构造函数   this.Parent(xxx)
}
Child.prototype = new Parent(); //得到test()
var child = new Child(); //child.xxx为'a', 也有test()

进程与线程

  1. 进程:程序的一次执行,它战友一片独有的内存空间
  2. 线程:是进程内一个独立的执行单元,是程序执行的一个完整流程,是CPU最小 的调度单元
    在这里插入图片描述
  3. 一个进程里面,一个线程:单线程,多个进程:多线程
  4. 程序运行在某个进程某个线程上

**在这里插入图片描述**
在这里插入图片描述

浏览器内核

在这里插入图片描述

定时器引发的思考

1.定时器真的是定时执行的吗?

  • 定时器不能保证真正定时执行
  • 一般会延迟一点点,也有可能延长很长时间

2.定时器回调函数是在分线程执行的吗?
在主线程执行的,js是单线程的
在这里插入图片描述

在这里插入图片描述
加上长时间执行那个程序之后,定时器执行的时间就说不准了

js是单线程执行的

在这里插入图片描述
加上了一个时间为0的(初始化代码->回调代码)
在这里插入图片描述
1.如何证明js执行是单线程的?

  • setTimeout()的回调函数是在主线程执行的
  • 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行

2.为什么js要用单线程模式,而不是用多线程模式?

  • JavaScript的单线程,与它的用途有关
  • 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM
  • 这决定了它只能是单线程,否则会带来很复杂的同步问题

3.代码的分类:

  • 初始化代码
  • 回调代码

4.js引擎执行代码的基本流程

  • 先执行初始化代码:包含一些特别的代码 回调函数(异步执行)
    异步执行指的是某些函数代码,必须在所有的初始化代码执行完之后才执行
  • 设置定时器
  • 绑定事件监听
  • 发送ajax请求
  • 后面在某个时刻才会执行回调代码

事件循环模型

在这里插入图片描述

  1. stack栈,栈中存放的对象
  2. 定时器肯定不会有问题的,到了一定的时间点,就会把回调函数放在下面的callback队列当中(待执行),如果前面初始化代码执行很长时间,回调函数就会延迟等待。
  3. 初始化代码执行完了之后才有可能执行回调代码
    在这里插入图片描述
    在这里插入图片描述

H5 Web Workers多线程

在这里插入图片描述
未使用H5线程
在这里插入图片描述
主线程

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<script type="text/javascript">
		window.onload=function(){
			var input = document.getElementById("number");
		var btn = document.getElementById("btn");
		btn.onclick = function () {

			var number = input.value;
			//console.log(number);
			//创建一个worker对象(链接分线程)
				var worker=new Worker("./script/worker.js");

				//向分线程发送消息
				worker.postMessage(number);
				console.log("主线程向分线程发送的数据: "+number);
			//function是异步的,不论写在那,都在postMessage之后(因为它是初始化代码,function是回调函数)
			//接受消息的节点
				worker.onmessage=function(event){
					console.log("主线程向分线程返回的数据 "+event.data);
console.log(this);
			}
		}
		}
	</script>
</head>

<body>
	<input type="text" name="" id="number" placeholder="数值">
	<button id="btn">返回结果</button>
</body>

</html>

分线程

//分线程

function fab(n){
    return n<=2 ? 1 : fab(n-1)+fab(n-2);
}
//这个地方写this.message=也行
var onmessage=function(event){
  
    var number=event.data;
    console.log("分线程接受主线程发送的数据: "+number);
var result=fab(number);
postMessage(result);
console.log("分线程计算完毕,返回结果 "+result);

//alert(result);alert是window的方法,调用会出错,分线程不可以使用

}
  游戏开发 最新文章
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-04-07 23:02:30  更:2022-04-07 23:04:33 
 
开发: 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 20:58:54-

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