关于js声明提升和作用域链

一句话:函数的作用域,与声明时有关,与调用时无关。

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

函数声明,使用function关键字可以声明一个函数,声明不必放到调用的前面。
函数表达式,声明必须放到调用的前面

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

在一个作用域下,var 声明的变量和function 声明的函数会前置。
变量的声明前置,var声明的变量按顺序提升到js最前,赋值为undefined
函数的声明前置,function声明的函数和变量一样前置,且都按照顺序提升。

3. arguments 是什么

argument是类数组对象,每个函数中都存在argument对象,argument并不是一个真正的数组,所以不具备除length属性之外的属性,这个对象维护着所有传入该函数的参数列表,通过arguments[1、2、3]等...可以获取到相应的传入参数。

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

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

function fn(a, b, c){
  if( a !== void 0){  //若a不为undefined,就输出a
    console.log(a);
  }
  if( b !== void 0){  //若b不为undefined,就输出b
    console.log(b);
  }
  if( c !== void 0){  //若c不为undefined,就输出c
    console.log(c);
  }
}

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

立即执行函数表达式,首先它是一个表达式,而不是一个声明函数。其次,因为是表达式,所以可以用(), +, !, -等运算符来触发。
例如:(function(){…})()
函数会立即执行,并且可以隔离作用域,立即执行函数内的任何赋值都不会影响这个函数外的变量和函数。

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

function factorial(n){
  if( n === 1 ){
    return 1;
  }
  return n * factorial(n-1);
}

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('Llane', 2, '男'); 
//name: Llane
//age: 2
//sex: 男
//["Llane",2,"男"]
//name valley

getInfo('小L', 3); 
//name: 小L
//age: 3
//sex: undefined
//["小L",3]
//name valley

getInfo('男'); 
//name: 男
//age: undefined
//sex: undefined
//["男"]
//name valley

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

function sumOfSquares(){
  var sum = 0
  for(var 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);
var a = 1;
console.log(b);

变量提升后代码为:

var a;
console.log(a); //a此时还没赋值,所以输出 undefined
a = 1;
console.log(b); //变量b没有声明过,所以会报错 Uncaught ReferenceError: b is not defined

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

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

变量提升后代码为:

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

sayName('world');
sayAge(10); //函数表达式,声明必须在调用前

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()
}

//声明提升后为
var x
function foo() {
  console.log(x)
}
function bar(){
  var x
  x = 30
  foo()
}
x = 10
bar() 

作用链伪代码:

globalContext = {
  AO: {
    x:10
    foo:function
    bar:function
  }
  scope:null
}
foo.[[scope]] = globalContext.AO
bar.[[scope]] = globalContext.AO

barContext = {
  AO:{
    x:30
  }
  scope:globalContext.AO
}

fooContext = {
  AO:{}
  scope:globalContext.AO
}

bar()开始执行,声明局部变量x=30,
foo()开始执行,执行console.log(x),发现fooContext中没有x,
到scope:globalContext.AO中找x
发现:globalContext.AO中x为10
所以输出10

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

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

//声明提升后为
var x;
function bar(){
  var x
  function foo(){
    console.log(x) 
  }
  x = 30;
  foo();
}
x = 10
bar() 

作用域伪代码为:

globalContext = {
  AO:{
    x:10
    bar:function
  }
  scope: null
}
bar.[[scope]] = globalContext.AO

barContext = {
  AO:{
    x: 30
    foo:function
  }
  scope:globalContext.AO
}
foo.[[scope]] = barContext.AO

fooContext = {
  AO:{}
  scope:barContext.AO
}

bar()执行,声明局部变量x=30
foo()执行,发现fooContext.AO中没有x
根据scope:barContext.AO去找
发现scope:barContext.AO中x为30
所以输出30

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

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

//声明提升后
var x;
function bar(){
  var x
  x = 30;
  (function (){
    console.log(x)
  })()
}
x = 10;
bar() 

作用域链伪代码:

globalContext = {
  AO:{
    x:10
    bar:function
  }
  scope:null
}
bar.[[scope]] = globalContext.AO

barContext = {
  AO:{
    x:30
    (anomyous):function
  }
  scope:globalContext.AO
}
(anomyous).[[scope]] = barContext.AO

(anomyous)Context = {
  AO:{}
  scope:barContext.AO
}

bar()执行声明局部变量x = 30
执行立即执行函数,立即执行函数中没有x
所以去他的scope:barContext中找,发现x=30
所以输出30

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)

//声明提升

var a

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

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

a = 1;
fn()
console.log(a)

作用域伪代码:

globalContext = {
  AO:{
    a:1 => 200
    fn:function
    fn3:function
  }
}
fn.[[scope]] = globalContext
fn3.[[scope]] = globalContext

fnContext = {
  AO:{
    a:undefined => 5 => 6 => 20
    fn2:function
  }
  scope:globalContext
}
fn2.[[scope]] = fnContext

fn3Context = {
  AO:{}
  scope:globalContext
}

fn()开始执行
//输出 undefined 5 1 6 20 200

ps:13题的伪代码中匿名函数的AO写法有待商榷。

补充:

1.如果是带参数的函数

例如:

function exampleFn(a){
  console.log(a);
}
example(10);

//相当于

function exampleFn(a){
   var a = 10;
  console.log(a);
}

它的伪代码作用域为:

exampleFnConext = {
  AO:{
    a:10
  }
  scope:globalContext.AO
}

你可能感兴趣的:(关于js声明提升和作用域链)