js 回调函数 this 丢失 闭包解决问题

之前也遇到过构造函数访问this 找不到的问题,当时的解决办法就是把this改成生成的实例化对象,比如var fo=Func(),this直接就用fo代替,这样无怎么调用都不会出现this找不到(因为没有this了!!!~_~) 

回调函数 this丢失

  举个例子:

//创建一个构造函数
var Func=function(){
	this.age=12;
	this.say=function(){
		console.log("age="+this.age);
	};
}
//生成实例化对象fo
var fo=new Func();
//调用fo的方法say
fo.say()//age=12
//创建一个回调方法,调取其他方法
function callback(cal){
	if(typeof(cal)=="function"){
		cal&cal();
    }
}
//回调函数调用fo.say(),此时的this是window ,this.age就是undefined
callback(fo.say)//age=undefined

 回调函数调用fo.say(),此时的this是window ,this.age就是undefined.

产生this丢失的现象,有两个方向可以思考1:实例的方法,this.say的含义作用是不是这个问题,2;回调函数回调的方法是只调用方法本身吗!,打断点去看下回调地方具体的参数传递,

首先我们看到fo.say这个方法显示anonymous()(匿名函数),再看cal方法显示的function(){console.log''age="+this.age)}这也显然是个匿名函数,

知识点一:

  一般一个函数的定义有三种模式

函数关键字(function)语句:

1:function fn(x){ console.log("age"); }

2:函数字面量(Function Literals):

var fn = function(x){ console.log("age");}

3:Function()构造函数:

var fn= new Function();

第1种就是最常用的方法,后两种都是把一个函数复制给变量fn,而这个函数是没有名字的,即匿名函数。

(当然字面量函数也可以是有名函数var fn=function fnx(){};此时的fnx是方法名,这个方法名是没办法外部调用的,但可以内部调用)

在构造函数中Func中this.say=function(){}是函数字面量的方法,且this.say指代的函数是匿名函数,当我们调用fo.say()这个方法时,内部的this指的就是fo,但是当我们回调时,this就不是fo而是window,

到当回调的时候,callback(cal)中的cal的指代是function(){console.log("age="+this.age);}匿名实体方法(这里其实是内存中的地址),而不是之前的fo.say,的方法体,这个有点绕,多体会两遍,cal是参数,fo.say传入后就不再是fo.say了,他传入的是function(){console.log("age="+this.age);}的地址,和fo.say()直接调用的区别在执行环境不同,,换句话就是我把方法的地址告诉你,你调用你就是他的执行环境this指的就是你的执行环境(或window 是this)

复习知识点2:

//基本类型
var a=4;
function funcA(a){
	++a;
	console.log(a)
}
funcA(a);//5
console.log(a);//4
//引用类型


var obj= new Object();
    obj.age=2;
function funcB(b){
	b.age++;
	console.log(b.age)
}
funcB(obj);//3
console.log(obj.age);//3

javascript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制到函数内部的参数,就和把值从一个变量复制到另一个变量一样.在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(命名参数或arguments对象的一个元素);在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部.(基本类型:string,number,boolean,null,undefined。引用类型:Function,Array,Object)这里的引用类型是Function,比Object多了层this执行环境的因素。

解决方法:

常规方法:

使用call,apply保存this

//创建一个构造函数
var Func=function(){
	this.age=12;
	this.say=function(){
		console.log("age="+this.age);
	};
}
//生成实例化对象fo
var fo=new Func();
//调用fo的方法say
fo.say()//age=12
//创建一个回调方法,调取其他方法
function callback(cal,obj){
	if(typeof(cal)=="function"){
		cal&cal.apply(fo);
    }
}
//回调函数调用fo.say(),此时的this是window ,this.age就是undefined
callback(fo.say,fo)//age=12

还有一种非 常规,但是看上去虽然差不多的方式但原理却很不一样:

//创建一个构造函数
var Func=function(){
	this.age=12;
	this.say=function(){
		console.log("age="+this.age);
	};
}
//生成实例化对象fo
var fo=new Func();
//调用fo的方法say
//fo.say()//age=12
//创建一个回调方法,调取其他方法
function callback(obj,cal){
	if(typeof(obj[cal])=="function"){
		obj[cal]&obj[cal]();
    }
}
//回调函数调用fo.say(),此时的this是window ,this.age就是undefined
callback(fo,"say")//age=12

 

补充说明下:

回调方法除了上面的还可以是setTimeout(fo.say,500);用立即执行函数解决也可以用bind(this)


//创建一个构造函数
var Func=function(){
	this.age=12;
	this.say=function(){
		console.log("age="+this.age);
	};
}
//生成实例化对象fo
var fo=new Func();
//调用fo的方法say
//fo.say()//age=12
//创建一个回调方法,调取其他方法
function callback(cal){
	if(typeof(cal)=="function"){
		cal&cal();
    }
}
//回调函数调用fo.say(),此时的this是window ,this.age就是undefined
//callback(fo.say)//age=undefined

setTimeout(fo.say,500);//age=undefined
用,bind(this)传进去
setTimeout(function(){
		fo.say();//age=12
	}.bind(fo),500);
//立即执行函数构建闭包保存主体
(function (obj){
	setTimeout(function(){
		obj.say();//age=12
	},500);
})(fo)

或者ajax的异步回调。在都是的时候这个原理。有问题多留言多交流

//立即执行函数构建闭包保存主体
(function (obj){
    setTimeout(function(){
        obj.say();//age=12
    },500);
})(fo)

//立即执行函数构建闭包保存主体
(function (obj){
    setTimeout(function(){
        obj.say();//age=12
    },500);
})(fo)

//立即执行函数构建闭包保存主体
(function (obj){
    setTimeout(function(){
        obj.say();//age=12
    },500);
})(fo)

重要的方法写三遍

 

 

 

 

 

 

 

 

回调函数并非直接构成闭包,但在某些特定场景下它们可以涉及到闭包的概念。 ### 回调函数概述 回调函数是一种常见的编程模式,在这种模式中,一个函数作为参数传递给另一个函数,并在其内部由该外部函数调用。回调函数通常用于处理异步操作的结果或事件处理器。例如,在JavaScript中,`setTimeout`、`setInterval` 和 `fetch` 等 API 都允许传入回调函数来处理操作完成后的动作。 ### 闭包概述 闭包是指有权访问另一个作用域内变量的函数。简单来说,闭包就是那些在局部作用域创建并返回的函数,它能够记住并访问自身创建时的作用域内的变量,即使外部作用域已经不存在了。闭包使得函数能够“记忆”和保存在执行过程中遇到的数据状态,这对于管理数据流、封装私有数据等非常有用。 ### 回调函数如何涉及闭包 虽然回调函数本身并不是闭包,但当回调函数接收包含外部作用域中变量的对象时,可能会间接形成闭包的效果: 1. **匿名回调**:在一些场合下,如果回调函数通过某个对象(如Promise实例)来触发,则这个对象包含了其创建时作用域中的变量信息。因此,当回调函数运行时,它可以访问到这些变量,类似于形成了闭包。例如,在使用 Promise 的链式调用中,每一个步骤都可能依赖于上一步的状态,此时回调实际上是在闭包环境中运行的。 2. **自定义事件监听器**:当你创建一个自定义事件,并为其添加监听器时,监听器可以是一个函数,这个函数可能需要访问与事件相关的上下文。在这种情况下,监听器函数可以在闭包环境中运行,因为它有机会访问创建它的上下文中定义的变量。 ### 相关问题: 1. **闭包和作用域的区别是什么?** 闭包和作用域都是理解现代编程语言中变量生命周期的重要概念。作用域指定了变量可见性的范围,而闭包则强调了在特定环境下的功能复用能力。简而言之,作用域决定了何时能访问到哪些变量,而闭包则是利用这样的作用域限制来提供持久化的计算环境。 2. **如何在JavaScript中安全地使用闭包?** 在JavaScript中,合理使用闭包可以帮助避免全局污染和内存泄漏等问题。为了安全地使用闭包,应遵循几个最佳实践: - 尽量将闭包限定在最小范围内,只包含必要的变量。 - 使用let而非var声明变量,因为let不会泄漏到全局作用域。 - 将变量声明放在闭包外部,以充分利用其生命周期。 3. **闭包在实际应用中有哪些常见用途?** 闭包的应用广泛,包括但不限于: - **私有成员**:在类或模块中保护数据,只对外暴露受控的接口。 - **事件监听器**:在DOM操作或其他异步事件处理中,持续跟踪和响应状态变化。 - **延时执行**:利用闭包控制时间间隔的执行逻辑,如计时器管理和延迟函数调用。 - **模块化编码**:封装函数,使其能在不同上下文中保持独立性和私密性。 总之,尽管回调函数本身并不构成闭包,但它们可以通过引用外部作用域的变量来形成闭包的效果,这在实际编程中是非常有用的。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值