JavaScript中闭包和this的指向问题 (思考题)

本文探讨了JavaScript中闭包和this的指向问题。在非严格模式下,通过两种情况分析了函数调用时this的指向。第一种情况中,由于没有形成闭包,this指向全局对象。第二种情况中,形成了闭包,this保持了调用时的对象引用。通过显性代码解释了执行过程,并展示了如何利用call方法改变this的指向。
摘要由CSDN通过智能技术生成

ES5/6 - 闭包和this的指向问题 (思考题)

⚠️非严格模式


——当作为对象的方法调用时,这个函数里的this绑定到这个对象上面,
当作为函数调用时,这个函数里的this绑定到全局对象上面

要求说出以下两段代码在控制台的最终输出结果:
(1)

 var name = 'The Window';
        var object = {
            name:'my object',
            getNameFunc:function(){
                console.log(this);
                return function(){
                    console.log(this);
                    return this.name;
                }
            }
        };
        console.log(object.getNameFunc()());

(2)

 var name = 'The Window';
        var object = {
            name: 'My Object',
            getNameFunc : function(){
                var that = this;
                return function(){
                    return that.name;
                }
            }
        }
        console.log(object.getNameFunc()());

《JavaScript高级程序设计》上讲到匿名函数时大概有这么一句话:”匿名函数的this通常指向全局window“,为什么这么说呢?比如题目中返回的那个匿名函数,你是无法使用object.func()这样的形式调用它的,因为它没有函数名。也就是说你只能通过某种途径在某个位置调用它,却无法令某一个对象调用它。因此它通常只有指向window。

第一种情况

 var name = 'The Window';
        var object = {
            name:'my object',
            getNameFunc:function(){
                console.log(this);
                return function(){
                    console.log(this);
                    return this.name;
                }
            }
        };
        console.log(object.getNameFunc()());

全局的变量和函数都会存储在window对象中,对于第一种情况,由于在getNameFunc函数中不存在变量的存储,因此未形成闭包。
getNameFunc函数在函数外被object调用,然而其返回的匿名函数在使用()调用时并没有明显的调用者(直接作为函数来调用),因此向上追溯到window作为调用对象,此时this指向window对象。控制台最终的输出结果为My Window

第二种情况

在JavaScript的CDN中有如下描述

闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用
域内的任何局部变量。在本例子中,myFunc 是执行 makeFunc 时创建的 displayName 函数实例的引用。displayName 的实例维持了一个对它的词法环境(变量 name 存在于其中)的引用。因此,当 myFunc 被调用时,变量 name 仍然可用,其值 Mozilla 就被传递到alert中。

可以看出,需要使用另外一个变量名来指向执行完函数的那段内存空间,这个内存空间保留了函数执行完毕后各个变量的值,让它的生命周期得以延续,这个大义凛然的变量名称为函数状态的引用,因此可以直接用变量名()来调用函数副本。

 var name = 'The Window';
        var object = {
            name: 'My Object',
            getNameFunc : function(){
                var that = this;
                return function(){
                    return that.name;
                }
            }
        }
        console.log(object.getNameFunc()());

console,log的执行过程中,首先使用object对象来调用getNameFunc,显然在var that = this这条语句中this指向调用者object,形成闭包,和匿名函数体一道保存下来,再次调用匿名函数,that在匿名函数的作用域中可见,因此可以打印出that.name为object作用域中的name属性,结果为My Object。

新解

可以把console.log执行过程化为下述显性代码,更容易理解

var a = object.getNameFunc();
var b = a();
console.log(b);

可以得出以下结论:

第一种情况中,由于getNameFunc只有返回匿名函数体,因此在此函数被调用结束后没有形成闭包,a直接成为匿名函数体的引用。直接以函数方式调用a,this的调用者为window,最终结果为My Window。

第二种情况中,由于getNameFunc中存在了名为that的变量,因此在此函数被调用结束后形成了闭包,在此闭包中,变量that存储了当时的this指向(它的调用者object对象),在执行结束后,返回了一个匿名函数以及其变量that,而变量a成为了此函数状态的引用。直接以函数方式调用a,that存活在引用a的作用域中,此时输出that.name相当于输出object.name,最终结果为My Object。

拓展延伸

那么如何在不修改代码的情况下输出My Object呢?这时要借助Function原型对象内建的call方法来修改函数的调用者,代码如下

console.log(object.getNameFunc().call(object));

给匿名函数指定调用者为object,this也就指向它,最终打印输出object作用域中的name,结果为My Object

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值