如何理解js中的作用域,闭包,私有变量,this对象概念呢?
就从一道经典的面试题开始吧!
题目:创建10个<a>标签,点击时候弹出相应的序号
????//先思考一下你会怎么写,是不是这样? ???????可是结果呢,弹出来的都是10,为啥? ?????????var i,a ????for(i=0;i<10;i++){ ?????????a=document.createElement(‘a‘) ?????????a.innerHTML=i+‘<br>‘ ?????????a.addEventListener(‘click‘,function(event){ ??????????????var event=event||window.event ??????????????event.preventDefault() ??????????????alert(i) ???????????????????????????}) ?????????document.body.appendChild(a) ????}
这个题目答案最后再说,当然,你就可以直接翻到最后了解,不过我不建议这样!!
涉及到今天的变量、作用域、闭包等等,在变量、作用域知识中已经说了一些,再重新简单的说一下。
执行环境:也称之为执行上下文,两个,一个是全局环境(window),一个是函数环境(每个函数都是一个函数环境)
作用域链:当代码在某一个环境中执行,会创建变量对象的一个作用域链,以保证对执行环境有权访问的所有变量和函数的有序访问。外部环境访问不到内部环境;内部环境通过作用域链可以访问外部环境。
块级作用域:js没有块级作用域,类似循环语句、条件判断语句内作用域都是全局的;只有两个执行环境
变量:var操作符判断是局部变量还是全局变量。
浏览器解析:浏览器解析时,先将所有的变量提升,变量都存为undefined,当执行到哪一行才给予赋值;函数声明一开始就是完整的,而函数表达式跟解析变量是一样的
????console.log(a) ????????//undefined ????var a=100 ????console.log(a) ????????//100 ?????????person("double") ??????//double,20 ????function person(name){ //声明函数 ?????????age=20 ?????????console.log(name,age) ?????????var age ????}
this:引用执行环境的对象;只有在执行时才能确认,定义时无法确认
???????//普通的构造函数;this就是构造函数的实例 ???????function Foo(name){ ???????????//var this ???????????this.name=name ???????????//return this ???????} ???????var f=new Foo("double") ???????//普通对象;this就是这个对象 ???????var obj={ ???????????name:"double", ???????????printName:function(){ ???????????????console.log(this.name) ???????????} ???????} ???????obj.printName() ???????//普通函数 this就是window ???????function fn(){ ???????????console.log(this) ???????} ???????fn() ???????//call apply bind来设置this的值 ???????function fn1(name,age){ ???????????alert(name) ???????????console.log(this) ???????} ???????fn1.call({x:100},‘double‘,20) ?????//this 就是{x:100}这个对象 ???????fn1.apply({x:100},["double",20]) ??//this 就是{x:100}这个对象 ???????var fn2=function (name,age){ ???????????alert(name) ???????????console.log(this) ???????}.bind(‘double‘,20) ???????????????//this 就是double了 ????????????fn2()
闭包
这个概念有些抽象,这样理解:有权访问另一个函数作用域中的变量的函数;也就是说一个函数可以访问另一个函数作用域里的变量
一般两个地方,一个作为返回值,一个作为传递的参数
//闭包//函数作为返回值function F1(){ ???var ?a=100; ???//返回一个函数(函数作为返回值) ???return function(){ ???????console.log(a) ???}}//f1 得到一个函数var f1=F1()var a=200f1() ?????????//a=100 ????执行返回函数,函数在定义函数的作用域查找变量(而非执行函数的作用域),没有才会往父级作用域查找//函数作为参数传递function F1(){ ???var a=100 ???return function(){ ???????console.log(a) ???}}var f1=F1() ??//这是个函数function F2(fn){ ???var a=200 ???fn()}F2(f1) ???????//a=100 ??????给函数传参给F2
以闭包为返回值为例,详细说一下
????//这是一个闭包以返回值的例子
????function create(){ ?????????var a=100 ???????????????????????return function(){ ???????????????console.log(a) ?????????} ????} ????var a=200 ???????????????????var result=create() ???????//得到返回函数 ????result() ??????????????????//执行返回函数 ?a=100 ??函数会在定义函数的作用域中查找,然后再在父级中查找 ????当某个函数被调用的时候,会创建一个执行环境及相应的作用域链,使用arguments和其他参数 ????来初始化函数的活动对象,但在作用域链中,外部函数的活动对象始终处于第二位, ????对于create函数内部的匿名函数,其作用域链被初始化包含三个的活动对象,一个自身的,一个为create函数的,一个是全局环境的 ????一般来说,函数执行完毕,其活动对象就已经被销毁; ????但是对于闭包来说,当create函数执行完毕,函数的活动对象依然不会销毁,因为内部的匿名函数一直引用其中的活动对象
再来看一下这个
????//普通对象 的 一个方法 ????var person={ ????????name:"double", ????????getName:function(){ ?????????????console.log(this.name) ????????} ????} ????person.getName() ????//double ?????????//普通对象方法返回的是匿名函数 ????var person={ ??????????name:"double", ??????????getName:function(){ ?????????????????return function(){ ????????????????????????console.log(this.name) ?????????????????} ??????????} ????} ????var name="single" ????person.getName()() ????//single ???????//普通对象方法返回的是匿名函数 ????var person={ ????????name:"double", ????????getName:function(){ ?????????????var that=this ??????????//在定义匿名函数前将this对象赋予给that的变量 ?this和arguments差不多,也要将其赋值给某个变量 ?????????????return function(){ ????????????????????console.log(that.name) ?????????????} ????????} ????} ????var name="single" ????person.getName()() ???//double
ok,了解闭包、作用域链的知识后,回到那道面试题,应该知道其中的原因
???正确的写法
????var i ????for(i=0;i<10;i++){ ?????(function(i){ ????????var a=document.createElement(‘a‘) ?????????a.innerHTML=i+‘<br>‘ ?????????a.addEventListener(‘click‘,function(event){ ??????????????var event=event||window.event ??????????????event.preventDefault() ??????????????alert(i) ??????????????????//去父作用域查找 ??????????????????}) ?????????document.body.appendChild(a) ???????})(i) ????} ??????????//js没有块级作用域,所以alert(i)的i是在全局作用域下的,返回的当然是10, ????//如何让i是每个弹出来的呢? ????//我们知道环境分函数环境和全局环境,只需要将每个i都有自己的执行环境就可以了 ????//怎么弄? ????//直接加个自执行函数,让每个i都有自己的函数环境,也就是构建出私有作用域
内存泄露
由于闭包的存在,外部函数的变量始终被引用着,闭包不结束,则引用始终存在,内存也就不会被回收
??????//内存泄露 ??????function person(){ ?????????????var element=document.getElementById("someElement") ?????????????element.onclick=function(){ ??????????????????????alert(element.id) ?????????????} ??????} ?????????????//解决内存泄露 ??????function person(){ ?????????????var element=document.getElementById("someElement") ?????????????var id=element.id ???//将所需内容赋值给一个变量 ?????????????element.onclick=function(){ ??????????????????????alert(id) ?????????????} ??????????element=null ????????//闭包会引用包含函数的整个活动对象,其中包含着element ??????}
模仿块级作用域
都是知道的,js没有块级作用域,所以要模仿,怎么模仿呢?
上面的面试题已经告诉了:通过匿名函数来构造函数环境,形成私有作用域
下面解释有关涉及到匿名函数,函数声明,函数表达式的东西,说明一下块级作用域
????//变量名只是个值的另一种形式 ????var count=10 ????alert(count) ??????alert(10) ??????????//既然变量可以,那么函数呢 ?????var person=function(){ ??????????alert("hello") ????} ????person() ????var person=function(){ ?????????alert("hello") ????}() ?????????function(){ ?????????alert("hello") ????}() ???//报错 ????//为啥?因为js将function关键字作为一个函数声明的开始,而函数声明是不能加圆括号 ?????????(function(){ ????????alert("hello") ????})() ????//将函数声明转为函数表达式即可,外部加个圆括号即可,也就是形成自己的私有作用域
看了汤姆大叔的博客,关于这个,他是这么说的
????//对于函数声明和函数表达式的转换 ????//js中括号()是不包含语句的,和js中的&&,||,逗号等操作符是在函数表达式和函数声明上消除歧义的 ????//所以 ????var i=function(){console.log("hello")}() ????true &&function(){console.log("hello")}() ????0,function(){console.log("hello")}() ????!function(){console.log("hello")}() ????~function(){console.log("hello")}() ????-function(){console.log("hello")}() ????+function(){console.log("hello")}() ????new function(){console.log("hello")} ????var value=10 ????new function(){console.log(value)}(value) ??//括号传递参数
关于闭包的知识,就说这么多
解析js中作用域、闭包——从一道经典的面试题开始
原文地址:https://www.cnblogs.com/iDouble/p/8413214.html