1.作用域
为可访问变量,对象,函数
的集合,是一个独立的空间,让变量不会外泄,作用就是隔离变量
作用域种类:
全局作用域
(window, document)函数作用域
(当前函数中)块级作用域
(大括号{}}里, const let 声明,if语句和for语句里面的{ }也属于块作用域,每次循环都产生一个块级作用域 )
// es6块级作用域
If (true) {
let x = 100
}
console.log(x) // 会报错
var let const 区别
- var let声明
变量
,const声明常量
- let只在
当前作用域
生效,不能跨块访问,也不能跨函数访问
for(let I = 0; I< 10; I++) {
let a = 4
}
console.log(I) // I is not defined
console.log(a) // a is not defined
- var
可全局访问
,能跨块访问,不能跨函数访问
for (var j = 0; j < 10; I++){
var a = 5
}
console.log(j) // 10
console.log(a) // 5
- var存在
变量提升
,可以先使用后声明
,只不过变量是Undefined,
而let先使用后声明会报错 is not undefined
console.log(a) // undefined
var a = 4
console.log(b) // b is not defined
let b = 12
undefined 和 is not defined
undefined是js基本数据类型
,变量未赋值或者函数没有返回值
时返回
is not defined变量未定义
,是一种错误类型
自由变量
没被定义但被使用
的变量,一层一层向上级作用域
查找,如果找到全局都没找到,报错 xx is not defined
作用域链
在当前作用域中没有查到值,就会向上级作用域
去查,直到查到全局作用域,
形成一个链条
在创建时候就确定了的
2. This(取值是在函数执行时
确认的!!!不是函数定义时确认的!)
This的不同应用场景,如何取值?
- 作为普通函数去调用(window)
- 使用call apply bind去调用(传入的值)
- 作为对象方法被调用(返回对象)
- 在构造函数或者类中调用(返回实例)
- 箭头函数(返回上一级this值)
// 作为普通函数调用
Function fn1() {
console.log(this)
}
fn1() // window,
// 使用call调用
fn1.call({ x: 100 }) // {x: 100}
// 使用bind调用
const fn2 = fn1.bind({ x: 200 }) // bind返回一个新函数
fn2() // 200, 所以要执行新函数
// 作为对象方法调用
const zhangsan = {
name: ‘张三’,
sayHi() {
// 作为对象方法被执行,返回当前对象,this即当前对象
console.log(this)
},
wait() {
setTimeout(function(){
// 是setTimeout函数执行的打印this,作为一个普通方法去执行,而不是作为sayHi()对象方法执行的,所以this === window
// setTimeout执行函数中的this永远指向window,因为setTimeout是挂载到window上执行的,调用的代码运行在与所在函数完全分离的执行环境上
console.log(this)
})
}
}
// 箭头函数调用
const zhangsan = {
name: ‘张三’,
sayHi() {
// this即当前对象
console.log(this)
},
waitAgain() {
console.log(this) // 同样作为对象方法被执行,this=== 当前对象
setTimeout(() => {
// 箭头函数的this永远取它上级作用域的this值,它自己本身不会决定this的值,this即当前对象
console.log(this)
})
}
}
// class方法中调用
class People {
constructor(name) {
this.name = name // 此处this为当前创建的实例zhangsan
this.age = 20
}
sayHi() {
console.log(this) // 此处this也是当前实例zhangsan
}
}
const zhangsan = new People(‘张三’)
zhangsan.sayHi() // People {name: '张三', age: 20}
class Student extends People {
constructor(name, number) {
super(name)
this.number = number
}
sayHi() {
console.log(`姓名 ${this.name} 年龄 ${this.age}`)// 打印当前对象
}
}
const xialuo = new Student(‘xialuo’, 18)
xialuo.sayHi() // sayHi为xialuo对象上的方法,此对象上有name age属性
xialuo._proto_.sayHi() // undefined因为这里的sayHi被当作 xialuo._proto_隐式原型对象上的方法,这个隐式原型对象上没有name和age属性
xialuo.__proto__.sayHi.call(xialuo)
bind函数的使用(谁执行
this就指向谁)
Function fn1(a, b, c) {
console.log(‘this’, this)
console.log(a, b , c)
return ‘this is fn1’
}
const fn2 = fn1.bind1({ x: 100}, 10, 20)
const res = fn2()
console.log(res)
手写bind函数 - bind(要绑定的this,参数,参数…)
// class原型函数
Function.prototype.bind1 = function() {
// 1.将参数arguments解析为数组,
const args = Array.prototype.slice.call(arguments) // 将arguments赋值给Array.prototype.slice的this,通过slice分隔成数组
// 2.获取 this(取出数组第一项,数组剩余的就是传递的参数)
const that = args.shift() // 指bind函数中第一个要指向的参数{x: 1000}
const self = this // 调用bind的函数或者对象,此处指当前函数 fn1.bind(…)中的fn1
// 返回一个函数
return function() { // const fn2 = fn1.bind1({ x: 100}, 10, 20)
// 执行原函数,并返回结果 // const res = fn2()
return self.apply(that, args) // apply(this, 参数列表)
}
}
箭头函数和普通函数的区别
1.this指向不一样,普通 函数作用域为调用它的对象
,箭头函数为当前上下文
(如声明在全局那么this的作用域即为上下文)
2.arguments,普通函数可通过arguments
来实现重载。箭头函数没有arguments,代替的是...rest
(剩余参数)
3.原型对象,普通函数有自己的原型对象,箭头函数没有原型对象
。
4.new实例化,箭头函数不能作为构造函数,使用New 实例化
5.箭头函数简短,通常是匿名函数
。
3.闭包:
从一个作用域可以使用另一个作用域的变量
,比如在函数里返回一个函数,将内部 变量return出去,在外部可以使
- 函数
作为参数被传递
function print(fn) {
let a = 200
fn()
}
let a = 100
function fn() {
console.log(a)
}
print(fn) // 100, 变量查找在函数定义的地方,不是函数调用的地方
- 函数
作为返回值
被返回
function create() {
let a = 100
return function() {
console.log(a)
}
}
let fn = create()
let a = 200
fn() // 100,变量查找在函数定义的地方,不是函数调用的地方
内存泄漏:变量没有再被引用,且没被垃圾回收机制清除。
1)闭包就是函数里嵌套函数,并return出去。它可以访问其他函数的内部变量,或者一直被引用
,并不经过内存回收机制
,所以闭包会引起内存泄漏。
2)避免闭包引起的内存泄漏,我们可以在退出函数之前,将不使用的局部变量赋值为null。
闭包常用场景
1.小范围内使用全局变量,返回一个或者几个函数,调用执行
exp1:
(fuction() {
var test2 = 2
function outer() {
alert(test2)
}
function test() {
alert('测试闭包', test2)
}
outer()
test()
})()
alert(test2) // 在函数外访问不到test2
addEventListener写在函数内部
window.onload = function() {
btn.addEventListener('click', function(){
...
})
}
3.接收一个回调函数作为参数然后执行
封装一个请求接口的方法,调用后执行传递进来的回调函数
function requestUrl(callback) {
let promise = new promise((resolve, reject) => {
this.$http.get('http: //...').then((res) => {
resolve(res)
callback()
}).catch(err => {
reject(err)
})
})
}
4.提供一个隐藏数据的api(跟vue的数据劫持有点像)
function createCache() {
const data = {}
return {
set: function(key, value) {
data[key] = value
},
get: function(key) {
return data[key]
}
}
}
const c = createCache()
c.set('a', 100)
c.get('a')
因为不可以直接读取修改data,只可以通过get与set方法去读写,所以相当于隐藏了数据