JavaScript中的作用域和闭包

263 阅读4分钟

作用域和闭包

题目

● 对变量提升的理解

变量定义

函数声明(注意和函数表达式的区别)

● 说明this集中不同的使用场景

作为构造函数执行

作为对象属性执行

作为普通函数执行

call apply bind

● 创建10个 标签 ,点击的时候但出来对应的序号
//这里就不写用 let 的写法了
var i;
for(i = 0 ; i < 10 ; i++){ 
    (function(i){                //iife
        var a = document.createElement('a')
        a.innerHTML = i + '<br />'
        a.addEventListener('click',function(e){
            e.preventDefault()
            alert(i)
        })
        document.body.appendChild(a)
    })(i)
}
● 如何理解作用域

自由变量

作用域链,即自由变量的查找

闭包的两个场景

● 实际开发中闭包的应用
//闭包实际应用中主要用于封装变量,收敛权限

function isFirstLoad(){
    var _list = []
    return function (id) {
        _list.push(id)
        if(_list.indexOf(id) >= 0){
            return false
        }else{
            return true
        }
    }
}


//使用

var firstLoad = isFirstLoad()
firstLoad(10) //true
firstLoad(10) //false

firstLoad(20) //true
firstLoad(20) //false

知识点

● 执行上下文

1、 范围 : 一段 script标签 或者一个函数
2、 全局 : 变量定义、函数声明 一段script标签
3、 函数 : 变量定义、函数声明、this、arguments 函数

● this

this 要在执行时才能确认值 , 定义时无法确认
let a = {
    name : 'A',
    fn : function(){
        console.log(this.name)
    }
}
let b = {
    name:'hong'
}
a.fn() // A            

a.fn.call(b)   //hong          显示绑定 把this指向b

let fn1 = a.fn
fn1()  // 现在的this 指向了 window  所有什么都没有打印
1、作为构造函数执行
function Foo(name){
    this = {}  //可省略  用 new 关键字自动创建
    this.name = name
    return this //可省略  用 new 关键字自动返回
}
2、作为对象属性执行
let obj = {
     name : 'A',
     printName: function(){
         console.log(this.name)
     }
 }
 obj.printName()   //this 就指向 obj
3、作为普通函数执行
function fn(){
    console.log(this)      // 如果在浏览器环境下执行 这个this 就是 指向window this === window
}
fn()
4、call apply bind
function fn1(name,age){
    console.log(name) //张三
    console.log(age) // 20
    console.log(this) //{x: 100}
}
fn1.call({x:100},'张三',20) //用第一个参数来当this 后面的参数就是fn1函数的实参  
fn1.apply({x:100},['三哥',22]) //apply 也一样,只不过参数变为数组形式

//bind 使用
//错误的示范
//  function fn2(name){
//     console.log(name) 
//     console.log(this) 
//  }.bind({y:200})

//要用函数表达式来使用.bind
let fn2 = function(name){
    console.log(name) 
   console.log(this) 
}.bind({y:200})

 fn2('zhansan',20)

● 作用域

1、没有块级作用域
if(true){
    var name = 'zhangsan'
}
console.log(name) 

2、只有函数和全局作用域
var a = 100
function fn() {
    var a = 200
    console.log('fn',a) //200
}
console.log('global',a) //100         在外面是访问不到函数里面的变量
fn()

● 作用域链

var a = 100
function fn() {           //这里处于 fn函数里面的作用域 它的父级作用域是全局作用域
    var b = 200

    // 当前作用域没有定义的变量,即'自由变量'
    console.log(a)      
    console.log(b)     // 这个作用域没有定义变量b ,但是会往上一层作用域去查找  , 一层一层往上找,直到找到或者没找到
    //注意:函数父级作用域是函数定义的时候决定的,不是执行的时候决定的
}
fn()

// 例如

var a  = 100
function F1(){     // F1的父作用域是全局作用域
    var b = 200
    function F2() {  // F2的父作用域是F1
        var c = 300
        console.log(a);   //  a  是自由变量  往上找
        console.log(b);   //  b 是自由变量   往上找
        console.log(c);
    }
    F2()
}

● 闭包

1、函数作为返回值
function F1() {
    var a = 100            //2. 找到了a 
    //返回一个函数(函数作为返回值)
    return function () {     // 用一个函数作为返回值
        console.log(a)  //1. a 是自由变量  这个作用域没有定义a  就会往上一级作用域找a
    }
}
//f1 得到一个函数
var f1 = F1()      // 这样用中文解释就是 用 f1这个变量接收 F1() 函数调用返回的返回值 这个返回值是一个函数
var a = 200  // 全局作用域的 a  并不能影响函数里面的 a
f1()  // 因为这个返回值是一个函数  所有可以用() 来调用  虽然是在全局作用域下执行 但是函数的作用域并不是看在哪里执行的 而是看在哪里定义的
2、函数作为参数传递
function F1() {
    var a = 100
    return function () {
        console.log(a)   // 自由变量,父作用域寻找
    }
}

var f1 = F1()

function F2(fn){             //接收f1函数
    var a = 200
    fn()   //调用函数
}

F2(f1)  // 打印100   这里是在F2作用域下执行 f1函数 ,F2的 a 并没有影响到 f1里面的 a ,证明了函数的作用域并不是看在哪里执行的 而是看在哪里定义的