一、作用域和自由变量
1.作用域:
作用域代表的就是一个变量的合法使用范围,作用域分为全局作用域,函数作用域,还有ES6新增的块级作用域
- 全局作用域就是js最外层的作用域,
- 函数作用域是Js通过函数创建一个独立作用域
- Es6中新增了块级作用域,块作用域由
{ }
包括,if
语句和for
语句里面的{ }
也属于。只适用于const,let
2.自由变量:
当前作用域外的变量都是自由变量,一个变量在当前作用域没有定义,但是被使用了,就会向上级作用域,一层一层依次查找,直至找到为止,找到这个变量后就会停止,不会继续查找这个变量,如果全局作用域都没有找到这个变量就会报错
3.作用域链
向上级作用域查找变量的过程会形成一个链条,这个逐渐查找的过程就是作用域链。
4.变量提升(预解析)
每个var
声明的变量,function
声明的函数存在变量提升。let const
不存在变量提升
1.在js中声明一个并定义一个变量时,会把声明提前,以下会打印出undefined
//声明之前未定义,会在js的最上方会形成一个预解析池,用来存储声明了但没有先定义的变量
console.log(a)
var a = 10
console.log(a)
2.函数声明也是,以下函数相当于把整个fn提到作用域的最上面,所以调用fn时会正常打印jack
fn('jack');//jack
function fn (name){
console.log(name)
}
3.不过函数表达式不行,以下是一个函数表达式,js会把var fn提到作用域最上面,没有把函数提上去,所以会报错。
fn("jack");//报错
var fn = function(name) {
console.log(name);
};
二、闭包
· 闭包:函数里面可以访问外边的,但是外边不能访问里边的。 形式函数中套了一个函数,内层函数可以访问外层函数中的局部数据
闭包最大的作用就是隐藏变量,闭包的一大特性就是内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回了之后
闭包的优点:变量长期驻扎在内存中,可以隔离作用域,不造成全局污染
闭包的缺点:由于闭包长期驻留内存,则长期这样会导致内存泄露
//闭包隐藏数据,只提供API
function createCache(){
const data={} //闭包中的数据,被隐藏,不被外界访问
return {
set:function(key,val){
data[key]=val
},
get:function(key){
return data[key]
}
}
}
const c=createCache()
c.set('a',100)
console.log(c.get('a')) //100
两种常见方式
//函数作为一个返回值
function create(){
const a=100
return function(){
console.log(a)
}
}
let fn = create()
let a=200
fn() //100
//函数作为参数被传递
function print(fn){
const a=200
fn()
}
const a=100
function fn(){
console.log(a)
}
print(fn) //100
三、this
this
取什么值是在函数执行的时候确认的,不是在定义的时候确认的
- 在浏览器内,在全局范围内this指向window对象
- 在函数中,this永远指向最后调用它的那个对象
- 在构造函数中,this指向new出来的那个新的对象
- call,apply,bind中的this被强绑定在指定的那个对象上
- 箭头函数中的this比较特殊,箭头函数this为父级作用域的this,不是调用时的this,要知道前四种方式,都是调用时确认,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来。
- apply、call、bind都是js给函数内置的一些API。调用他们可以为函数指定this的执行,同时也可以传参
const zhangsan={
name:"张三",
sayHi(){
//this 即当前对象
console.log(this)
},
wait(){
setTimeout(function(){
//this===window
console.log(this)
})
}
}
//setTimeout本身触发的执行,作为一个普通函数执行,不是作为一个方法执行
const zhangsan={
name:"张三",
sayHi(){
//this 即当前对象
console.log(this)
},
wait(){
setTimeout(()=>{
//this 即当前对象
console.log(this)
})
}
}
//箭头函数取值是取得上级作用域的值
//模拟bind(class原型的函数)
Function.prototype.bind1 = function () {
//将数组拆解为数组
const args = Array.prototype.slice.call(arguments) //将一个列表变成数组
//获取this(数组第一项)
const t = args.shift()
// fn1.bind(...)中的fn1
const self = this
// 返回一个函数
return function () {
return self.apply(t, args)
}
}
function fn1(a, b, c) {
console.log('this', this)
console.log(a, b, c)
return 'this is fn1'
}
//第一个this,
const fn2 = fn1.bind1({ x: 100 }, 10, 20, 30) //bind可以传入多个参数
const res = fn2()
console.log(res)
//bind是返回一个新的函数执行,call是直接调用就会执行