首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >js中的作用域

js中的作用域

作者头像
RobinsonZhang
发布于 2018-08-28 03:45:54
发布于 2018-08-28 03:45:54
3.7K00
代码可运行
举报
运行总次数:0
代码可运行

前言

相信自从es6出来之后,你一定多少知道或者已经在项目中实践了部分的块级作用域,在函数或者类的内部命名变量已经在使用let了,但是你知道它真正的作用是什么吗?又是因为什么我们要用这个块级作用域,本文与你一起探讨块级作用域的场景以及所有细节问题。

es6之前的作用域

特点1 :js只有函数级作用域以及全局两种 特点2 :不通过var声明的变量直接赋值也可以用并且可以访问,原理是直接赋值到了window对象属性变量下,两者如果同时定义,那么会覆盖使用,因为访问方式一样,两者都可以通过window属性值访问到。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//通过var 申明的
var a=13
console.log(window.a)//13
a=14
//通过定义到window下
b=13
console.log(b)//13
var b=14
console.log(a)//14
console.log(b)//14

特点 3:var定义的变量与直接window对象属性变量的区别,window属性变量可以通过delete(window.attrname)删除 ,而通过var申明变量只能内存回收,使用时永久有效。(具体文件报错还有显示报错可以自行尝试,没有定义和没有初始化还是有区别的)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a
console.log(a)//打印undefined,没有初始化值,(申明过的不会直接文件报错)
a=12
delete(window.a) //删除失败 ,仍然可以访问
b = 12
console.log(b)//12
delete(window.b)//返回true,b不可以再被访问
console.log(b)//Uncaught ReferenceError 文件报错

特点4:语句块是没有局部作用域的,在下面的语句块中,与外部是完全一致的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a=12
if(true){
  var a=13
  var b=13
  console.log("a:"+a)//13
  console.log("b:"+b)//13
 c=14
 console.log("c:"+c)//14
}
console.log("a:"+a)//13
console.log("b:"+b)//13
console.log("c:"+c)//13

特点5 :函数内有局部作用域,函数内可以访问外部作用域,而全局是不可以访问函数作用域内的变量或者方法的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a=b=c=12
function demo(){
  var z=13
  console.log(b)//12
  console.log(z)//13

}
demo()
console.log(z)//undefined

特点6:函数内部的变量对于其内部的函数或者对象都是可访问的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function demo(){
  var z=13
  return (function(){
    console.log(z)
  })()

}
demo()//13

特点7:如果函数内部不加var申明,并且全局定义过的,使用的是全局变量并且改变影响的也是全局变量;如果函数内部不加var申明,并且全局没有定义过的,会定义到window对象属性下并造成全局变量的污染;如果加了var申明 那么使用的是自己的变量,不会影响全局变量也不会造成全局变量污染;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a=b=c=12
function demo(){
  var a=13
  b=13
  z=15
  console.log(a)//13
  console.log(b)//13
  console.log(c)//12
  console.log(z)//15
}
demo()
console.log(a)//12
console.log(b)//13
console.log(c)//12
console.log(z)//15

闭包

为了解决特点5中暴露的问题,也就是外部环境不能使用函数内部变量,而我们实际的场景中是需要的,我们利用特点6可以解决,具体的方案就是闭包的方式。 * 代码案例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function package(){
  var private_t=123
  return (function(){
       return private_t
  })
}
var t=package()
console.log(t())
  • 优点 保护了函数的私有变量和方法,利用了函数的链接作用域的特点,同时可以对外暴露部分,将我们需要的部分保留在内存中。
  • 缺点 占用内存,因为一般不用的变量会被gc回收,但是这部分闭包写的变量因为一直被申明占用而不被回收。解决方案是增加一个临时副本,函数结束时将不必要的局部变量删除。代码如下: function pack(){ var private_t=123 return function(){ var temp=private_t console.log(temp) temp=null } } pack()()//123 //如果你觉得上面写两个方法执行比较麻烦,也可以直接返回自运行函数 function pack(){ var private_t=123 return (function(){ var temp=private_t console.log(temp) temp=null })() }
  • 注意事项 因为返回方法中可以修改以及得到对应的私有变量,要避免将其作为公用方法而修改私有变量造成其他人使用时出错。这种建议针对私有变量分析清楚是否需要作为可修改属性,还是只读属性。

块级作用域

场景一 循环中的块级作用域

如果我们有一个遍历循环的绑定事件,并且需要把当前的指针绑定到对应方法中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var arr=[]
for(var i=0;i<10;i++){
  arr[i]=function(){
    console.log(i)
  }
}
arr[2]()//10 
//不是你预想的2,原因是函数执行时,对应的i已经变成了10而不是函数定义时的2
//解决方案 1 闭包 将当时的变量i当做参数传入函数中
var arr=[]
for(var i=0;i<10;i++){
  arr[i]=(function(temp){
   return function(){
     console.log(temp)
   } 
  })(i)
}
arr[2]()//2 
//解决方案 2 块级作用域 ,利用let块级作用域特性,区别就是定义变量时 i是块级变量,所以定义的函数中的变量也是当时的块级作用域,不随外面非块级元素值变化影响
var arr=[]
for(let i=0;i<10;i++){
  arr[i]=function(){
     console.log(i)
  }
}
arr[2]()//1
console.log(i)//undefined

场景二 结合this以及函数运行环境 执行环境的理解,通过这个案例也能更好的理解闭包。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var name='window'
var  demo={
  name:'project',
  sayname:function(){
    return function(){
      console.log(this.name)
  }}
}
var  demo2={
  name:'project',
  sayname:function(){
    var that=this
    return function(){
    console.log(that.name)
  }}
}
demo.sayname()()//window
demo2.sayname()()//project

场景三 多变量在不同包含以及不包含区块重名

我们经常会遇到经常性的用一个或者多个变量名重名导致的各种问题,包括在循环以及不同的语句块中。他们会互相影响,导致我们不得不重新命名或者细致的区分要不要使用原来的变量做某个事情。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if(true){
  let i=2
  console.log(i)//2
  if(true){
    let i=5
    console.log(i)//5,使用的是自己区块变量
  }
  console.log(i)//2,仍然是自己区块变量,内部其他let同名不影响自己
}
if(true){
  let i=3
  console.log(i)//3,其他同级区块不影响自己
  if(true){
    console.log(i)//3,子区块可以链式得到父区块变量
    i=4
    console.log(i)//4 ,子块级直接修改父块级变量
  }
  console.log(i)//4 ,看到修改了的结果,已经变成4
}

变量提升(js-hoisting)

场景一 变量可以先使用后申明,其申明会自动提前

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
a=12
console.log(a)
var a //这句申明会提前 ,在window环境下不会报错是因为赋值在了window下
//以上代码等效于
var a
a=12

场景二 :变量初始化不会提前(申明提前了,但是赋值没有)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
console.log(b)//undefined
var b=12
var j=1;
function demo(){
    console.log(j);//提示undefined ,因为布局有定义变量,先声明了变量,而赋值延迟
    var j=3;
    console.log(j);
}
demo()

综上

建议大家尽量避免变量提升的问题,在变量使用前定义并初始化为你需要的值。

参考文档

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-11-202,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
layoutSubviews解析
3、 设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。
全栈程序员站长
2022/09/17
2670
layoutSubviews 详解
但是是用initWithFrame 进行初始化时,当rect的值不为CGRectZero时,也会触发
全栈程序员站长
2022/09/17
5690
layoutSubviews总结
可是是用initWithFrame 进行初始化时,当rect的值不为CGRectZero时,也会触发
全栈程序员站长
2022/09/07
3230
谈谈 Autolayout
刚开始使用 Autolayout 遇到下面的警告人容易让人气馁,经常不知所措而放弃了使用 Autolayout。
s_在路上
2018/09/11
8040
谈谈 Autolayout
【iOS 开发】从 setNeedsLayout 说起
本文从 <code>setNeedsLayout</code> 这个方法说起,分享与其相关的 UIKit 视图交互、使用场景等内容。
KyXu
2019/04/11
8220
【iOS 开发】从 setNeedsLayout 说起
layoutSubviews的使用
1.直接调用layoutSubviews . 如:[self layoutSubviews];
全栈程序员站长
2022/09/13
5880
layoutSubviews和drawRect
子类可以重写此方法,因为需要更精确执行他们子视图的布局。只有当 autoresizing 和基于约束的行为的子视图不提供你想要的行为,应重写此方法。
全栈程序员站长
2022/09/13
2760
UIView - 生命周期
一般情况都是说UIViewController的生命周期,UIView的生命周期经常被忽视。
大壮
2019/02/22
2.9K0
layoutSubviews 调用
学习了一下UIView的setNeedsDisplay和setNeedsLayout方法。首先两个方法都是异步执行的。而setNeedsDisplay会调用自动调用drawRect方法,这样可以拿到UIGraphicsGetCurrentContext,就可以画画了。而setNeedsLayout会默认调用layoutSubViews,就可以处理子视图中的一些数据。 宗上所诉,setNeedsDisplay方便绘图,而layoutSubViews方便出来数据。 \
全栈程序员站长
2022/09/17
6370
layoutSubviews 调用
setNeedsLayout和layoutIfNeeded看我就懂!
前言: 开发得跟view打交道,我们也经常看到苹果官方代码有layout方法的相关调用,但是大家可知道什么时候调用,什么时候需要吗?针对网上大部分资料讲得不够清晰,我决定用Demo来讲解 一、layoutSubviews 不能直接调用这个方法。强制刷新布局,调用 setNeedsLayout,如果想马上刷新界面,调用layoutIfNeeded 二、setNeedsLayout跟layoutIfNeded setNeedsLayout调整视图的子视图的布局时,在应用程序的主线程调用此方法。此方法记录
Dwyane
2018/05/22
2.9K0
iOS layout相关方法
这个方法,默认没有做任何事情,需要子类进行重写 。 系统在很多时候会去调用这个方法:
码客说
2019/10/22
1.2K0
关于Autolayout和Masonry自动布局的几个坑
自动布局 02 Mar 2016 Comments 前言 最近遇到一个复杂视图:根控制器里面有上下两个子控制器,子控制器中各自实现类似PageView的视图,然后PageView的每一页是一个WebView,同时中间有个可拖拽的控件,实现上下两个控制器视图的大小调整。采用子控制器的原因是因为防止所有的逻辑代码都混在根控制器中,所以没有使用nicklockwood 的iCarousel 或SwipeView ,而是采用了之前一直在用的SCPageViewController 。 记录下自动布局中遇到的几个坑。 关于translatesAutoresizingMaskIntoConstraints
freesan44
2018/09/05
1.9K0
关于Autolayout和Masonry自动布局的几个坑
setNeedsDisplay看我就懂!
前言: setNeedsDisplay异步执行的。它会自动调用drawRect方法,这样可以拿到 UIGraphicsGetCurrentContext,就可以绘制了。而setNeedsLayout会默认调用layoutSubViews,处理子视图中的一些数据。 一、着手 我定义了一个UIView的子类,用于演示使用setNeedsDisplay,这个CircleView子类会在draw(_ rect: CGRect)方法内简单绘制一个圆,它有一个颜色属性,这是我们将要设置用来改变圆的颜色。 imp
Dwyane
2018/05/22
1.5K0
iOS 面试策略之系统框架-UIKit
本章节主要从视图、网络、设计模式几个方面考察开发者的开发水准,这是任何一个合格的 iOS 开发者都应该具备的基本素养。
会写bug的程序员
2021/05/13
1.7K0
iOS 面试策略之系统框架-UIKit
UIView layoutSubviews调用时机
1.init初始化的时候是不会触发的。但是调用initWiftFrame 并且参数fram不为0的时候会调用.换句话就是大小或者位置更改的时候调用。
编程那点事
2023/02/25
4870
关于Autolayout和Masonry自动布局的几个坑
最近遇到一个复杂视图:根控制器里面有上下两个子控制器,子控制器中各自实现类似PageView的视图,然后PageView的每一页是一个WebView,同时中间有个可拖拽的控件,实现上下两个控制器视图的大小调整。采用子控制器的原因是因为防止所有的逻辑代码都混在根控制器中,所以没有使用nicklockwood的iCarousel或SwipeView,而是采用了之前一直在用的SCPageViewController。
freesan44
2018/09/05
1.7K0
关于Autolayout和Masonry自动布局的几个坑
android
目前iOS开发中大多数页面都已经开始使用Interface Builder的方式进行UI开发了,但是在一些变化比较复杂的页面,还是需要通过代码来进行UI开发的。而且有很多比较老的项目,本身就还在采用纯代码的方式进行开发。
xiangzhihong
2022/11/30
9590
ios约束
一、苹果的VFL语法约束 在使用约束之前,要先将没有设置frame的view添加到父视图上。如何将view设置为使用AutoLayout约束,取消默认约束。 UIView *red = [[UIView alloc]init]; red.backgroundColor = [UIColor magentaColor]; [self.view addSubview:red]; red.translatesAutoresizingMaskIntoConstraints = NO; //
谦谦君子修罗刀
2018/04/28
1.2K0
ios约束
iOS界面布局的核心以及TangramKit介绍
TangramKit是iOS系统下用Swift编写的第三方界面布局框架。他集成了iOS的AutoLayout和SizeClass以及Android的五大容器布局体系以及HTML/CSS中的float和flex-box的布局功能和思想,目的是为iOS开发人员提供一套功能强大、多屏幕灵活适配、简单易用的UI布局解决方案。Tangram的中文即七巧板的意思,取名的寓意表明这个布局库可以非常灵巧和简单的解决各种复杂界面布局问题。他的同胞框架:MyLayout是一套用objective-C实现的界面布局框架。二者的主体思想相同,实现原理则是通过扩展UIView的属性,以及重载layoutSubviews方法来完成界面布局,只不过在一些语法和属性设置上略有一些差异。可以这么说TangramKit是MyLayout布局库的一个升级版本。大家可以通过访问下面的github站点去下载最新的版本:
欧阳大哥2013
2018/08/22
2.4K0
iOS界面布局的核心以及TangramKit介绍
iOS-自定义View的封装
xy_ss
2023/11/22
4100
iOS-自定义View的封装
相关推荐
layoutSubviews解析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验