this作用域和闭包

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方法去读写,所以相当于隐藏了数据

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值