从数据类型到变量、作用域、执行上下文
从数据类型到变量、作用域、执行上下文
JS数据类型
分类
1》基本类型:字符串String、数字Number、布尔值Boolean、undefined、null、symbol、bigint
2》引用类型:Object (Object、Array、Function、Date、RegExp、Error、Arguments)
Symbol是ES6新出的一种j基本数据类型,特点就是没有重复的数据,所以它可以作为object的key。
数据的创建方法Symbol(),因为它的构造函数不够完整,所以不能使用new Symbol()创建数据。由于Symbol()创建数据具有唯一性,所以 Symbol() !== Symbol(), 同时使用Symbol数据作为key不能使用for获取到这个key,需要使用Object.getOwnPropertySymbols(obj)获得这个obj对象中key类型是Symbol的key值
const s1 = Symbol("hi");console.log(s1); // Symbol(hi)const s2 = Symbol("hi");console.log(s1 == s2); // falselet obj = {};obj[s1] = "hello";obj[s2] = "world";const keys = Object.getOwnPropertySymbols(obj);console.log(keys); // [Symbol(hi), Symbol(hi)];
bigint大整数,不能用于Math 对象中的方法;不能和任何Number实例混合运算,两者必须转换成同一种类型
const bigNum1 = BigInt(1);const bigNum2 = BigInt(2);const num = Number(bigNum2 - bigNum1); // 1
数据类型判断
typeof
typeof检测原始类型(基本数据类型)的具体类型
返回值:数据的类型;
不能判断null的类型,如果是null,返回的是object
能判断函数的类型,函数返回的是function
console.log(typeof 666); // numberconsole.log(typeof "nb"); // stringconsole.log(typeof true); // booleanconsole.log(typeof undefined); // undefinedconsole.log(typeof null); // objectconsole.log(typeof [1, 2]); // objectconsole.log(typeof { content: "hi" }); // objectfunction func(){console.log("月亮不睡我不睡");};console.log(typeof func); // function
补充:任何实现内部使用了call方法的对象,使用typeof都返回function,比如正则表达式;在Chrome7和Safari5及之前版本,上述浏览器的正则表达式内部实现使用了call,但是后面都是返回object了
instanceof
适用于检测引用类型的具体类型
返回值:布尔值
自己定义的构造函数,new 出来的实例,也可以检测
console.log([1, 2] instanceof Array); //trueconsole.log({ content: "hi" } instanceof Object); //trueconst fn = () => {console.log("不是秃头小宝贝");};console.log(fn instanceof Function); //trueconsole.log([1, 2] instanceof Object); //trueconsole.log({ content: "hi" } instanceof Array); //falsefunction Person(name) {this.name = name;}const p = new Person("luka");console.log(p instanceof Person); //true// instanceof不能检测基本数据类型console.log(66 instanceof Number); //falseconsole.log(null instanceof Object); //false
Object.prototype.toString
const toString = Object.prototype.toString;console.log(toString.call(undefined)); // [object Undefined]console.log(toString.call(null)); // [object Null]console.log(toString.call(true)); // [object Boolean]console.log(toString.call(1)); // [object Number]console.log(toString.call("")); // [object String]console.log(toString.call({})); // [object Object]console.log(toString.call([])); // [object Array]console.log(toString.call(() => {})); // [object Function]console.log(toString.call(new Date())); // [object Date]
构造函数
对象的constructor指向创建该实例对象的构造函数
注意 null
和 undefined
没有 constructor
,以及 constructor
可以被改写,不太可靠
const arr = [1, 2, 3];console.log(arr.constructor === Array); // true
全等===
console.log(null === null);console.log(undefined === undefined);
变量
变量的存储方式
基本数据类型存在栈中,引用类型存在堆中
栈中存数据想罐子中放东西,先放的放在底下,取出的时候,最后才拿出来;栈中先进后出
堆数据结构是树状结构,从堆中拿数据就像从书架中找书
垃圾回收:基本思路就是确定变量不会再使用,就释放它的内存,这个过程是周期性的,隔一段时间回收一次
一般是函数的上下文执行完毕,函数上下文退出函数调用栈,解除变量引用,内存释放,等待垃圾回收;全局的上下文是再退出的时候才被销毁,所以我们尽量减少全局变量,也要尽量减少闭包
变量声明
var let const关键字都可以声明变量
比较三者:
1》变量提升不同;var 存在变量提升,let、const没有
console.log(num); // undefinedvar num = 1;
console.log(num); // ReferenceError: Cannot access 'num' before initializationlet num = 1;
let 在声明前使用变量会报错,也就是我们说的暂时性死区
2》作用域不同;var 声明的变量作用域在最近的上下文中(全局或函数的上下文),而let是块级作用域
var定义的age实在全局作用域中
if (true) {var age = 20;}console.log(age); // 20
if形成了块级作用域,全局作用中域访问不到就报错了
if (true) {let age = 20;}console.log(age); // ncaught ReferenceError: age is not defined
3》声明同名变量不同;var 可以在同一作用域下声明同名变量,而let不行,会报错
var str = "hihi";var str = "嗨嗨";console.log(str); //嗨嗨
let str = "hihi";let str = "嗨嗨"; // Uncaught SyntaxError: Identifier 'str' has already been declaredconsole.log(str); // 嗨嗨
4》在全局上下文中声明变量,var声明的变量会添加到window对象中(如果没有使用var来声明,直接赋值也会添加到window对象上,不会报错),而let声明的变量不会被添加到window对象中
var num1 = 20;let num2 = 22;console.log(window.num1); // 20console.log(window.num2); // undefined
const和let差不多,唯一不同的时它声明的是个常量,声明时必须赋值,声明之后,就不能重新赋值(引用类型只要不改变引用地址就行)
变量提升
一个上下文创建的时候会创建变量对象,创建变量对象的过程:
1》建立argument对象,他是一个伪数组,属性名是:0,1,……,属性值就是传入的值
2》函数提升;在变量对象中创建一个变量名为函数名,值为指向函数内存地址的引用
3》变量提升;在变量对象中以变量名建立一个属性,属性值为undefined;但是let/const声明的变量没有赋值undefined,所以不能提前使用
console.log(num);var num = 0;
相当于:
var num
console.log(num)
num=0
函数的提升先于变量的提升,首先函数的声明提升,然后变量提升
如果 var 变量与函数同名,则在这个阶段,以函数值为准,在下一个阶段,函数值会被变量值覆盖
console.log(cal); // cal函数var cal = 10;function cal(num1, num2) {return num1 - num2;}console.log(cal); // 10
相当于:
function cal() {return num1 - num2;};var cal;console.log(cal);cal = 10;console.log(cal);
预编译
在上下文创建以后, 并不会立即执行JS代码, 而是会先进行一个预编译的过程, 根据上下文的不同, 预编译又可以分为:
- 全局预编译
- 函数预编译每个执行上下文都有一个与之相关联的变量对象 (Variable Object, 简称 VO, 初其实就是一个对象:
{key : value}
形式) , 当前执行上下文中所有的变量
和函数
都添加在其中
预编译大致过程:
1》function关键字声明的函数进行提升,声明的函数就是函数本身,但是不会执行
2》var关键字声明变量会进行提升, 属性值置为 undefined
3》如果函数名与变量名冲突(相同), 函数声明会将变量声明覆盖, 属性值就是函数本身
4》预编译结束以后, 再逐行执行代码
console.log(logA); // function logA(a){log(a)}function logA(a) {console.log(a);}var logA = 2;logA(logA); // 报错:logA is not a function
执行上下文与作用域链
执行上下文
执行上下文就是当前代码的执行环境;
上下文有全局上下文和函数上下文,函数上下文是在函数被调用的时候产生的;
var globalValue = "shake you body";function greet() {alert("hello");}greet();
JavaScript引擎会以栈的方式来处理上下文,这个栈,我们称其为函数调用栈(call stack)。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文
首先全局上下文进栈,greet被调用时,产生一个函数上下文,进入栈中
上下文放在栈中,栈中的先进后出,就像往一个罐子中放东西,拿出来的时候,先拿出的是罐子顶部的东西,也就是后放进去的东西,那么首先进栈的全局上下文是在栈底,最先进入,但是最后出来;函数上下文在函数被调用时产生,放入栈中,执行完后退出来;而全局上下文在程序退出前出栈
函数中,遇到return能直接终止可执行代码的执行,因此会直接将当前上下文弹出栈
同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
全局上下文只有唯一的一个,它在浏览器关闭时出栈
函数的执行上下文的个数没有限制
每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此
上下文的生命周期:
1》创建阶段
这个阶段会创建变量对象、确定this指向,作用域链等
2》执行阶段
完成变量的赋值,执行其他js代码等
上下文在执行阶段,该上下文的变量对象VO就变为了活动对象AO
3》销毁阶段
执行上下文出栈,对应的引用失去内存空间,等待回收
变量对象
变量对象创建过程:
1》创建arguments 对象,检查当前上下文的参数,建立对应属性与属性值
2》检查当前上下文的函数声明,在变量对象中以函数名建立一个属性
3》检查当前上下文的变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined,const/let 声明的变量没有赋值,不能提前使用
如果 var 变量与函数同名,则在这个阶段,以函数值为准,在下一个阶段,函数值会被变量值覆盖
这就解释了变量提升,以及变量提升中function声明比var声明优先级高一些
作用域
作用域就是一套规则,它规定了一个变量可以使用的范围
可分为:
1》全局作用域
2》函数作用域
3》块级作用域
作用域链
作用域链,是由当前环境与上层环境的一系列变量对象组成,保证对执行环境有权访问的所有变量和函数的有序访
问
上下文创建的时候会创建变量对象arguments,并确定变量对象的作用域链
在一个执行上下文中,首先会在当前执行上下文的变量对象中查找,没有找到会往一层上下文中的变量对象上找;这种(单向)链式查找的机制被称为作用域链
var a = 20;
function test() {var b = a + 10;function innerTest() {var c = 10;return b + c;}return innerTest();
}
test();
下面代码的作用域:
content greet(全局变量对象) ----- content response (greet函数变量对象))----- str(response函数变量对象)
response变量对象中没有找到,它就往greet变量对象中找,如果greet函数的变量对象中也没有,就从全局变量对象中找
console.log(content); // hellofunction greet() {var content = "hi";console.log(content); // hifunction response() {var str = "你好";console.log(content); //hi}response();}greet();
首先全局上下文进栈,greet被调用时,产生一个函数上下文,进入栈中
上下文放在栈中,栈中的先进后出,就像往一个罐子中放东西,拿出来的时候,先拿出的是罐子顶部的东西,也就是后放进去的东西,那么首先进栈的全局上下文是在栈底,最先进入,但是最后出来;函数上下文在函数被调用时产生,放入栈中,执行完后退出来;而全局上下文在程序退出前出栈
上下文在其所有代码都执行完毕后被销毁
es6增加了块级作用域的概念:由最近的一堆{}花括号界定;if块、while块、function块
深拷贝与浅拷贝
浅拷贝
浅拷贝和深拷贝的主要区别在于是否完全独立于原始对象
浅拷贝:对于对象上的每一个属性,如果是简单类型,直接赋值值,如果是引用类型,赋值的是引用地址
const obj1 = {name: "马冬梅",more: {boyfriend: "unknow",},};const obj2 = { ...obj1 };obj1.name = "马什么梅";obj1.more.boyfriend = "none";
修改后两个对象的name不一致,boyfriend一致
实现方式
1>扩展运算符…
const arr = [1, 2, 3];const copyArr = [...arr];
2>Object.assign()
const obj1 = {name: "马冬梅",more: {boyfriend: "unknow",},};const obj2 = Object.assign({}, obj1);
const arr = [1, { name: "hello" }, 3];const copyArr = Object.assign([], arr);arr[1] = 6; // 两者索引为1的值不一样
3>Array.prototype.concat()
const arr = [1, { name: "hello" }, 3];copyArr = [].concat(arr);
4>Array.prototype.slice()
const arr = [1, { name: "hello" }, 3];copyArr = arr.slice();
深拷贝
创建一个全新的对象,新对象与原始对象具有不同的内存地址
两者相互独立,互不影响
let obj1 = { name: "马什么梅" };let obj2 = obj1;console.log(obj2); // { name: "马什么梅" }obj2.name = "马冬梅";console.log(obj1.name, obj2.name); // 马冬梅 马冬梅
实现方式
1>JSON
JSON.parse(JSON.stringify(obj))
JSON使用方便,但是它也有一些坑
当对象中有undefined类型或function类型的数据时 — undefined和function会直接丢失
const object1 = {name: undefined,fn: (v) => v,};const object2 = JSON.parse(JSON.stringify(object1));
当对象中有时间类型的元素时候 -----时间类型会被变成字符串类型数据
const object1 = {date: new Date(),};const object2 = JSON.parse(JSON.stringify(object1));console.log(typeof object1.date === typeof object2.date); // false,'object'!=='string'
当对象中有NaN、Infinity和-Infinity这三种值的时候 — 会变成null
const object1 = {isNum: NaN,};const object2 = JSON.parse(JSON.stringify(object1));console.log(object2); // { isNum: null }
当对象循环引用的时候 --会报错
const obj = {objChild: null,};obj.objChild = obj;const objCopy = JSON.parse(JSON.stringify(obj));console.log(objCopy, "objCopy"); // 报错 Converting circular structure to JSON
2>递归
const cloneDeep1 = (target, hash = new WeakMap()) => {// 对于传入参数处理if (typeof target !== 'object' || target === null) {return target;}// 哈希表中存在直接返回if (hash.has(target)) return hash.get(target);const cloneTarget = Array.isArray(target) ? [] : {};hash.set(target, cloneTarget);// 针对Symbol属性const symKeys = Object.getOwnPropertySymbols(target);if (symKeys.length) {symKeys.forEach(symKey => {if (typeof target[symKey] === 'object' && target[symKey] !== null) {cloneTarget[symKey] = cloneDeep1(target[symKey]);} else {cloneTarget[symKey] = target[symKey];}})}for (const i in target) {if (Object.prototype.hasOwnProperty.call(target, i)) {cloneTarget[i] =typeof target[i] === 'object' && target[i] !== null? cloneDeep1(target[i], hash): target[i];}}return cloneTarget;
}
简单思路
function deepCopy(obj){//判断是否是简单数据类型,if(typeof obj == "object"){//复杂数据类型var result = obj.constructor == Array ? [] : {};for(let i in obj){result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];}}else {//简单数据类型 直接 == 赋值var result = obj;}return result;
}
相关文章:

从数据类型到变量、作用域、执行上下文
从数据类型到变量、作用域、执行上下文 JS数据类型 分类 1》基本类型:字符串String、数字Number、布尔值Boolean、undefined、null、symbol、bigint 2》引用类型:Object (Object、Array、Function、Date、RegExp、Error、Arguments) Symbol是ES6新出…...

一文读懂:AI时代到底需要什么样的网络?
各位小伙伴们大家好哈,我是老猫。 今天跟大家来聊聊数据中心网络。 提到网络,通常把网络比作高速公路,网卡相当于上下高速公路的闸口,数据包就相当于运送数据的汽车,交通法规就是“传输协议”。 如高速公路也会堵车一…...

基于HarmonyOS的宠物收养系统的设计与实现(一)
基于HarmonyOS的宠物收养系统的设计与实现(一) 本系统是简易的宠物收养系统,为了更加熟练地掌握HarmonyOS相关技术的使用。 项目创建 创建一个空项目取名为PetApp 首页实现(组件导航使用) 官方文档:组…...
严格模式报错
部分参考: Android内存泄露分析之StrictMode - 星辰之力 - 博客园 (cnblogs.com)...
nginx: [emerg] the “ssl“ parameter requires ngx_http_ssl_module in nginx.conf
nginx: [emerg] the "ssl" parameter requires ngx_http_ssl_module in /usr/local/nginx/conf/nginx.conf:42 查看/usr/local/nginx/conf/nginx.conf文件第42行数据: listen 443 ssl; # server中的配置 原因是:nginx缺少 http_ssl_modul…...

Docker 部署loki日志 用于微服务
因为每次去查看日志都去登录服务器去查询相关日志文件,还有不同的微服务,不同日期的文件夹,超级麻烦,因为之前用过ELK,原本打算用ELK,在做技术调研的时候发现了一个轻量级的日志系统Loki,果断采…...
[Day 57] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
區塊鏈的零知識證明技術 一、引言 隨著區塊鏈技術的不斷發展,如何在保護用戶隱私的同時確保數據的完整性和可信度成為了研究的焦點。零知識證明(Zero-Knowledge Proof,ZKP)技術就是其中的一項關鍵技術,它允許一方在不…...

06结构型设计模式——代理模式
一、代理模式简介 代理模式(Proxy Pattern)是一种结构型设计模式(GoF书中解释结构型设计模式:一种用来处理类或对象、模块的组合关系的模式),代理模式是其中的一种,它可以为其他对象提供一种代…...

《深入浅出多模态》(九)多模态经典模型:MiniGPT-v2、MiniGPT5
🎉AI学习星球推荐: GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料,配有全面而有深度的专栏内容,包括不限于 前沿论文解读、资料共享、行业最新动态以、实践教程、求职…...
调试和优化大型深度学习模型 - 0 技术介绍
调试和优化大型深度学习模型 - 0 技术介绍 flyfish LLaMA Factory LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上…...
华为S3700交换机配置VLAN的方法
1.VLAN的详细介绍 VLAN(Virtual Local Area Network)即虚拟局域网,是一种将一个物理的局域网在逻辑上划分成多个广播域的技术。 1.1基本概念 1)作用: 隔离广播域:通过将网络划分为不同的 VLAN,广播帧只会在同一 VLAN 内传播,而不会扩散到其他 VLAN 中,从而有效…...
学懂C++(三十八):深入详解C++网络编程:套接字(Socket)开发技术
目录 一、概述与基础概念 1.1 套接字(Socket)概念 1.2 底层原理与网络协议 1.2.1 网络协议 1.2.2 套接字工作原理 二、C套接字编程核心技术 2.1 套接字编程的基本步骤 2.2 套接字编程详细实现 2.2.1 创建套接字 2.2.2 绑定地址 2.2.3 监听和接…...

SpringBoot-配置加载顺序
目录 前言 样例 内部配置加载顺序 样例 小结 前言 我之前写的配置文件,都是放在resources文件夹,根据当前目录下,优先级的高低,判断谁先被加载。但实际开发中,我们写的配置文件并不是,都放…...

第八周:机器学习笔记
第八周机器学习笔记 摘要Abstract机器学习1. 鱼和熊掌和可兼得的机器学习1.1 Deep network v.s. Fat network 2. 为什么用来验证集结果还是不好? Pytorch学习1. 卷积层代码实战2. 最大池化层代码实战3. 非线性激活层代码实战 总结 摘要 本周学习对李宏毅机器学习视…...

音乐怎么剪切掉一部分?5个方法,轻松学会音频分割!(2024全新)
音乐怎么剪切掉一部分?音频文件是娱乐和创作的重要基础。音频在我们日常生活中发挥着重要作用,从音乐播放列表到有趣的视频,它无处不在。无论是音乐爱好者还是内容创作者,我们常常需要对音频文件进行剪切和编辑。想象一下…...

洛谷 CF295D Greg and Caves
题目来源于:洛谷 题目本质:动态规划dp,枚举 解题思路:将整个洞分成两半,一半递增,一半递减。我们分别 DP 求值,最后合并。状态转移方程为:dpi,jk2∑j(j−k1)dpi−1,k1。枚举极…...
【图像处理】在图像处理算法开发中,有哪些常见的主观评价指标和客观评价指标?
主观评价指标 在图像处理算法开发中,主观评价指标依赖于观察者的个人感受和判断,通常用于评估图像的视觉质量。以下是一些常见的主观评价指标: 平均意见分数 (Mean Opinion Score, MOS):通过收集多个评价者的评分并计算平均值来评…...

从零开始学cv-6:图像的灰度变换
文章目录 一,简介:二、图像的线性变换三、分段线性变换四,非线性变换4.1 对数变换4.2 Gamma变换 五,效果: 一,简介: 图像灰度变换涉及对图像中每个像素的灰度值执行数学运算,进而调整图像的视觉…...

使用Apache POI和POI-OOXML实现word模板文档自动填充功能
最近接到一个新的需求,用户创建好模板文件保存到模板库,然后使用在线文档编辑器打开模板时,将系统数据填充到模板文件并生成新的word文件,然后在线编辑,研究使用Apache POI和POI-OOXML实现了这个功能。 Maven依赖 <…...

【HarmonyOS NEXT星河版开发学习】综合测试案例-各平台评论部分
目录 前言 功能展示 整体页面布局 最新和最热 写评论 点赞功能 界面构建 初始数据的准备 列表项部分的渲染 底部区域 index部分 知识点概述 List组件 List组件简介 ListItem组件详解 ListItemGroup组件介绍 ForEach循环渲染 列表分割线设置 列表排列方向设…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...