【JS之this、作用域与闭包】

this

this:当前函数的执行环境

当一个函数被调用时,JS引擎会为当前函数创建一个执行环境,用来存储函数内部的变量和对象等,this就用来指代当前执行环境的上下文

与this相关的关键字:call/apply/bind,它们可以用来改变函数的执行环境,实现函数的高阶调用、绑定函数上下文等

this永远指向一个对象,this的指向取决于函数的调用位置,或通过call/apply/bind改变

context(上下文环境,webpack钩子,compiler对象,compilation对象始终贯穿于整个钩子的)

全局作用域下的this指向

全局作用域:没有包含在任何函数中的代码块

// 1. 全局作用域下的this
console.log(this) //window
// 打印宿主环境根对象,浏览器:Window, node:global

function test(){
	console.log(this)
}
test() // window

结论:this只会指向对象,不会指向其他的数据类型

function fun(){
	console.log(this)
}
var obj = {
	s: "a",
	f: fun
}
var s = "b"

obj.f() // obj
fun() // window
// 对象初始化时,会绑定this

结论:this的指向和调用有关和定义无关

对象方法中的this指向

var name = 'window'
const obj2 = {
	name: "obj2",
	fun: function(){
		console.log('fun',this.name) // obj2
		function fun2() {
			console.log('fun2',this.name) // window
		}
		fun2()
		// 通过箭头函数可以解决this指向问题
		const fun3 = () => {
			console.log('fun3', this.name) // obj2
		}
		fun3()
	}
}
obj2.fun()

对象的方法会进行this自动绑定,这并不代表方法的内部函数也会自动绑定this

构造函数中的this指向

function Person(name){ // 构造器Person
	this.name = name
	this.sayName = function (){
		console.log(this.name) // p1
	}
}
const p1 = new Person('p1')
p1.sayName()

call/apply方法中的this指向

函数对象具有call和apply方法,调用call和apply方法时,this指向当前函数对象
call和apply方法允许开发者在调用方法时自定义this

let obj1 = { obj: obj2}
let obj2 = { fun() { console.log(this) }}
obj2.fun.call(obj1) // this是obj1
obj2.fun.call(obj1.obj) //this 是obj2

实现call

Function.prototype.selfCall = function (context, ...params){
    context== null ? context = window : null
    // context 为空则赋值window,否则不做操作
    !/^(object|funciton)$/.test(typeof context)?context = Object(context) :null
    // 判断context参数的数据类型,不为对象或函数,则强制转换
    let _this = this,result = null,UNIQUE_KEY = Symbol('UNIQUE_KEY')
    // 此时的this指向调用selfCall方法的函数对象
    context[UNIQUE_KEY] = _this
    result = context[UNIQUE_KEY](...params)
    // 为context增加一个方法,并调用
    // 调用该方法中this指向context
    delete context[UNIQUE_KEY]
    return result
}

注意:undefined == null是true

实现bind

Function.prototype.selfBind = function (context, ...outParams){
	let _this = this
	// this指向调用selfBind方法的函数对象
	return function(...innerParams) {
		_this.selfCall(context, ...outParams.concat(...innerParams))
	}
}

bind 和call、apply的区别: bind不会立即执行,而是返回一个新函数
如果需要改变函数中的this指向 就不能使用箭头函数

const say = () => {
	console.log(this)
}
const obj = {}
say.call(obj) // window

箭头函数中的this指向

箭头函数中的this指向调用该函数的上下文环境(即当前作用域所属的对象)

let obj = {
	name: 'lily',
	fun: function(){
		setTimeout(() => console.log(this.name), 100)
	}
}

obj.fun() // lily

函数作为参数和返回值时的this指向

链式调用时常用

事件绑定中的this指向

函数中的 this 指向通常是绑定事件的元素

总结

情况this值
以函数形式调用时(隐式绑定)永远都是根对象
以方法形式调用时(隐式绑定)调用方法的对象
以构造函数形式调用时(隐式绑定) 新创建的对象
用call、apply、bind调用时(显示绑定)传参指定的那个对象

作用域与闭包

作用域

JS中作用域分为两种:词法作用域和动态作用域
词法作用域: 变量只能在定义它的代码块中使用,一旦离开该代码块,变量就会消失
动态作用域: 变量可以在函数内部被重新定义,并可以在不同的函数调用之间共享

闭包

闭包: 一个函数能够访问并操作其创建时所在词法作用域中的变量的能力;闭包就是能够将变量保存到函数外部的一种机制

创建方式:
使用嵌套函数:用于实现模块化编程、封装私有属性
使用call/apply/bind:用来改变函数执行环境,实现高阶调用

使用场景: 实现私有属性,缓存计算结果,模拟事件处理程序
缺点: 容易导致内存泄露,难以维护

var boxs = document.querySelectorAll('.box')
var boxDoms = Array.prototype.slice.call(boxs)

function $(selector){
	const dom = document.querySelector(selector)
	return{
		css:function (styleObj){
			Object.entries(styleObj).forEach(([key,value]) =>{
				dom.style[key] = value
			})
			return this
		},
		animate: function(){}
	}
}

$('.box').css({
	color: 'red'
})

for(var i = 0; i< boxDoms.length;i++){
	const dom = boxDoms[i]
	setTimeout(function(){
		console.log(i)
		dom.addEventListener('click',function(){
			console.log(i)
		})
	},1000)
}
boxDoms.forEach(function(dom,i){
	setTimeout(()=>{
		console.log(i)
		dom.addEventListener('click',()=>{
			console.log(i)
		})
	},1000)
})

闭包的具体使用场景:
1.函数柯里化
2.单模式处理、工厂模式处理
3.react hooks

函数柯里化的实现

function curry(fn){
	// 在没有默认值的时候,fn.length指的是形参的个数,如果参数有默认值,那么就取第一个具有默认值之前的参数个数
	const arity = fn.length
	return function curried(...args) {
		console.log(this)
		if(args.length >= arity) {
			return fn.apply(this, args)
		} else {
			return function (...moreArgs) {
				return curried.apply(this, args.concat(moreArgs))
			}
		}
	}
}

compose函数实现

function compose(...funcs){
	if(funcs.length === 0) {
		return (arg) => arg
	}
	
	if(funcs.length === 1) {
		return funcs[0]
	}
	
	return funcs.reduce(
		(a,b) => 
			(...arg) => {
				return a(b(...arg))
		}
	)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值