众所周知,js 的基本数据类型有 number
、 string
、 boolean
、null
、 undefined
等。那么问题来了 typeof null
和 typeof undefined
分别是什么呢?var 、 const 、 let
变量提升?暂时性死区又是什么东西?以前刚学 js 的时候有人跟我说 ===
相比于 ==
不仅比较值还要比较类型,难道不是这样的?
通常我们说的浏览器的内核一般是指支持浏览器运行的最核心的程序,分为两个部分,也就是渲染引擎和 JS 引擎。渲染引擎负责解析 HTML,然后进行布局,渲染等工作。而 js 引擎顾名思义就是解析并且执行 js 代码的。
一些常见浏览器 js 引擎,比方说老版本 IE 使用 Jscript 引擎,而 IE9 之后使用的 Chakra 引擎。safari 使用的是 SquirrelFish 系列引擎。firefox 使用 monkey 系列引擎。chrome 使用 V8 引擎,而且 nodeJs 其实上就是基于 V8 引擎做了进一步封装。我们今天讨论的内容也都是基于 V8 引擎的。
我们知道 js 引擎(V8)在拿到代码之后,会进行词法分析,将 js 代码拆分成对应的 Token,然后再根据 Token 继续生成对应的 AST,也就是语法分析的过程。而在这一过程中肯定也伴随着很多的优化策略。有兴趣的同学可以阅读下我们之前的一篇非常不错的文章《[V8 执行 JavaScript 的过程] 》。在这里呢,笔者将从 V8 执行代码过程中实际操作内存的角度来进行进一步的分享。
首先,我们先认识下这个模型:
V8内存大体上可以分为:栈
、堆
、常量池
这三大区域,当然其他的一些(甚至比方说 buffer 模块需要调配更加底层的 C++ 内存)模块不在本次讨论范围所以没有体现。图中清晰的体现了 js 基本数据类型在内存中的存储情况。
栈内存结构最大的特点就是小且存储连续,操作起来简单方便。在 js 中,变量名是用来保存内存中某块内存区的地址的,而栈区就是用来保存变量名和内存地址的键值对的,所以我们就可以通过变量名获取或者操作某一内存地址上的内容。而 undefined 正是栈空间中表示未定义含义的一块特殊的固定的内存区域。
console.log(b); // undefined
var a;
var b = '政采云前端团队';
然而,js 引擎在实际执行代码之前,会先从上往下依次处理变量提升和函数定义,然后再按序执行。拿以上代码块为例,这一过程在内存中的具体体现就是:
顾名思义,常量池就是用来存储常量的,包括 string
、number
、boolean
这三个基本类型的数据。常量池最大的特点就是:
所以这也就是为什么 a===b
是 true,因为 ===
比较的是变量 a 和 b 在内存中的指针指向的物理地址是否相等。
var a = '政采云前端团队';
var b = '政采云前端团队';
相对于栈和池来说,堆的存储形态会更加复杂。但是从另一个抽象的角度来说,堆区域却又是最单一的,因为存放在堆区域的都是 object
。
typeof {}; // object
typeof []; // object
typeof null; // object
typeof new Date(); // object
typeof new RegExp(); // object
那么就有人要问了,null
不是基本类型么,为什么 typeof null
又是 object
呢?
其实正如上文对 undefined
的定义那样,js 引擎对于 null
的基本定义其实是,在堆内存空间中的具有固定内存地址且唯一存在的一个内置对象。所以这就是 null
和 undefined
本质上的区别所在。
name = '政采云前端团队'
var a = {
name: '政采云前端团队'
}
console.log(name === a.name); // true
实际上,堆内存中的情况是非常复杂但又是非常精妙的。比方,上面这小段代码,执行过程中会在栈中创建 a
和 name
两个变量。针对于给 a
赋值的这个对象,v8 会在堆区中分配一块内存区域。并且区域内部依然会有内部的栈区和堆区,这就是精妙的分型思想。而 name === a.name
也侧面引证了常量的唯一。
可能就会有细心的小伙伴会发现,图中还有两个很大的黄色区域,貌似是和函数有关。并且上文刚刚还说堆区里面都是 object
?也可能会有一些大佬看到此处会微微一笑,这个人接下来肯定要开始扯什么 new Function()
。所以 function
从定义的抽象上来说也是 object
了。
是的,在介绍完基础且常用的三大区域后,接下来我们来聊一聊函数。但是,我们换个角度,还是回到这个模型上来尝试去理解一下函数的执行、函数的继承以及闭包。
上代码:
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function () {
console.log('Animal eat');
};
function Dog(name) {
Animal.apply(this, arguments);
}
var animal = new Animal();
Dog.prototype = animal;
Dog.prototype.constructor = Dog;
var dog = new Dog();
dog.eat();
console.log(Animal.prototype === animal.__proto__); // true
这是一段比较标准的组合继承的例子,相信这种代码片段对大家来说应该再熟悉不过了。那么这样的一段代码的运行过程在实际内存中是什么样的一个过程呢?
首先,如下左图,在代码执行之前会进行变量提升和函数定义,所以会在变量栈和函数定义区中准备好 obj
、 Animal
、 dog
以及一个不容发现的匿名函数。这里要注意一个点,就是 var a = function() {}
和 function a(){}
是两个完全不同的概念,给个眼神自己体会。
并且在函数定义时会,就会创建一个对象空间。函数的 prototype
属性指向到这个地址,这就是函数的原型对象。同时对象内存空间的内部又将会划分出栈结构空间和堆结构空间。娃,又套上了~
后续会在赋值语句时,将 Animal.proptotype.eat
指向到匿名函数。
至此,变量定义、函数定义以及赋值操作这些基础的过程已经梳理完成。
我们发现,new Animal()
、new Dog()
的这部分刚刚并没有提到。因为它们还要特殊,我们继续深入。
如上右图,其实,js 在执行 var animal = new Animal();
这种 new 操作符的时候,js 引擎会在栈空间的函数缓存区中创建一块空间用于保存该函数运行所需要存储的状态和变量。空间中有一个 __proto__
属性指向到构造函数的 prototype
,也就是图中的 Animal.prototype
。这也就从内存的角度解释了为什么 Animal.prototype === animal.__proto__
会是 true
。
实际上,在 new Animal()
执行完之后,本来 GC 就会清除掉函数的缓存区内存,释放空间。但是由于我们定义了一个 obj
变量,这个变量的内存地址是指向到这块缓存区,所以阻止了 GC 对这块内存的回收。这种问题在闭包问题中尤为典型。我们可以通过定义一个变量来阻止 GC 回收已经运行完的闭包函数的缓存区内存块,从而达到保护闭包内部的状态。然后在我们希望释放闭包空间的时候,将该变量置为 null
,从而在下一个 GC 周期时释放该内存区域。
function fn() {
var a = 1;
return function () {
console.log(a);
};
}
var onj = fn();
最后,我们通过 Dog.prototype = animal;
,将 Dog 的原型指向到了缓存区中的白色区域。我们可以通过打印 Dog.prototype === animal
和 Dog.prototype.__proto__ ===Animal.prototype
以及 dog.__proto__ === animal
的方式来验证图中的指向关系。这也就是原型继承在具体内存模型中的过程。
在代码的学习过程中,难免会觉得枯燥,而且有很多内容抽象难懂。强行死记硬背,不去知其所以然的话容易了解片面甚至理解错误,更何况也非常没有乐趣。借助于这种看得见摸得着的模型去理解和分析代码实际运行的情况会帮助理解,并且能够发现其中的设计精妙之处。
文中最后部分多次提及到 GC,其实 GC 的模型设计的也是非常巧妙,非常有意思的。可以移步至《[V8 引擎垃圾回收与内存分配] 》继续阅读。有兴趣的同学可以尝试将 GC 的模型和这个 V8 内存模型结合在一起去思考下代码运行和回收的全过程。而且 GC 还只是管理堆空间的垃圾回收,那么栈空间又是以什么方式进行自我回收的呢?还有很多很多有趣的东西值得我们思考~
本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/OqFlCvmAu1AD4okRDZc-9w
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。