微信扫一扫看面试题

关注面试题库

【面试题】闭包是什么?this 到底指向谁?

一通百通,其实函数执行上下文、作用域链、闭包、this、箭头函数是相互关联的,他们的特性并不是孤立的,而是相通的。因为内部函数可以访问外层函数的变量,所以才有了闭包的现象。箭头函数内没有 this 和 arguments,所以内部调用了 this,会去上层函数作用域中查找 this 的指向。这些内容都是相通的。我们要融会贯通全面理解,而不是死记硬背哦。

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库

变量定义

  • var 声明变量

  • 变量未声明直接使用

这两种方式都可以声明变量,但是该变量会是全局变量,绑定在 window 对象上。同时 var 会有声明提升的作用。

console.log(a);  // undefined;并不会报错var a;
复制代码
  • let 声明变量

  • const 声明变量

let 和 const 是 ES6 新增的关键字,let 和 const 声明的变量不会有声明提前的特点。同时,let 和 const 声明之前的执行瞬间被称为”暂时性死区“,在此阶段引用任何后面才声明的变量都会抛出 ReferenceError。

let 和 const 在全局作用域中声明的变量不会成为 window 对象的属性。

const 声明变量时必须同时初始化变量。

最佳实践:

  • 不使用 var

  • const 优先,let 次之

函数定义

与变量不同,函数定义会提前;变量只是声明提前。

say()  // Hello worldfunctionsay(){
    console.log('Hello world')
  }
复制代码

但是使用变量声明函数,函数定义不会提前,同 var 变量声明一样,只是声明会提前。

console.log(say)  // undefinedvar say = function(){
    console.log('Hello world')
  }
复制代码
原因是在执行 js 代码之前会进行变量提升和函数定义。

函数执行

作用域 规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。

执行上下文:当我们执行一个方法时,JavaScript 会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中有这个方法的私有作用域、上层作用域的指向、方法的参数(arguments)、私有作用域中定义的变量以及 this 对象。这个执行环境被添加到一个栈中,这个栈就是执行栈。

如果在这个方法的代码中执行到了一行函数调用语句,那么 JavaScript 会生成这个函数的执行环境并将其添加到执行栈中,然后进入这个执行环境继续执行其中的代码。执行完毕后返回结果后,JavaScript 会退出执行环境并把这个执行环境从栈中销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。这个执行环境的栈就是执行栈。

执行上下文分全局上下文、函数上下文和块级上下文。

内层函数 fn 内有 arguments 对象、 this 对象、变量;以及函数 Fn 上层作用域的指向。fn 可以访问上层 Fn 的变量,但是不能访问 Fn 的 this 和 arguments 对象。

JavaScript 采用词法作用域,也就是静态作用域。

var value = 1;

functionfoo() {
    console.log(value);
}

functionbar() {
    var value = 2;
    foo();
}

bar();
复制代码

JavaScript采用的是词法作用域,函数的作用域基于函数创建的位置。

块级作用域(ES6新增)

for(let i=0;i<10;i++){
    // i 只能在这里使用
}
复制代码
functionf(flag){
    if(flag){
        var x = 10;
    }
    return x;
}
console.log(f(false));  //不会报错, undefined复制代码

闭包和 this

闭包(closure)指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。注意:只有引用了另一个函数作用域中的变量,才称为闭包,如果一个函数 A 内部返回了另一个函数 B,内部函数 B 并没有引用 A 的作用域中的变量是不会形成闭包的。博客

javascript - 理解闭包与内存泄漏 - 前海拾贝 - SegmentFault 思否 已验证。

闭包和执行上下文以及作用域是相通的,那就是内部函数可以访问外部函数的变量,但是不能访问外部函数的 this 和 arguments 对象。如下的例子:

window.identity = 'The Window';
let object = {
     identity: 'My Object',
     getIdentityFunc() {
         let code = 'closure'returnfunction() {
             console.log(code)
             returnthis.identity;
         };
     }
};
console.log(object.getIdentityFunc()()); // 'The Window'复制代码

第6行的匿名函数调用了外部函数 getIdentityFunc 函数作用域中的变量 code,我们称之为闭包,由于内部函数并不能访问外部函数的 this 和 arguments 对象,所以 this 对象并不是 object , 而是全局函数的 this window。

对于普通函数调用时的 this 指向:在全局函数中调用 this,this 等于 window;如果作为某个对象的方法调用,则 this 等于这个对象。先判断谁调用了这个函数,然后再判断 this 指向。

对于箭头函数 this 指向:因为箭头函数内部没有 this 和 arguments 对象,所以箭头函数的 this 指向该函数所在的作用域指向的对象。符合作用域查找逻辑,箭头函数本身没有 this 定义,会沿着作用域链查找。这时的 this 相当于一个变量,按照词法作用域逻辑查找。修改上面的例子:

window.identity = 'The Window';
let object = {
     identity: 'My Object',
     getIdentityFunc() {
         let code = 'closure'return() => {
             console.log(code)
             returnthis.identity;
         };
     }
};
console.log(object.getIdentityFunc()()); // 'My Object'复制代码

此时,内部函数修改为箭头函数,我们知道箭头函数内部没有自己的 this,所以会沿着作用域链查找上层函数 getIdentityFunc 的 this 指向。由于是 object 调用的 getIdentityFunc,所以 getIdentityFunc 的 this 指向 object 对象。再复杂一点:

window.identity = 'The Window';
let object = {
     identity: 'My Object',
     getIdentityFunc() {
         let code = 'closure'return() => {
             console.log(code)
             returnthis.identity;
         };
     }
};
const callObject = {identity: 'call object'}
console.log(object.getIdentityFunc.call(callObject)()); // 'call object'复制代码

这里调用的时候,使用了 call 改变了 getIdentityFunc 了 this 指向 callObject, 那么箭头函数在调用的时候,会沿着作用域链找到 getIdentityFunc 的 this 指向为 callObject 对象。

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库

posted @ 2023-03-15 18:06  web前端面试小助手  阅读(7)  评论(0编辑  收藏  举报  来源