深入了解JS之作用域和闭包


Posted in Javascript onJune 16, 2020

作用域和闭包

ECMAScript5: JS 的代码没有代码块;使用函数运行的机制进行创建闭包;闭包就是作用域的意思;

ES5中,JS中只有函数才可以创建能操作的作用域;

JavaScript中的内存也分为栈内存和堆内存。一般来说,栈内存中存放的是存储对象的地址,而堆内存中存放的是存储对象的具体内容。对于原始类型的值而言,其地址和具体内容都存在与栈内存中;而基于引用类型的值,其地址存在栈内存,其具体内容存在堆内存中。堆内存与栈内存是有区别的,栈内存运行效率比堆内存高,空间相对推内存来说较小,反之则是堆内存的特点。所以将构造简单的原始类型值放在栈内存中,将构造复杂的引用类型值放在堆中而不影响栈的效率。

函数执行时形成私有作用域

函数执行的时候(直接目的:让函数体中的代码执行)会形成一个新的私有的作用域(栈内存),供函数体中的代码执行;

  1. 给形参赋值
  2. 私有作用域下的预解释
  3. 私有作用域下的代码执行

形成的新的私有的作用域还保护了里面的私有变量不受外界的影响,我们把函数的这种保护机制叫”闭包”:为什么要有作用域;因为变量要规定活动范围的,为便于管理不同范围的变量;所以要给变量设置活动范围;

预解释也是在各自的作用域里进行预解释的;

function fn(){
 var a=1;
}
fn();
fn();
console.log(a);//Uncaught ReferenceError: a is not defined;

因为a没有声明和定义过,所以报错了;

上面的fn运行了两次,所以产生了两个堆内存;两个作用域(作用域也就是闭包)各自分别有一个a的变量;a的值都是数字1,但是两个变量是不相等的;两个a之间是没有任何关系的;

就好比我们都属于人类;我们都继承了人类这个对象所具有的特征;我有一双手,你也有一双手,但是我们两个人的手是没有关系的;我的手不等于你的手;

全局变量和私有变量

  • 在全局作用域下声明的变量是全局变量;
  • 在私有作用域中声明的变量是私有变量;函数的形参也是私有的变量;

如何区分函数中出现的变量是私有的还是非私有的?

首先看是否为形参,然后看是否在私有作用域中声明过(有没有var过),两者有其一就是私有的变量,那么在当前函数中不管什么位置出现都是私有的,和全局的没有半毛钱的关系;如果两者都没有,说明不是私有的;如果一个函数中出现的变量不是私有的,那么会往其上级作用域查找,上级没有则继续查找,一直找到window为止,如果window也没有呢?

如果是获取:会报错

function fn() {
 console.log(num);//Uncaught ReferenceError: num is not defined
}
fn();

如果是设置:不是私有的,找全局,全局没有的话相当于给全局加一个

function fn() {
 num = 13;//相当于给window增加了一个叫做num的属性名,属性值是13 window.num=13;
}
fn();
console.log(num);//13

如何查找当前作用域的上级作用域?

当前作用域的上级作用域是谁和函数在哪执行的没有任何的关系,我们只需要看当前函数是在哪个作用域下定义的,那么它的上级作用域就是谁;

下面是查找上一级作用域,一直找到window的案例;

var total = 100;
function fn() {
 var total = 10;
 return function () {
  console.log(total);
 }//如果返回的是一个引用数据类型的值(对象、数组、函数...),首先是把这个值开辟一个内存空间,有一个地址,然后把内存地址返回 ->例如在这里其实返回的就是 return xxxfff111;
}
var f = fn();//f=xxxfff111;
f();
// 10

//输出的结果是 10 还是 100 ? 为什么 ?

如果是在自执行函数里呢?

var total = 100;
function fn() {
 var total = 10;
 return function () {
  console.log(total);
 }
}
var f = fn();//f=xxxfff111;
~function () {
var total = 1200;

f();//->;里面变量的上级作用域是谁
}();
// 10

下面代码输出的是什么?

var a=0;
var b="0";
function fn(){
 console.log(a);
console.log(b);

console.log(typeof a);
 console.log(typeof b);

a="1";

var b=1;

console.log(a);

console.log(b);

console.log(typeof a);

console.log(typeof b);
}
fn();//运行后会输出什么?如果没有这行代码;函数fn的定义是没有意义的;函数只声明定义,而不运行是没有任何意义的;
//0,undefined,"number","undefined","1",1,"string","number"

预解释是作用域中的预解释;js里是可以函数里面套函数的;都运行的时候,是在函数创建的作用域里再创建一个作用域;

下面是作用域的范围;

var a="window";
console.log(a);//window
function father(){
console.log(a);//undefined

var a="father";

console.log(a);//father

function children(){
 
console.log(b);//undefined
 
console.log(a);//father
 
var b="flag";
 
a="children";
 
console.log(a);//children
  }
  children();
  console.log(a);//children
}
father();
console.log(a);//window

JavaScript中的代码执行顺序是从上到下逐条运行的;遇到function定义函数的代码块;直接跳过;遇到函数执行的代码;就找到引用的函数地址;开始跳到执行函数产生的作用域中执行代码;等函数执行完以后,再回到当前作用域执行下面的代码;

上面的代码运行后,输出的是

“window”,undefined,“father”,undefined,“father”,“children”,“children”,“window”

作用域链查找:当作用域套作用域的时候,children内找不到某个变量;会到children的父作用域father中找;当father中找不到的时候;会到father的父作用域找;一直找到window这个根作用域;属于作用域链式查找;

函数运行产生的作用域

函数的运行是一个有生命周期的内存地址;

函数运行时,会创建一个内存地址(产生堆内存,函数保存的就是这个堆内存的地址),当此函数运行结束时,此内存地址又会销毁;这个地址,我们无法保存;它的灵活的,活动的;有生命周期的;我们也没有办法给这个作用域起一个变量名字,也没办法保存这个作用域,JS不提供这种机制;

也就是说:在作用域外面是没办法控制作用域内部的数据的;只能在作用域内部控制;而且作用域内部的代码可以控制外部的数据;这种机制就叫做闭包,闭包与作用域链和函数的运行有关系的;

函数里的变量,就在这个内存里创建;我们可以把这个内存当成一个对象;那函数里的变量就是这个内存对象的属性;

函数的定义和函数的执行是两码事(fn和fn()的区别);函数的执行与函数的定义地方无关;这个一定要理解!

闭包

作用域就是闭包;我理解的是相同的意思;只是不同人对这个机制的叫法不同;闭包是一种机制;并不是某种形式或者概念;最大的闭包就是window,我们可以把window当做一个闭包;

权威指南182页中对闭包的解释;

权威指南解释:函数的执行依赖于变量作用域,这个作用域是函数定义时决定的,而不是函数调用时决定的

注意:函数对象可以通过作用域链相互关联起来;函数体内部的变量都可以保存在函数作用域内,这种特性叫闭包; 批注:这里和作用域链有关系的,和闭包没有关系的;函数的作用域是谁,和在哪运行没有关系;只和在哪儿定义有关系;

如下代码;

var a=0;
function fn(){
 var a=1;
 function fm(){
  console.log(a);
 }
 return fm;
}
var testFn1=fn();//hanshu这个变量就相当于fm函数;
testFn1();//相当于fm函数运行;此时输出的是1;而不是0;虽然是在window中运行的;但是在fn中定义的;所以a找到的是fm上一级作用域fm的a;而不是window中的a;

作用域不销毁的情况|内存释放

作用域不销毁的总结:当函数内return一个引用数据类型;并且函数外面有一个变量接收这个引用数据类型;此时的作用域是不销毁的;

堆内存

对象数据类型或者函数数据类型在定义的时候首先都会开辟一个堆内存,堆内存有一个引用的地址,如果外面有变量等指到了这个地址,我们就说这个内存被占用了,就不能销毁了;

堆内存释放的问题->堆内存用来存储引用数据类型值的

[谷歌]:浏览器会每隔一段时间,看我们的堆内存是否还有其他的东西引用着,如果还在被占用着,浏览器不会进行处理;但是如果我们的堆内存已经没有任何的东西占用了,那么浏览器会把这个堆内存进行回收释放

[IE和火狐]:开辟了一个堆内存,我们有一个占用的时候浏览器记一个数(记录有多少个占用这个内存),当我们减少引用的时候,浏览器会把记数减1,当记的数字减为0的时候,浏览器会把我们的堆内存回收释放

var obj1 = {name: "张三"};
var obj2 = obj1;
//我们想要让堆内存释放/销毁,只需要把所有引用它的变量值赋值为null即可,如果当前的堆内存没有任何东西被占用了,那么浏览器会在空闲的时候把它销毁...
obj1 = null;
obj2 = null;

以上就是深入了解JS之作用域和闭包的详细内容,更多关于JavaScript 作用域和闭包的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
用户注册常用javascript代码
Aug 29 Javascript
js对数字的格式化使用说明
Jan 12 Javascript
Javascript引用指针使用介绍
Nov 07 Javascript
JQuery切换显示的效果实例代码
Feb 27 Javascript
js数组的基本用法及数组根据下标(数值或字符)移除元素
Oct 20 Javascript
Javascript Memoizer浅析
Oct 16 Javascript
根据user-agent判断蜘蛛代码黑帽跳转代码(js版与php版本)
Sep 14 Javascript
js轮播图代码分享
Jul 14 Javascript
JavaScript设计模式之策略模式详解
Jun 09 Javascript
页面点击小红心js实现代码
May 26 Javascript
JS原生带缩略图的图片切换效果
Oct 10 Javascript
webpack中如何使用雪碧图的示例代码
Nov 11 Javascript
JS数组及对象遍历方法代码汇总
Jun 16 #Javascript
浅谈Vue 函数式组件的使用技巧
Jun 16 #Javascript
原生JS实现天气预报
Jun 16 #Javascript
vue实现公告栏文字上下滚动效果的示例代码
Jun 16 #Javascript
原生JS实现无缝轮播图片
Jun 24 #Javascript
如何利用Node.js与JSON搭建简单的动态服务器
Jun 16 #Javascript
js+css实现全屏侧边栏
Jun 16 #Javascript
You might like
PHP+MYSQL的文章管理系统(一)
2006/10/09 PHP
mysql下创建字段并设置主键的php代码
2010/05/16 PHP
PHP CURL获取cookies模拟登录的方法
2013/11/04 PHP
Laravel中encrypt和decrypt的实现方法
2017/09/24 PHP
JS面向对象、prototype、call()、apply()
2009/05/14 Javascript
基于jQuery的可以控制左右滚动及自动滚动效果的代码
2010/07/25 Javascript
js 手机号码合法性验证代码集合
2012/09/29 Javascript
node.js中的fs.utimesSync方法使用说明
2014/12/15 Javascript
关于JavaScript的变量的数据类型的判断方法
2015/08/14 Javascript
JS刷新父窗口的几种方式小结(推荐)
2016/11/09 Javascript
Vue.js开发环境快速搭建教程
2017/03/17 Javascript
JavaScript实现的文本框placeholder提示文字功能示例
2018/07/25 Javascript
在 Angular-cli 中使用 simple-mock 实现前端开发 API Mock 接口数据模拟功能的方法
2018/11/28 Javascript
npm的lock机制解析
2019/06/20 Javascript
vue实现购物车小案例
2019/09/27 Javascript
Python环境下安装使用异步任务队列包Celery的基础教程
2016/05/07 Python
详解使用python的logging模块在stdout输出的两种方法
2017/05/17 Python
python 拼接文件路径的方法
2018/10/23 Python
Python tornado上传文件的功能
2020/03/26 Python
Xadmin+rules实现多选行权限方式(级联效果)
2020/04/07 Python
Keras 在fit_generator训练方式中加入图像random_crop操作
2020/07/03 Python
如何卸载python插件
2020/07/08 Python
基于Python爬取fofa网页端数据过程解析
2020/07/13 Python
简单几步用纯CSS3实现3D翻转效果
2019/01/17 HTML / CSS
AmazeUI 导航条的实现示例
2020/08/14 HTML / CSS
美国宠物商店:Wag.com
2016/10/25 全球购物
皇家阿尔伯特英国官方商店:Royal Albert骨瓷
2019/03/25 全球购物
切尔西足球俱乐部官方网上商店:Chelsea FC
2019/06/17 全球购物
商务考察邀请函范文
2014/01/21 职场文书
银行服务明星推荐材料
2014/05/29 职场文书
党员群众路线对照检查材料思想汇报
2014/09/17 职场文书
2014年行政后勤工作总结
2014/12/06 职场文书
慰问信格式
2015/02/14 职场文书
重温入党誓词主持词
2015/06/29 职场文书
如何使用Maxwell实时同步mysql数据
2021/04/08 MySQL
Python Socket编程详解
2021/04/25 Python