浅谈js内存

本文是lhyt本人原创,希望用通俗易懂的方法来理解一些细节和难点。转载时请注明出处。文章最早出现于本人github

0.前言

主要结合了内存的概念讲了js的一些的很简单、但是又不小心就犯错的地方。

结论:js执行顺序,先定义,后执行,从上到下,就近原则

1.先说类型

在ECMAscript数据类型有基本类型和引用类型,基本类型有Undefined、Null、Boolean、Number、String,引用类型有Object,所有的的值将会是6种的其中之一(数据类型具有动态性,没有定义其他数据类型的必要了)

引用类型的值,也就是对象,一个对象是某个引用类型的一个实例,用new操作符创建也可以用字面量的方式(对象字面量创建var obj ={ })。ECMA里面有很多原生的引用类型,就是查文档的时候看见的那些:Function、Number (是对于原始类型Number的引用类型)、String(是对于原始类型String的引用类型)、Date、Array、Boolean(...)、Math、RegExp等等。

在程序运行的时候,整块内存可以划分为常量池(存放基本类型的值)、栈(存放变量)、很大的堆(存放对象)、运行时环境(函数运行时)

内存划分


对于如下代码:

var a = 1;

var b = 'hello';

var c = a;

var obj1 = new Object();

obj1.name = 'obj1'

var obj2 = obj1

基本数据类型的值是直接在常量池里面可以拿到,而引用类型是拿到的是对象的引用

c = a,这种基本数据类型的复制,只是重新复制一份独立的副本,在变量的对象上创建一个新的值,再把值复制到新变量分配的位置上,a、c他们自己的操作不会影响到对方。

a++;console.log(a);console.log(c)显然是输出2、1

obj1和obj2,拿到的是新创建的对象的引用(也就是家里的钥匙,每个人带一把),当操作对象的时候,对象发生改变,另一个obj访问的时候,发现对象也会改。就像,家里有一个人回去搞卫生了,另一个回家发现家里很干净了。

console.log(obj2) //'obj1'


基本类型和引用类型

函数也是同理

var a = function(){console.log(1)}

var b = a;

a = null;

b();a()

//b输出1,a报错:Uncaught TypeError: a is not a function

把a变成null,只是切断了a和函数之间的引用关系,对b没有影响

2.再说顺序

大家常听说的先定义后执行,其实就是在栈中先开辟一块内存空间,然后在拿到他所对应的值,基本类型去常量池,引用类型去堆拿到他的引用。大家常说的原始类型值在栈,其实就是这种效果。

基本类型和引用类型取值顺序

2.1 为什么引用类型值要放在堆中,而原始类型值要放在栈

栈比堆的运算速度快,Object是一个复杂的结构且可以扩展:数组可扩充,对象可添加属性,都可以增删改查。将他们放在堆中是为了不影响栈的效率。而是通过引用的方式查找到堆中的实际对象再进行操作。

因此又引出另一个话题,查找值的时候先去栈查找再去堆查找。为什么先去栈查找再去堆查找

简单来说,你宁愿大海捞针呢还是碗里捞针呢?

具体如何,还得问一下语言的底层去了

3.函数

先抛出一个问题

function a(){console.log(2)}; var a = function(){console.log(1)}; a()

覆盖?那么交换的结果又是什么呢?

var a = function(){console.log(1)}; function a(){console.log(2)}; a()

都是1,然后有的人就说了,var优先。好的,那为什么var优先?


先定义后执行,先去栈查找

变量提升,其实也是如此。先定义(开辟一块内存空间,此时值可以说是undefined)后执行(从上到下,该赋值的就赋值,该执行操作的就去操作),就近原则

函数声明和函数表达式,有时候不注意,就不小心出错了

a(); function a(){console.log(666)}//666

另一种情况:

a(); var a = function (){console.log(666)}//a is not a function

虽然第一种方法有变量提升,不会出错,正常来说,还是按顺序写,定义语句放前面。如果想严格要求自己,就手动来个严格模式‘use strict’吧。对于框架的开发,需要严谨遵守规则,所以一般会用严格模式。

4.接着是临时空间

函数执行的时候,会临时开辟一块内存空间,这块内存空间长得和外面这个一样,也有自己的栈堆,当函数运行完就销毁。

4.1 eg1:

var a = 10;

function() {

console.log(a);//undefined

var a = 1;

console.log(a)//1

}

宏观来说,只有2步一和二,当执行第二步,就跳到函数内部执行②-⑧

函数外部的a=10完全就没有关系,这里面造成undefined主要因为变量提升,其实准确的顺序是:

var a

console.log(a);//undefined

a = 1;

console.log(a)//1

为什么不出去找全局的a?

就近原则。为什么就近原则?都确定函数内部有定义了,就不会再去外面白费力气,说到底这个还是得问一下底层怎么实现。类似的一个例子,我们用函数声明定义一个函数f,再用一个变量g拿到这个函数的引用,然后在外面用f是访问不了这个函数的,但是在函数内部是能找到f这个名字的:

var g = function f(){}

f()//报错

我猜想是,这是内部的一种性能优化方法,他不会浪费更多的资源去干一件事,具体是什么原因,为什么就近原则,得问底层原理去了。

4.2 eg2

function f(){

return function f1(){

console.log(1)

}

};

var res = f();

res();

f1()

res(),返回的是里面的函数,如果直接f1()就报错,因为这是window.f1()

函数声明后,可以通过引用名称查找或者内存地址查找

局部作用域用function声明,声明不等于创建,只有调用函数的时候才创建

函数f有内存地址的话,通过栈找f的内存空间,如果找不到栈中f这个变量,就去堆中找

5.IIFE和闭包

5.1 IIFE

立即执行函数,内部就是一个闭包,形成一个沙盒环境,防止变量污染内部,是做各种框架的好方法

先手写一段假的jQuery

(function(root){

var $ = function(){

//代码

}

root.$ = $

})(this)

这样子在内部函数里面写相关的表达式,我们就可以用美元符号使用jQuery(实际上jQuery第一个括号是全局环境判断,真正的函数体放在第二个括号里面,号称世界上最强的选择器sizzle也里面)

5.2闭包

闭包的概念各有各的说法,平时人家问闭包是什么,大概多数人都是说函数中的函数、函数里面能访问到外面的变量。

《权威指南》:函数对象通过作用域链相互关联起来,函数内部变量都可以保持在函数的作用域中,有权访问另一个函数作用域中的变量

《忍者秘籍》:一个函数创建时允许自身访问并操作该自身函数以外的变量所创建的作用域

《你不知道的js》:是基于词法的作用域书写代码时所产生的结果,当函数记住并访问所在的词法作用域,闭包就产生了

其实这是闭包的现象。闭包有现象与产生。闭包的产生,会导致内存泄漏。

js具有垃圾回收机制,如果发现变量被不使用将会被回收,而闭包相互引用,让他不会被回收,一直占据着一块内存,长期持有一块内存的引用,这就是内存泄漏


原文来源于:lhyt的github

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,149评论 0 13
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,763评论 2 17
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,798评论 0 38
  • 单例模式 适用场景:可能会在场景中使用到对象,但只有一个实例,加载时并不主动创建,需要时才创建 最常见的单例模式,...
    Obeing阅读 2,073评论 1 10
  • 当满世界新绿,发散昂扬生机的季节,看那微风中摇曳枝条的桑树时,常常不由自主地想起三年前那场养蚕的经历。那是一段合...
    书香精灵阅读 558评论 4 3