标签 作用域 下的文章


// 闭包 

// 函数套函数 子函数调用父函数的局部变量 - 闭包

function fn(){
    var a = 5;
    function fn2(){
        console.log(a);
    }

    fn2();
}

fn();

闭包能做什么

示例:点击不同的按钮打印不同的下标


<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>

<script>
    var btns = document.querySelectorAll('button');
		
    for(var i = 0;i<btns.length;i++){
        // 按选项卡那的思路的话,给个自定义属性把下标存起来,
        btns[i].index = i;
        btns[i].onclick = function(){
            console.log(this.index);
        }
    }
</script>

优化一:
循环过程中,可以把里面的代码封装成函数,然后循环里调用


for(var i = 0;i<btns.length;i++){
    tab();
}
// 这样写没太大区别,只是把它放外面了
function tab(){
    btns[i].index = i;
    btns[i].onclick = function(){
        console.log(this.index);
    }
}

如果不用声明自定义属性btns[i].index = i;存标签上了,而是用形参把它传进去


for(var i = 0;i<btns.length;i++){
    // 把i传进去
    tab(i);
}
// 然后tab这接收一个值,接收的这个值就是每次调用是i的值
function tab(index){
    // 这样就可以直接在这调用index
    btns[index].onclick = function(){
        // 这就直接使用index
        console.log(index);
    }
}

以上写法也是没问题的,上面index可以直接用的原因


// 因为这段代码里就有闭包的概念
// 函数套函数,
// function tab(index) 套 function(){}
// 子函数调用父函数的局部变量,就称之为闭包
// console.log(index) 里的 index 是调用的 function tab(index) 里的 index(局部变量)

// 如何实现i值的利用的(帮助解决掉局部的属性,下标的存储,不存储下标了也管用)

// 首先这里的循环的作用就是将里面的代码执行3次而已
for(var i = 0;i<btns.length;i++){
    tab(i);
}

// 言外之意就是这个函数体tab(i)有三个,并且会立即执行

// 闭包作用 可以存储局部变量值
function tab(index){
    btns[index].onclick = function(){
        console.log(index);
    }
}

// 解析循环过程
// tab(i),参数里定义了i,循环过程就相当于每次把i传入不同的函数里 


(function tab(index){ // 2. 参数传入后,这个index就代表0
    // 3. 这时这个局部变量在这个函数里就相当于写了个变量声明
    // var index = 0; 这时值就给固定死了

    // btns[index]就等于btns[0]
    btns[0].onclick = function(){
        console.log(0); // 局部函数调用父级作用域下的局部变量(var index = 0;)
    }
})(0) // 1. 第一次传入了0

(function tab(index){
    // var index = 1; 
    btns[index].onclick = function(){
        console.log(index);
    }
})(1) // 第二次传入了1

(function tab(index){
    // var index = 2; 
    btns[index].onclick = function(){
        console.log(index);
    }
})(2) // 第三次传入了2


// 类似选项卡就可以用这种方法去实现
for(var i = 0;i<btns.length;i++){
    tab(i);
}

// 闭包作用 可以存储局部变量值
function tab(index){
    btns[index].onclick = function(){
        console.log(index);
    }
}

// 简化
// tab(i)无非就是function tab(index){...}的自执行
// 去掉命名,简写成匿名函数,让它自执行
for(var i = 0;i<btns.length;i++){
    (function(index){
        btns[index].onclick = function(){
            console.log(index);
        }
    })(i) //这就把i传递进去
}

JS 预解析机制 (变量提升 Hoisting)

预解析:在当前作用域下,js运行之前,会把带有varfunction关键字声明的变量先声明,然后从上至下解析js语句



console.log(a);  //undefined

let a = 0;

console.log(a);  //0

// 预解析
// 会将页面中的js变量都前置声明!

// 变量声明
let a;

// 只声明了变量,没赋值,打印出来就是undefined
console.log(a);  //undefined

// 赋值变量
a = 0;

// ----------------------------

// 如果连声明都没有,就会报错
console.log(a);  //Uncaught ReferenceError: a is not defined

// let a = 0;



function fn(){
    console.log(1);
}

fn();  // 1

// 预解析
// 会将页面中的函数声明前置!

fn();  // 1

// 就算函数写到下面,它也会给前置了
function fn(){
    console.log(1);
}



fn();  // Uncaught TypeError: fn is not a function

let fn = function (){
    console.log(1);
}

fn();  // 1

// ----------------------------

// 函数声明 - 命名函数
// function fn(){

// }

// fn 变量 =  函数表达式
let fn = function (){
    console.log(1);
}

//它会理解为你声明了一个变量,只不过变量里面存的是函数而已

// 声明变量
var fn;

fn();

// 赋值
fn = function(){
    console.log(1);
}

作用域 (scope)


// 作用域  - 在特定的区域可以使用

// 函数 外面的变量 在里面随便用  - 全局变量 (可以在任何函数下调用)

let a = 12;

function fn(){
    console.log(a)
}

fn(); //12

// ----------------------------

// 函数 里面的变量 在外面不能用  - 局部变量 (只能在函数内部使用)

function fn(){
    let a =5;
    console.log(a);
}

fn();
console.log(a);  //Uncaught ReferenceError: a is not defined

var a =12;

function fn(){
    a = 5;
}

console.log(a); // 12

// ----------------------------

var a =12;

function fn(){
    // 没有var 就是赋值
    a = 5;
}

fn();

console.log(a); // 5

// ----------------------------

var a =12;

fn(); // 预解析

console.log(a); // 5

function fn(){
    a = 5;
}

// ----------------------------

var a =12;

console.log(a); // 12

fn();

function fn(){
    a = 5;
}

// ----------------------------

var a =12;

fn();

console.log(a); // 12

function fn(){
    var  a = 5;
}

// ----------------------------

var a =12;

function fn(a){
    // 形参声明过这个名称,函数内部这就是一个局部变量
    // 形参 -- 局部变量声明
    a = 5;

    return a;
}

console.log(fn()) //5

console.log(a); //12

多个作用域嵌套(作用域链)



// 定义的函数,看到调用后再去看函数内的代码  -- 追根溯源,找到函数触发的点,再去看函数发生了什么

function fn1(){
    var a = 12;
    
    // 函数内声明的函数称为局部函数
    function fn2(){
        var b = 5;
        return a+b;
    }
    return fn2();
}


console.log(fn1()); // 17


// 函数内查找变量时 - 顺序 
// 先从自身作用域下查找 - 如果没有就会从父级作用域下找 - 如果父级也没有 还会往上找
// 由内向外 - 一层一层 - 直到找到window下面
// 反之 不能调用局部变量!(内层作用域能访问外层作用域,而外层不能访问内层。)

var c = 1;
function fn1(){
    var a = 12;
    
    // 局部函数
    function fn2(){
        var b = 5;

        return a+b+c;
    }
    
    // b是fn2的局部变量,它只能在fn2的函数内去调用
    // console.log(b);

    return fn2();
}

console.log(fn1()); // 18