1、题目
- 说一下对变量提升的理解
- 说明 this 几种不同的使用场景
- 创建10个
<a>
标签, 点击的时候弹出来对应的序号 - 如何理解作用域
- 实际开发中闭包的应用
2、知识点
⑴、执行上下文
console.log(a) // undefined
var a = 100;
fn('zhangsan') // 'zhangsan' 20
function fn(name){
age = 20
console.log(name, age)
var age
}
①、
②、函数
函数在执行之前,也会创建一个函数执行上下文环境,函数执行上线文中会多出this
arguments
和函数的参数。
- 范围:一段
<script>
或者 一个函数 - 全局:变量定义、函数声明
- 函数:变量定义、函数声明、this、arguments
备注:注意 “函数声明” 和 “函数表达式” 的区别
⑵、this
**this要在执行时才能确认值, 定义时无法确认,**因为this
是执行上下文环境的一部分,而执行上下文需要在代码执行之前确定,而不是定义的时候
var a = {
name: 'A',
fn: function(){
console.log( this.name )
}
}
a.fn() // this === a
a.fn.call({ name: 'B' )} // this === { name: 'B' }
var fn1 = a.fn
fn1() // this === window
- 作为构造函数执行
- 作为对象属性执行
- 作为普通函数执行
- 用于
call
apply
bind
// 构造函数
function Foo( name ){
this.name = name
}
var f = new Foo( 'zhangsan' )
// 作为一个对象的属性
var obj = {
name: 'A',
printName: function(){
console.log( this.name )
}
}
obj.printName()
//普通函数的 this
function fn() {
console.log(this)
}
fn() // window
// call apply bind
function fn1( name, age ){
// alert( name )
console.log( 'name', name )
console.log ( this )
}
// fn1.call({ x: 100}, 'zhangsan', 20)
fn1.apply({ x:200}, [ 'zhangsan', 20 ])
var fn2 = function( name, age ){
console.log( 'name', name )
console.log( this )
}.bind({ y: 300 })
fn2( 'zhangsan', 20 )
⑶、作用域
if (true) {
var name = 'zhangsan'
}
console.log(name)
从上面的例子可以体会到作用域的概念,作用域就是一个独立的地盘,让变量不会外泄、暴露出去。上面的name
就被暴露出去了,因此,JS 没有块级作用域,只有全局作用域和函数作用域。
var a = 100
function fn() {
var a = 200
console.log('fn', a)
}
console.log('global', a)
fn()
全局作用域就是最外层的作用域,如果我们写了很多行 JS 代码,变量定义都没有用函数包裹,那么他们就全部都在全局作用域中。这样的坏处就是很容易撞车。
⑷、作用域链
var a = 100
function fn() {
var b = 200
console.log(a)
console.log(b)
}
fn()
console.log(a)
要得到a
变量,但是在当前的作用域中没有定义a
(可对比一下b
)。当前作用域没有定义的变量,这成为自由变量。自由变量如何得到 ———— 向父级作用域寻找。
var a = 100
function F1() {
var b = 200
function F2() {
var c = 300
console.log(a)
console.log(b)
console.log(c)
}
F2()
}
F1()
如果父级也没呢?再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。
⑸、闭包
应用场景
- 函数作为返回值
- 函数作为参数传递
// 函数作为返回值
function F1() {
var a = 100
return function () {
console.log(a)
// a 自由变量, 向父作用域去寻找 - 函数定义
}
}
var f1 = F1()
var a = 200
f1()
// => 100
// 函数作为参数来传递
function F1() {
var a = 100
return function () {
console.log(a)
}
}
function F2(f1) {
var a = 200
console.log(f1())
}
var f1 = F1()
F2(f1)
// => 100
自由变量将从作用域链(父级向上)中去寻找,但是依据的是函数定义时的作用域链,而不是函数执行时。
3、解题
⑴、说一下对变量提升的理解
- 变量定义
- 函数声明 ( 注意和函数表达式的区别 )
⑵、说明 this 几种不同的使用场景
- 作为构造函数执行
- 作为对象属性执行
- 作为普通函数执行
- call apply bind
⑶、创建10个<a>
标签 点击的时候弹出对应的序号
错误的写法
var i, a
for (i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
正确的写法
var i
for (i = 0; i < 10; i++) {
(function (i) {
// 函数作用域
var a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
// 自由变量, 要去父作用域获取值
alert(i)
})
document.body.appendChild(a)
})(i)
}
拓展:
DocumentFragment
关于 DOM 操作的性能问题
⑷、如何理解作用域
- 自由变量
- 作用域链, 即自由变量的查找
- 闭包的两个场景
⑸、实际开发中闭包的应用
闭包的实际应用,主要是用来封装变量,收敛权限。即把变量隐藏起来,不让外面拿到和修改。
function isFirstLoad() {
var _list = []
return function (id) {
if (_list.indexOf(id) >= 0) {
return false
} else {
_list.push(id)
return true
}
}
}
// 使用
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true
// 在 isFirstLoad 函数外面, 根本不可能修改掉 _list 的值