函数与作用域

作者: 南山码农 | 来源:发表于2017-07-31 17:17 被阅读0次

1.函数声明和函数表达式有什么区别

函数声明
使用function关键字可以声明一个函数

  //函数声明
  function sayHello(){
    console.log('hello')
  }

  //函数调用
  sayHello()

区别:声明不必放到调用的前面

函数表达式

  var sayHello = function(){
    console.log('hello');
  }

  sayHello()

区别:声明必须放到调用的前面

2.什么是变量的声明前置?什么是函数的声明前置

所谓的变量声明前置就是在一个作用域块中,所有的变量都被放在块的开始处声明。
和变量声明前置一样,执行代码之前会先读取函数声明,只要函数在代码中进行了声明,无论它在哪个位置上进行声明,js引擎都会将它的声明放在范围作用域的顶部。

3.arguments 是什么?

描述

arguments对象是所有函数中可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数的条目,第一个条目的索引从0开始。例如,如果一个函数传递了三个参数,你可以参考它们如下:

arguments[0];
arguments[1];
arguments[2];

参数也可以被设置:

arguments[1] = 'new value';

arguments对象不是一个 Array。它类似于数组,但除了 长度之外没有任何数组属性。例如,它没有 pop方法。但是它可以被转换为一个真正的数组:

let args = Array.prototype.slice.call(arguments); 
let args = [].slice.call(arguments);

你还可以使用 Array.from()方法或 sprea 运算符将 arguments 转换为真正的数组:

let args = Array.from(arguments);let args = [...arguments];

对参数使用slice会阻止某些JavaScript引擎中的优化 (比如 V8 引擎 )。
如果你关心它们,尝试通过遍历arguments对象来构造一个新的数组。
另一种方法是使用 被忽视的/鄙视/轻视,/看不起 Array构造函数作为一个函数:

let args = (arguments.length === 1 ? [arguments[0]] :
 Array.apply(null, arguments));

如果 Array generics可用的话,下面的代码可以作为替代:

var args = Array.slice(arguments);

arguments 对象仅在函数内部有效,在函数外部调用 arguments 对象会出现一个错误。
arguments的typeof返回'object'。
console.log(typeof arguments); // 'object'

可以使用索引来确定各个arguments的类型。
console.log(typeof arguments[0]); //
这将返回单个参数的typeof。
如果你调用一个函数,当这个函数的参数数量比它显式声明的参数数量更多的时候,你就可以使用 arguments
对象。这个技术对于参数数量是一个可变量的函数来说比较有用。 你可以用 arguments.length来得到参数的数量,然后可以用 arguments
object 来对每个参数进行处理。 (想要得到函数签名的参数数量, 请使用 Function.length

属性

arguments.callee指向当前执行的函数。
arguments.caller指向调用当前函数的函数。
arguments.length指向传递给当前函数的参数数量。

4.函数的"重载"怎样实现

在JavaScript中没有重载! 同名函数会覆盖。 但可以在函数体针对不同的参数调用执行相应的逻辑,实现重载。

  function printPeopleInfo(name, age, sex){
    if(name){
      console.log(name);
    }

    if(age){
      console.log(age);
    }

    if(sex){
      console.log(sex);
    }
  }


  printPeopleInfo('Byron', 26);
  printPeopleInfo('Byron', 26, 'male');

5.立即执行函数表达式是什么?有什么作用?

立即执行函数就是当我们在定义了函数之后需要立即执行的函数,这样解释可能有点晦涩..所以,来看一下实例.

function(){ /* code */ }();// SyntaxError: Unexpected token 

一般情况下,也许有人认为立即执行函数可能会是这个样子的,但其实为了避免解析上的歧义,JavaScript引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。

所以一般情况下,在写,IIFE的时候要写成

(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称IIFE。

注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个IIFE,可能就会报错。

推而广之,任何让解释器以表达式来处理函数定义的方法,都能产生同样的效果,比如下面三种写法。

var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();

作用:
通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

6.求n!,用递归来实现

n!算法函数:

function factor(n) {
     if ( n>=0 ){
          if ( n === 0 || n  === 1 ){
            return n=1
          }
      return n*factor(n-1)
     }else {
      console.log("n为正整数")
      }
 }

1+2+...+n 的值

function sum(n){
  if(n === 1) {
    return 1
  }
  return n + sum(n-1)
}

sum(10)

7.以下代码输出什么?

function getInfo(name, age, sex){
    console.log('name:',name);
    console.log('age:', age);
    console.log('sex:', sex);
    console.log(arguments);
    arguments[0] = 'valley';
    console.log('name', name);
}

getInfo('饥人谷', 2, '男'); 
 输出结果:
name: 饥人谷
age: 2
sex: 男
["饥人谷", 2, "男"]
name valley

getInfo('小谷', 3);  
输出结果:
name: 小谷
age: 3
sex: undefined
["小谷", 3]
name valley

getInfo('男'); 
 输出结果2:
name: 男
age: undefined
sex: undefined
["男"]
name valley

8. 写一个函数,返回参数的平方和?

 function sumOfSquares(){
  var sum=0;
for(i=0;i<arguments.length;i++){
    sum+=arguments[i]*arguments[i];
    }
    return sum;
 }
 var result = sumOfSquares(2,3,4)
 var result2 = sumOfSquares(1,3)
 console.log(result) ;   /*29*/
 console.log(result2);    /*10*/

9. 如下代码的输出?为什么

console.log(a);/*a被声明但是未赋值,undefined*/
var a = 1;/*var a前置至作用域前端*/
console.log(b);/*b未被声明,所以会报错: b is not defined*/

10. 如下代码的输出?为什么

sayName('world');
sayAge(10);
function sayName(name){
    console.log('hello ', name);
}
var sayAge = function(age){
    console.log(age);
};

输出
hello world
Uncaught TypeError: sayAge is not a function(…)
函数声明不需要前置,函数表达式要前置

11. 如下代码输出什么? 写出作用域链查找过程伪代码

var x = 10
bar() 
function foo() {
  console.log(x)
}
function bar(){
  var x = 30
  foo()
}

输出10

globalContext = {
  AO: {
    x: 10
    foo: function
    bar: function
  },
  Scope: null
}
//声明 foo 时 得到下面
foo.[[scope]] = globalContext.AO
//声明 bar 时 得到下面
bar.[[scope]] = globalContext.AO

12. 如下代码输出什么? 写出作用域链查找过程伪代码

var x = 10;
bar() 
function bar(){
  var x = 30;
  function foo(){
    console.log(x) 
  }
  foo();
}   

输出10

globalContext = {
  AO: {
    x: 10
    bar: function
  },
  Scope: null
}
//声明 foo 时 得到下面
foo.[[scope]] = globalContext.AO
//声明 bar 时 得到下面
bar.[[scope]] = globalContext.AO

当调用foo() 时,先从 bar 执行上下文中的 AO里找,找不到再从 bar 的 [[scope]]里找,找到后即调用

barContext = {
  AO: {
    x: 30
  },
  Scope: bar.[[scope]] //globalContext.AO
}
bar.[[scope]] = globalContext.AO

当调用 foo() 时,进入 foo 的执行上下文

fooContext = {
  AO: {},
  Scope: foo.[[scope]] //globalContext.AO
}

13. 以下代码输出什么? 写出作用域链的查找过程伪代码

var x = 10;
bar() 
function bar(){
  var x = 30;
  (function (){
    console.log(x)
  })()
}

输出30

globalContext = {
  AO: {
    x: 10
    bar: function
  },
  Scope: null
}

//声明 bar 时 得到下面
bar.[[scope]] = globalContext.AO

当调用 bar() 时, 进入 bar 的执行上下文

  barContext = {
    AO: {
      x: 30,
      foo: function
    },
  Scope: bar.[[scope]] //globalContext.AO
  }
//在 bar 的执行上下文里声明 foo 时 得到下面
foo.[[scope]] = barContext.AO

当调用 foo() 时,先从 bar 执行上下文中的 AO里找,找到后即调用

fooContext = {
  AO: {},
  Scope: foo.[[scope]] // barContext.AO
}

当调用 foo() 时,进入 foo 的执行上下文

14. 以下代码输出什么? 写出作用域链查找过程伪代码

var a = 1;

function fn(){
  console.log(a)
  var a = 5
  console.log(a)
  a++
  var a
  fn3()
  fn2()
  console.log(a)

  function fn2(){
    console.log(a)
    a = 20
  }
}

function fn3(){
  console.log(a)
  a = 200
}

fn()
console.log(a)

输出结果
undefined
5
1
6
20
200

 var a = 1;   //全局上下文中有var a和 function fn(),function fn3(),声明前置

  function fn(){  //函数中有var a 和function fn2()
    console.log(a)//声明前置,但没有赋值,输出undefined
    var a = 5;
    console.log(a)//var a=5 故输出结果为5
    a++//自增,即fnContext里的a值变为6
    var a//同一个函数里前面已经声明,故没有变化
    fn3()//执行函数fn3()转function fn3(){ } → 执行完之后跳回fn(){ }
    fn2()//执行函数fn2(),转function fn2(){ } → 执行完之后跳回  fn(){ }
    console.log(a)//此时a的值为20,故输出20

   function fn2(){
    console.log(a)//fn2Context里没有a,故查找fnContext.AO,此时fn里面a为6,故输出6
    a = 20//fn2Context里没有a,故查找fnContext.AO,把20赋值给fnContext.AO里的a
  }
}

function fn3(){
  console.log(a)//fn3Context没有a,故向上查找global.AO,输出结果为1,
  a = 200///fn3Context没有a,故向上查找global.AO,把200赋值给a,即global.AO里的a变为200.
}

fn()//函数调用
console.log(a)//此时globalontext里的a的值诶200,故输出200

相关文章

  • 执行上下文、作用域链、this、闭包

    javascript 执行的是词法作用域,即静态作用域。与之相对的是动态作用域。 静态作用域:函数的作用域在函数定...

  • 作用域和作用域链

    作用域和作用域链 作用域 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在Java...

  • 作用域

    何为作用域 任何编程语言都有作用域的概念,简单来说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可...

  • JavaScript中的作用域和作用域链(一)

    作用域 1.作用域的概念: 变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。 2.全局作用域与...

  • JavaScript静态作用域解析

    静态作用域与动态作用域 因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。而与...

  • 词法作用域 - 2023-02-09

    静态作用域与动态作用域 因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。而与...

  • JS基础知识(2) -- 作用域和作用域链

    作用域 作用域就是变量与函数的可访问范围 全局作用域 函数作用域 ES6块级作用域 ES5没有块级作用域,ES6有...

  • 作用域链

    一、作用域 定义:作用域就是变量与函数的可访问范围。作用域控制着变量与函数的可见性和生命周期。 1、全局作用域(G...

  • JavaScript作用域学习笔记

    @(JS技巧)[JavaScript, 作用域] JavaScript作用域学习笔记 概念: 作用域就是变量与函数...

  • 我的JS笔记 -- 作用域

    作用域和作用域链概念 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaSc...

网友评论

    本文标题:函数与作用域

    本文链接:https://www.haomeiwen.com/subject/ryzllxtx.html