函数声明 函数表达式
- 区别是函数声明 可以提升,函数表达式不能提升函数,函数表达式只是个变量提升而不是函数提升
//var fn1 = undefined //这是var的变量声明的提升
//var a = undefined // 这是var的变量声明的提升
fn()//可执行
function fn(){
//函数声明
}
fn1()//undefined
var fn1 = function(){
//函数表达式
}
console.log(a)//undefined
var a = 100
var b = 200
console.log(b)//200
console.log(c)//抛错 c is not defined
fn('zhangsan')
function fn(name){
console.log(this)//指向window
console.log(arguments)//zhangsan
age = 20
console.log(name,age)//name不传值,就是undefined
var age //变量名提升是在fn函数作用域里的
bar(100)
function bar(num){//bar函数提升是在fn函数作用域里的
console.log(num)
}
}
题目
- 说一下变量提升的理解
- 说明this几种不同的使用场景
- 创建10个 a 标签,点击的时候弹出对应的序号
- 如何理解作用域
- 实际开发中的闭包的应用
知识点
执行上下文
- 范围: 一段< script > 或者 一个函数
- 全局:变量定义、函数声明 在一段< script >里
- 函数:变量定义、函数声明、this、 arguments
1.针对一段< script >执行上下文是全局的,执行上下文的范围是一段< script > 或者 一个函数
2.在全局中执行上下文(也就是在一段< script >里),会把变量定义、函数声明拿出来
3.在函数里,函数执行之前,会把变量定义、函数声明、this、 arguments 拿出来
注意,函数声明和函数表达式的区别
console.log(a)
var a = 100
fn('zhangsan');
function fn(name){
console.log(name)
age = 20;
console.log(name,age)
var age
}
//实际中不建议这么书写代码,缺乏可读性
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
this运行的几种场景
- 作为构造函数执行
function Foo(name){
//this = {} //执行上下文,函数中this先拿出来
this.name = name
//return this //返回this
}
var f = new Foo('zhangsan')
- 作为对象属性执行
var obj = {
name: 'A',
printName: function(){
console.log(this.name)
}
}
obj.printName()// this === obj //函数printName作为对象属性来执行
- 作为普通函数执行
function fn(){
console.log(this)
}
fn()//this === window //作为普通函数执行,this指向window
- call apply bind
call相对常用
function fn1(name,age){
alert(name)
console.log(this)
}
fn1.call({x:100},'zhangsan',20)//函数fn1运行,this === {x:100} // 'zhangsan',20 作为参数传入函数fn1中,
//call的作用就是改变this指向
fn1.apply({y:200},['zhangsan',20])//apply与call区别就是后面参数是否是数组形式
var fn2 = function(name){
alert(name)
console.log(this)
}.bind({z:300})//加了bind,函数fn2运行时,this指向{z:300},不然指向window
fn2('zhangsan');
//.bind() 只能用于函数表达式,不能用于函数声明,不然报错
作用域
- es5没有块级作用域
- es5只有函数和全局作用域
if(true){
var name = 'zhangsan'
//大括号里就是块级作用域
// es6 就有块级作用域 let const
//es5中尽量别再块级作用域里用var声明变量
}
console.log(name)// 'zhangsan'
//函数和全局作用域
var a = 100
function fn(){
var a = 200 //将变量定义在函数里,可以起到与外界隔绝的作用
console.log('fn',a)//a === 200,不用通过作用域链往层找a
}
console.log('global',a)
fn()//函数fn运行 就会修改全局变量a
作用域链
自由变量:当前作用域没有定义的变量
var a = 100
function fn(){
var b = 200
//当前作用域没有定义的变量,即“自由变量”
//函数可以规定作用域,函数体里可以定义变量
console.log(a)//100 //从父级作用域找a
//函数的父级作用域是函数定义时候的作用域
//而不是函数运行时候的作用域
console.log(b)//200
}
fn()
var a = 100
function fn1(){
var b = 200
function fn2(){
var c = 300
console.log(a)//a是自由变量
//fn2函数作用域中没有a变量
//a先从fn2中找,再从fn1中找,最后在全局作用域中找到a
console.log(b)//b是自由变量
console.log(c)
}
fn2()
}
fn1()
- 作用域
var a = 100
function fn(){
var b = 200 //函数作用域里的b
console.log(a)
console.log(b)
}
fn()
var b = 20000 //全局作用域里的b
//两个b没有任何关系,互不影响
- es5无块级作用域
if(true){
var name = 'zhangsan'
}
console.log(name)
//等同于,且推荐以下写法
var name
if(true){
name = 'zhangsan'
}
console.log(name)
闭包
function F1(){
var a = 100
//返回一个函数,(函数作为返回值)
return function(){
console.log(a)//这里的a是自由变量,就从父级找
//在定义的地方根据作用域链向父级作用域找a
}
}
//f1 得到一个函数
var f1 = F1()
var a = 200// 这个a定义在全局作用域里
f1()//100 //函数f1 在全局作用域下执行,但自由变量是根据定义时候的作用域查找
闭包的使用场景
- 函数作为返回值(上一个demo)
- 函数作为参数传递(下一个demo)
function F1(){
var a = 100 // 如果这行注释掉,最后结果是300,而不是200
return function(){
console.log(a)
}
}
f1 = F1()
function F2(fn){
var a = 200
fn()
}
var a = 300 // 上面的 var a=100 注释掉,同时这行也注释掉,结果是报错 is not defined
//因为这就是看在哪里定义的
F2(f1) // 100
解题
说一下变量提升的理解
- 变量定义
- 函数声明(注意和函数表达式的区别)
说明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)//此处的i是自由变量,点击的时候,循环完成
//全局变量的i===10,无论怎么点击都是10
})
document.body.appendChild(a)
}
正确写法
//正确写法
var i, a
for(i = 0, i < 10, i++){
(function(i){
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click',function(e){
e.preventDefault()
alert(i)//i找到参数的i
})
document.body.appendChild(a)
})(i)//i作为参数传入自运行函数,i存在与函数作用域里
}
如何理解作用域
- 自由变量
- 作用域链,即自由变量的查找
- 闭包的两个场景
实际开发中的闭包的应用
实际开发中闭包的应用:主要用于封装变量,收敛权限
//实际开发中闭包的应用:主要用于封装变量,收敛权限
function isFirstLoad(){
var _list = []
return function(id){
if(_list.indexOf(id) >= 0){
return false
}else{
return true
}
}
}
//使用
var firstLoad = isFirstLoad()
firstLoad(10)//true
firstLoad(10)//false
firstLoad(20)//true