资讯专栏INFORMATION COLUMN

译: 函数提升与提升面试的相关问题

wuaiqiu / 2847人阅读

摘要:函数提升在里有两种方式创建函数,通过函数声明和函数表达式。函数声明用指定的参数来定义函数。提示不要在中进行函数声明。问题输出两个都是用函数声明的函数,将被提升到的局部作用域顶端。函数本身将作为函数声明在全局范围内提升。

作者关于提升的话题,总共有两篇。(后来又有一个讨论篇),再次搬过来。水平有限,如果翻译的不准确请包涵,并去看原文。下面开始:

这是我之前的关于“提升”的文章,标题为《用let,const来指导你的JavaScript变量提升》(中/英)的第二部分。因此在深入研究之前,请确保你已经阅读过第一部分。

之前我只讨论过变量提升,是因为函数提升在JavaScript中与变量提升不同。它有自己的独特方式。我将在这对函数进行扩展,以及在面试中面试官总会提问的“提升”(变量和函数)的棘手问题。

希望通过完成这两部分,你能真的在你的JavaScript检查清单中划掉它。

让我们开始吧。

函数提升

在JavaScript里有两种方式创建函数,通过函数声明函数表达式。那就看看这两种方式是如何影响“提升”的。

函数声明

用指定的参数来定义函数。
语法:

function name(param1, param2, ...) {
  [statements]
}

在JavaScript里,函数声明提升函数定义。
因此,这些函数在被声明之前便可以使用
例子:

hoisted() // output: "Hoisted"

function hoisted() {
  console.log("Hoisted")
}

下面的例子,展示JavaScript编译器如何看待上面的代码:

// Hoisted code
function hoisted() {
  console.log("Hoisted")
}// Rest of the code
hoisted() // output: "Hoisted" 

如果你在全局作用域或者功能区作用域(在JavaScript里基本叫局部作用域)进行函数声明,这个行为是真实的。
这非常有用,因为你能在代码开头使用高级逻辑,使其可读与可理解。
提示:不要在“if/else”中进行函数声明。

函数表达式

“function”关键字也能在一个表达式中定义一个函数。
语法:

const myFunction = function [name](param1, param2, ...) {
  [statements]
}

函数名是可选的,因此可以是匿名函数。我们可以用箭头函数,如下所示:

const myFunction = (param1, param2, ...) => {
  [statements]
}

在JavaScript里,函数表达式没有被提升。
因此,你不能使用函数表达式在定义它们之前。
例子:

notHoisted() // TypeError: notHoisted is not a function

const notHoisted = function() {
  console.log("foo")
}

关于函数创建中“提升”的部分,以上所有这些都要记住。
现在来看看面试问题!

“提升”面试问题

“提升”的不稳定行为一直是面试过程中的热门问题。用之前和现在这两篇文章的知识,可以解答这个话题中的任何问题。

问题 1 :
var a = 1;

function b() {
  a = 10;
  return;

  function a() {}
}

b();
console.log(a);

输出: 1, 这是为什么?! ?
这是因为“function a() {}”声明被创建在“函数/局部”作用域中。这个新“a”函数在声明和定义的时候被提升到它的封闭函数b的顶端。下面演示发生了什么:

var a = 1;

function b() {
  // Hoisted
  function a() {}

  a = 10;
  return;
}

b();

console.log(a)

因此,赋值a=10;不能改变全局作用域a的值,任然是1,而是将局部a从函数改为整数10。
如果声明函数a不在这,那将输出10。

问题 2:
function foo(){
    function bar() {
        return 3;
    }
    return bar();
    function bar() {
        return 8;
    }
}
alert(foo());

输出:8
两个bar都是用函数声明的函数,将被提升到foo的局部作用域顶端。

然而,返回8的bar()将晚于第一个返回3的提升。因此,这个返回8的函数将被执行。

之后的场景:

function foo(){
    //Hoisted before
    function bar() {
        return 3;
    }
    // Hoisted after
    function bar() {
        return 8;
    }    return bar();
    
}
alert(foo());
问题 3:
function parent() {
    var hoisted = "I"m a variable";
    function hoisted() {
        return "I"m a function";
    }
    return hoisted(); 
}
console.log(parent());

输出:“类型错误:hoisted不是一个函数”
这个很诡异。函数 vs 变量!我们来分析下。
我们都知道,说到变量提升,只有声明被提升(值是“undefined”),而不是定义!

如果是函数声明的方式声明函数,声明和定义一并提升!
现在,如果是多个相同标识符声明(变量和函数在同一作用域里)这样的情况,这个变量的提升会直接忽略

解释器只声明函数并提升它。

最终,这个声明变量变量赋值被执行(没有被提升),且值分配给了被提升的函数,值“I‘m a variable” ,

这是一个简单的字符串而不是函数。于是报错了!

这后面的场景重现了问题:

function parent() {    // Function declaration hoisted with the definition
    function hoisted() {
        return "I"m a function";
    }    // Declaration ignored, assignment of a string
    hoisted = "I"m a variable";
    return hoisted(); 
}
console.log(parent()); 
问题 4:
alert(foo());
function foo() {
  var bar = function() {
    return 3;
  };
  return bar();
  var bar = function() {
    return 8;
  };
}

输出:3
这个简单。函数foo()本身将作为函数声明在全局范围内提升。在函数foo里面,是两个明确的函数表达式的例子。

编译器不会提前读取第二个函数bar() (没有提升)。第一个将被执行并返回。

问题 5:
var myVar = "foo";(function() {
  console.log("Original value was: " + myVar);
  var myVar = "bar";
  console.log("New value is: " + myVar);
})();

输出: “Original value was: undefined”, “New value is: bar”

在这个例子中,全局变量myVar的值‘foo’出现在picture之外。这是因为变量myVar在函数作用域内部声明和定义,而且被提升到了IIFE的顶端,值是‘undefined’,他被首先记录。然后将值“bar”分配并记录下来。

这是我这里对JavaScript提升的总结。?

希望这两篇(中/英)文章能对你有帮助。

如果你想学习箭头函数和ES6其他相关的功能,请查看下面的文章。

Peace ✌️

至此,关于提升的两篇文章结束了。后来一个读者和作者就问题1问题3产生了分歧。到时候也搬过来吧。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90452.html

相关文章

  • 】Javascript 面试完美指南(开发者视角)

    摘要:闭包是返回另一个函数并携带数据的函数。当程序的上下文和作用域发生变化时,也会发生相应的变化。之所以是类型,是因为类的构造函数它是类型的。如下这里的是一个回调函数,当成功响应请求时将执行该回调函数。 译者:前端小智 原文:medium.com/dev-bits/a-… 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 为了说明 JS 面试的复杂性,首先,请尝试给出以下结果...

    Joonas 评论0 收藏0
  • JavasScript重难点知识

    摘要:忍者级别的函数操作对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是创建一个供以后使用的函数。 JS 中的递归 递归, 递归基础, 斐波那契数列, 使用递归方式深拷贝, 自定义事件添加 这一次,彻底弄懂 JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果...

    forsigner 评论0 收藏0
  • 你应该要知道作用域和闭包

    摘要:写在前面对于一个前端开发者,应该没有不知道作用域的。欺骗词法作用域有两个机制可以欺骗词法作用域和。关于你不知道的的第一部分作用域和闭包已经结束了,但是,更新不会就此止住未完待续 这是《你不知道的JavaScript》的第一部分。 本系列持续更新中,Github 地址请查阅这里。 写在前面 对于一个前端开发者,应该没有不知道作用域的。它是一个既简单有复杂的概念,简单到每行代码都有它的影子...

    JouyPub 评论0 收藏0
  • 前端优化 - 收藏集 - 掘金

    摘要:虽然有着各种各样的不同,但是相同的是,他们前端优化不完全指南前端掘金篇幅可能有点长,我想先聊一聊阅读的方式,我希望你阅读的时候,能够把我当作你的竞争对手,你的梦想是超越我。 如何提升页面渲染效率 - 前端 - 掘金Web页面的性能 我们每天都会浏览很多的Web页面,使用很多基于Web的应用。这些站点看起来既不一样,用途也都各有不同,有在线视频,Social Media,新闻,邮件客户端...

    VincentFF 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<