前端JS知识要点总结(3)

作用域和闭包

该章节共包括以下几个知识点:

  • 执行上下文
  • this
  • 作用域
  • 作用域链
  • 闭包

题目十:对于变量提升的理解

变量提升并不是很难的概念,从原理层面解释,变量提升是因为js在执行前会有一次“预编译”过程,由于这个过程的存在,会导致只要是变量声明形式的内容,都会被提前定义,但是不会被赋值。有点类似于这样:

// 编写代码
// source code
var a = 100

// 代码运行分为两个阶段
// pre-combiling
var a
// running
a = 100

于是就会导致一些看起来比较难以理解,其实很简单的现象:

console.log(a)
//console.log(b)
var a = 100
console.log(a)


// 执行顺序
var a
console.log(a)
//console.log(b) //如果加上这句,会报错(Error: b is not defined)
a = 100
console.log(a)

// 执行结果
undefined
100

从上面的例子可以看出来,undefined 和 is not defined 的含义不同。

另外有一个比较重要的知识点,就是对于函数而言,有两种定义方法,一种是函数声明,一种是函数定义:

function a() {  // 函数声明
    console.log("this is function a")
}
var b = function() { // 函数表达式
    console.log("this is function b")
}
  • 函数声明形式,会在预编译阶段直接被定义好,即不管这段代码出现在什么位置,在执行的时候都会被提升到预编译阶段。因此,不管这段代码出现在什么位置,都可以在程序的任何位置使用
  • 而对于函数表达式的形式,类似于普通变量的定义,在这段代码出现之前,如果访问了b,会发现其值为undefined,因此只有在这段代码之后才能把它当做函数来使用。

题目十一:说明this的几种不同的使用场景

this的出现原因:


function printName() {
    console.log(this.name)
}
var a = {
    name: "wufan"
}
var b = {
    name: "sundan"
}
printName.call(a) //wufan
printName.call(b) //sundan

从上面的例子可以看出来,如果不使用this的话,需要给函数传递一个上下文环境,例如下面的形式:

function printName(context) {
    console.log(context.name)
}
printName(a)
printName(b)

虽然上面的形式看起来也很简洁,但是不得不说明的是,通过this来隐式的传递参数更为优雅。这并不是强词夺理,而是随着编程过程中代码量逐渐增加,使用的模式越来越复杂的时候,显式的传递会导致代码越来越混乱,使用this的话则不会
——整理自《你不知道的JavaScript(第二部分)》

但是我不太理解,因为如果不使用this的话不就是普通的函数编程吗?其他语言的编写不就是这样完成的吗?

题目十二:创建10个a标签,点击时弹出对应的序号

方法一:事件委托

<body>
    <div id="container">
        <a id="a1">a1</a>
        <a id="a2">a2</a>
        <a id="a3">a3</a>
        <a id="a4">a4</a>
        <a id="a5">a5</a>
        <a id="a6">a6</a>
        <a id="a7">a7</a>
        <a id="a8">a8</a>
        <a id="a9">a9</a>
        <a id="a10">a10</a>
    </div>
    <script>      
        document.getElementById("container").addEventListener('click', function(e) {
            e.preventDefault()
            if (e.target) {
                console.log(e.target.innerText)
            }
        })
    </script>
</body>

或者循环事件绑定的形式:

for (var i = 0; i < 10; i++) {
    (function(i) {
        var a = document.createElement('a')
        a.innerHTML = i + '<br>'
        a.addEventListener('click', function(e) {
            e.preventDefault()
            console.log(i)
        })
        document.body.appendChild(a)
    })(i)
}

下面给出一种错误写法:

for (var i = 0; i < 10; i++) {
    var a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function(e) {
        e.preventDefault()
        console.log(i)
    })
    document.body.appendChild(a)
}

结果为不论点击哪一个,输出都为10,因为click事件是一个非阻塞事件,会在点击的时候才发生。而点击的时候,i已经循环完毕,I的值为10,因此不论点击哪一个,结果都是10
修改方法:
将var i改为let i,并且要放在括号内定义i

for (let i = 0; i < 10; i++) {
    var a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function(e) {
        e.preventDefault()
        console.log(i)
    })
    document.body.appendChild(a)
}

上面的方法可行的原因是,let会创建块级作用域(ES6新特性),也就是同事创建了10个作用域分别用于执行click事件。

而上面使用立即执行函数进行封装的原理也是类似的,通过立即执行函数达到了将内部封装为一个函数作用域。

题目十三:如何理解作用域

JS没有块级作用域,只有函数作用域和全局作用域

ES6的新特性支持使用let,const等来创建类似于块级作用域的代码机制。

作用域是一套规则,用于确定在何处以及如何查找变量。如果查找的目的是对变量进行赋值,那么就会使用LHS查询,如果目的是获取变量的值,就会使用RHS查询。
——摘录于《你不知道的JavaScript(第一部分)》

题目十四:实际开发中闭包的应用

闭包的使用场景:将函数作为返回值,将函数作为参数进行传递。在这两种情况时,也就形成了闭包

闭包的应用主要是:可以用于封装变量,可以用于收敛权限(也就是函数的外面无法修改到内部的值,只能通过借口函数进行操作。)

// this is a simple example for closure(闭包)
function a() {
    var x = 100
    function b() {
        console.log(x)
    }
    return b
}
var c = a()
c()
// this is another example
function wait(message) {
    setTimeout(function timer() {
        console.log(message)
    }, 1000)
}

wait("Hello, closure!")

上面这个例子是将函数timer()当做参数进行的传递,从而产生了闭包。即timer()依然保有wait()作用域的闭包,即使在执行完之后也是。

只要使用了回调函数,其实就是在使用闭包

最后再写一个闭包应用的小功能,用于判断变量是否为第一次加载:

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

var firstload = isFirstLoaded()
firstload(10)   // true
firstload(10)   // false
firstload(20)   // true
firstload(30)   // true
firstload(20)   // false
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值