面试准备1 变量提升 作用域 堆栈内存 闭包

重名问题处理

1.
所以变量var a 和function a 名字相同 也算是重名的 其实 是一个a 只是存储的值不一样
2.关于重名之后是如何处理的:

fn()                          
    function fn(){console.log(1);}
fn()                          
    function fn(){console.log(2);}
fn()                          
    var fn=100                    
fn()                          
    function fn(){console.log(3);}
fn()                          
    function fn(){console.log(4);}
fn()                          

变量提升 对于 var fn=100 只处理左边部分 声明var fn=
对于 function fn(){} 不只是声明,并且赋值
对于变量提升阶段 最后结果 function fn(){ console.log(4)}
一步步执行 到第六行 进行赋值操作 fn=100
第七行100

let不会变量提升1

2并且切断了全局变量和winddow的属性映射机制

let a=10,b=10
let fn=()=>{
    console.log(a,b);
    let a=b=20
    console.log(a,b);
}
fn()
console.log(a,b);

在一个作用域中let 不能够声明相同名字的变量 let a=12 let a=13 会语法报错

3 ,代码不再执行

4\color{red}{用了let 声明之后 变量只能用在后面 用在前面就会报错 没有定义 一票否决 第三行报错a is not defined}

let a=10,b=10
let fn=()=>{
    let a=b=20
    console.log(a,b);
}
fn()
console.log(a,b);

第三行 let a=20;b=20 a 是私有变量 b没有声明 向上级作用域查找 b =20
第三行 (私有a 20,全局b 20)
第七行 (全局a 10,全局b 20)

暂时性死区

let 会形成块级私有作用域

var a =12
if(true){
   console.log(a) //a未定义  这里变量名为a   前面是let  形成a的块级  不能往外找
   // a is not defined
    let a=13
}
var a =12
if(true){
   console.log(a) //a12 这里变量名为b   前面是let 形成b的块级  a 可以向外找 所以是12
    // a =>12
    let b=13
}
console.log(typeof a) //undefined
var a
console.log(a)// a is not defined

console.log(typeof a)//undefined  区别  1变量没有声明  2基于 typeof 不会报错   
console.log(a)// a is not defined
console.log(typeof a)// a is not defined   let 同样会报错 解决了暂时性死区的问题
let a 

区分私有变量和全局变量

var a=12,b=13,c=14

function fn(a){
    console.log(a,b,c)  //12 un 14   a全局  b局部  c全局
    var b=c=20;   //只var了b   没有var c    
    console.log(a,b,c) //12 20 20
}

fn(a)

var a=12,b=13,c=14

function fn(a){
       //第一件事情 形参赋值  a=12
       //第二件事情 变量提升  var b
       // 在私有作用域中  只有两种情况是私有变量  var  function   第二种  形参  形参也是私有变量
       //剩下的需要基于作用域链的机制向上查找
      console.log(a,b,c)  //12 un 14   a全局(理解错了 a  形参 私有变量)  b私有  c全局
      var b=c=a=20;   //只var了b   没有var c    
      console.log(a,b,c) //12 20 20
}

fn(a)//  a实参就是具体的 值    把全局变量a  的  值  12  传递给函数的形参  ==fn(12) 
       // 传的是a的值 12  不是a  所以 全局a  和形参 a(12)  没有关系
console.log(a,b,c)  //12 13 20

私有全局练习2

var ary=[12,23]
function fn (ary){
    //第一件事情 形参赋值  ary=AAAFFF222=[12,23]  形参是私有变量  和外面的ary 无关
    console.log(ary)   //12,23   私有ary
    ary[0]=100
    ary=[100]
    ary[0]=0
    console.log(ary)   //0
}
fn (ary)
console.log(ary)   //100,23   全局ary

查找上级作用域

var a=12
function fn (){
console.log(a)//12
}
fn()
var a=12
function fn (){
console.log(a)//12 当前函数执行的上级  和函数在哪里创建有关
}

function sum (){
var a=120
fn()
}
sum()  //a 12
var a=12
function fn (){
        //arguments  :实参集合
        //arguments.callee  当前函数本身
        //arguments.callee.caller  当前函数在哪里执行  全局就是null
        console.log(arguments.callee)//
}

function sum (){
        var a=120
        fn()
}
sum()
var n=10 
fuction fn(){
    var n=20
    function f(){
        n++
        console.log(n)
    }
    f()
    return f
}

var x=fn()
x()
x()
console.log(n)

闭包及堆栈内存释放

堆内存

aaafff111 放函数 对象 数组 想释放 把占用的变量变为null fn=null arry1=null

栈内存

函数执行的时候生成的私有作用域 () 一般情况下函数执行完之后就销毁

特殊情况

1\color{red}{函数执行完成 当前形成的栈内存中 ,某些内容被栈内存以外的变量占用了,此时占内存不能释放}

2全局栈内存只有在浏览器关闭的时候才会释放如果当前栈内存没有被释放 ,那么在栈内存中存储的基本值也不会被释放能够一直保存下来

var i=1

function fn(i){
return function (n){
console.log(n+(++i))//i++   ++i 
}
}

var f=fn(2) //先把fn 执行 把fn执行的返回结果 return 后面的值 赋值给f  
f(3) //把返回的结果执行
fn(5)(6)
fn(7)(8)
f(4)
var k=1
console.log(5+k++,k)   k++先算后加  ++K 先加后算

闭包保护

闭包

\color{red}{函数执行形成一个私有的作用域,保护里面私有变量不受外界干扰,这种保护机制称之为闭包}

面试说法

这是闭包 柯里化函数

function fn(){
        return function(){

        }
}
var f=fn()  

惰性函数 自执行函数

var utils=(function (){
        return {

        }
})()   

最简洁闭包

function fn()()

闭包的项目实战应用

真实项目中为了保证js的性能(堆栈内存的性能优化)应该尽可能地减少闭包的使用

多人团队协作开发,为了防止全局变量过多,那么此时我们完全可以把自己这一部分的内容封装到一个闭包中,

(function (){
        function fn(){
          //我自己的函数 外面加一个保护壳 全局变量fn变成私有变量fn
        }
})()

不仅如此,我们封装类库插件的时候,也会把自己的程序都存放起来。但是我们又需要暴露一些方法给客户使用,这样我们如何处理?

JQ:把需要暴露的方法抛到全局

window.jQuery = window.('#box).animate

(function(){
function jQuery(){

}
window.jQuery=window.$=jQuery
})()

jQuery()
$()

Zepto 这种方式基于return 把需要供外面使用的方法暴露出去

var Zepto=(funcrion(){
        return {
            xxx:function(){

            }
    }
})()

Zepto.xxx()    //这种模式就是单例模式

闭包保存

for ( var i=1; i<=5; i++) {
    tabList[i].onClick=function(){
        changeTab(i)
    }
}

所有的事件绑定都是异步机制 (比如给dom绑定onClick事件)
(同步编程:一件事一件事做,按顺序执行
异步:一块儿做)

绑定事件后不需要等待[changeTab(i)]执行,继续执行下一个任务(i++),所以[changeTab(i)]执行的时候循环早已结束,这个时候全局的i已经是循环最后的结果 5(全局的i)

主要原因 点击changeTab(i) 找的i 是全局的i 不是私保存下来的

解决

1.用自定义属性 用之前把i绑定在自身(tabList)上,保存下来

for ( var i=1; i<=5; i++) {
    tabList[i].myIndex=i
    tabList[i].onClick=function(){
        changeTab(this.myIndex)  //给当前元素的某个事件绑定方法,方法执行,方法中的this是当前操作的元素对象
    }
}

2用闭包的方式解决

for ( var i=1; i<=5; i++) {
      tabList[i].onClick=(function(n){  //onClick绑定的是返回的小函数  自执行函数在给事件赋值的时候就已经执行了
      var i=n
      return function(){
        changeTab(i)  //不让i 找全局的  找局部的i  ==>让他的上级作用域不是全局 而是局部 并且把i 保存下来
    }
    })(i)                          让自执行函数执行,把执行的返回值return 赋值给onClick
}

3i 2n i=>n=>i

具体步骤
i=0 第一次循环
tabList[0].onClick=(function(n){
//自执行函数形成一个私有作用域(不释放;返回的函数对应的堆地址被外面的事件
onClick占用)
//形参赋值 n=0
//变量提示 var i
//自上而下执行 var i=0
//执行return
var i=n
return function(){
changeTab(i)
}
})(i)//把本次全局i 当做实参传递给形参

总结

循环5次 形成5个不销毁的私有作用域(自执行函数执行,而每一个不销毁的占内存中都存储了一个私有变量i ,而这个值分别是每一次执行传递来的全局i的值 1,2,3,4,5)当点击的时候执行返回的小函数,遇到变量i ,向他自己的上级作用域查找,找到的i值1,2,3,4,5 达到了想要的结果

闭包方法2

for ( var i=1; i<=5; i++) {
     (function(n){
          tabList[n].onClick=function(){
                changeTab(n)
          }
     })(i)
} 

let方法

for ( let i=1; i<=5; i++) {
        tabList[i].onClick=function(){
            changeTab(i)
        }
}

bebal转换

"use strict";

var _loop = function _loop(i) {
  tabList[i].onClick = function () {
    changeTab(i);
  };
};

for (var i = 1; i <= 5; i++) {
  _loop(i);
}

{
let i=0
tabList[i].onClick=function(){
changeTab(i)
}
}
{
let i=1
tabList[i].onClick=function(){
changeTab(i)
}
}
{
let i=2
tabList[i].onClick=function(){
changeTab(i)
}
}
{
let i=3
tabList[i].onClick=function(){
changeTab(i)
}
}
{
let i=4
tabList[i].onClick=function(){
changeTab(i)
}
}
{
let i=5
tabList[i].onClick=function(){
changeTab(i)
}
}

基于es6来let创建变量 是存在块儿级作用域的(类似于私有作用域)

作用域(栈内存)
1.全局作用域
2.私有作用域
3.块儿级作用域( { } 前提是es6)

{
let a=12
}
console.log(a) // a is not defined
let a=100
{
      let a=100
{
{
   console.log(a)
}
 let a=100
}
}
if(1===1){
let a=12
}
console.log(a)
for(let i=0;i<5;i++){
//循环体也是块儿级作用域,初始值的变量也是当前本次块级作用域
//形成了5个块级作用域 每个块级作用域中都有一个私有变量i   变量值就是每一次循环i的值
}
console.log(i)//i is not defined

var obj={}  对象不是块级作用域 就是值

你可能感兴趣的:(面试准备1 变量提升 作用域 堆栈内存 闭包)