lesson20 JS作用域链 & JS引用类型

第 1 题

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

1. 立即执行函数是什么

立即执行函数就是

  1. 声明一个匿名函数
  2. 马上调用这个匿名函数


    lesson20 JS作用域链 & JS引用类型_第1张图片
    立即执行函数

    上面是一个典型的立即执行函数。

  • 首先声明一个匿名函数 function(){alert('我是匿名函数')}。
  • 然后在匿名函数后面接一对括号 (),调用这个匿名函数。
    那么为什么还要用另一对括号把匿名函数包起来呢?

其实是为了兼容 JS 的语法。

如果我们不加另一对括号,直接写成

function(){alert('我是匿名函数')}()

浏览器会报语法错误。想要通过浏览器的语法检查,必须加点小东西,比如下面几种

(function(){alert('我是匿名函数')} ()) // 用括号把整个表达式包起来
(function(){alert('我是匿名函数')}) () //用括号把函数包起来
!function(){alert('我是匿名函数')}() // 求反,我们不在意值是多少,只想通过语法检查。
+function(){alert('我是匿名函数')}()
-function(){alert('我是匿名函数')}()
~function(){alert('我是匿名函数')}()
void function(){alert('我是匿名函数')}()
new function(){alert('我是匿名函数')}()

2. 立即执行函数有什么用?

只有一个作用:创建一个独立的作用域。

这个作用域里面的变量,外面访问不到(即避免「变量污染」)。

以一个著名的面试题为例:

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  liList[i].onclick = function(){
    alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5
  }
}

为什么 alert 的总是 6 呢,因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i,如下:


lesson20 JS作用域链 & JS引用类型_第2张图片
img

那么怎么解决这个问题呢?用立即执行函数给每个 li 创造一个独立作用域即可(当然还有其他办法):

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  !function(ii){
    liList[ii].onclick = function(){
      alert(ii) // 0、1、2、3、4、5
    }
  }(i)
}

在立即执行函数执行的时候,i 的值被赋值给 ii,此后 ii 的值一直不变。

i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的 ii 「分别」是 0、1、2、3、4、5。

第 2 题

求n!,用递归来实现。

function Recursion(n){
   if (n>1) return n*Recursion(n-1);
   return 1;
}
var a=Recursion(6);
console.log(a);

第 3 题

以下代码输出什么?

    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, '男');
    getInfo('小谷', 3);
    getInfo('男');
//getInfo('饥人谷', 2, '男');
name:饥人谷
age:2
sex: sex
['饥人谷', 2, '男']
name valley
//getInfo('小谷', 3);
name:小谷
age:3
sex:undefined
['小谷', 3]
name valley
//getInfo('男');
name:男
age:undefined
sex:undefined
['男']
name valley

第 4 题

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

   function sumOfSquares(){
   }
   var result = sumOfSquares(2,3,4)
   var result2 = sumOfSquares(1,3)
   console.log(result)  //29
   console.log(result2)  //10
   function sumOfSquares(){
     var sum = 0;
     for (var i=0;i

第 5 题

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

    console.log(a);
    var a = 1;
    console.log(b);

undefined
error

var a声明提前
等价于

var a;
console.log(a);
a= 1;
console.log(b);

console.log(a)中的a声明未赋值,所以返回undefined
console,log(b)中的b在全局作用域中遍寻不到所需变量,所以抛出异常 ReferenceError

第 6 题

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

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

hello world
error

  • 函数声明提前,函数表达式无法声明提前,var sayAge
    等价于
    function sayName(name){
        console.log('hello ', name);
    }
    var sayAge;
    sayName('world');
    sayAge(10);
        sayAge= function(age){
        console.log(age);
    };
  • sayName('world')查看sayName前面有函数声明,所以为hello world
  • sayAge(10)查看sayName前面有var sayAge声明,说明sayAge()被分配给所在作用域,因此sayAge()不会返回ReferenceError。但是sayAge此时没有赋值,sayAge()由于对undefined值进行函数调用而导致非法操作,因此抛出异常TypeError

第 7 题

写一个函数squireArr,其参数是一个数组,作用是把数组中的每一项变为原值的平方

var arr = [3, 4, 6]
function squireArr( arr ){
  //补全
}
squireArr(arr)
console.log(arr)  // [9, 16, 36]
var arr = [3, 4, 6];
function squireArr( arr ){ 
  for(var i=0;i

第 8 题

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

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

10
函数声明提前
等价于

var x=10
function foo() {
  console.log(x)
}
function bar(){
  var x = 30
  foo()
}
bar()
  • bar()在该作用域找bar声明,找到了,进去,
  • 进入到bar作用域里,foo()在该作用域找不到函数声明,于是往上一层找,找到了,在foo()作用域中,console.log(x)的x在foo()下的作用域里找不到变量x,于是继续往上一层作用域找,找到了,所以x的值等于10,console.log(x)本身值等于undefined

第 9 题

写一个函数squireArr,其参数是一个数组,返回一个新的数组,新数组中的每一项是原数组对应值的平方,原数组不变

var arr = [3, 4, 6]
function squireArr( arr ){
  //补全
}
var arr2 = squireArr(arr)
console.log(arr)  // [3, 4, 6]
console.log(arr2)  // [9, 16, 36]
var arr = [3, 4, 6];
function squireArr( arr ){
  var arr2 = [];
  for (var i = 0;i < arr.length;i++)
    arr2[i] = arr[i] * arr[i];
  return arr2;
}
var arr2 = squireArr(arr);
console.log(arr);  // [3, 4, 6]
console.log(arr2);  // [9, 16, 36]

第 10 题

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

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

30
等价于

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

第 11 题

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

var a = 1
function fn1(){
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //输出多少

2
fn()在该作用域找函数声明,没有,找函数表达式,找到被赋值fn1(),fn1()在该作用域找函数声明,找到,在fn1()作用域下,return fn3,
在fn1()函数作用域找fn3函数声明,找到,在fn3()作用域下,遇到fn2(),在该作用域找fn2(),没找到,从上一级找,找到,在fn2()作用域下,遇到console.log(a),a在该作用域找不到变量声明,往上一级找,找到,var a=2 ,所以最后结果为2

第 13 题

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

var a = 1
function fn1(){

  function fn3(){
    function fn2(){
      console.log(a)
    }
    fn2()
    var a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //输出多少

undefined
注意 var a=4 变量声明提前 var a 提前 在fn2()前面,所以为undefined

第 14 题

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

var obj1 = {a:1, b:2};
var obj2 = {a:1, b:2};
console.log(obj1 == obj2);
console.log(obj1 = obj2);
console.log(obj1 == obj2);

false
{a:1, b:2}
true

  1. obj1和obj2的指针地址不同,打印false;
  2. 将obj2指针地址赋值给obj1,并打印对象;
  3. 由于obj1和obj2的指针地址相同,打印true。

第 15 题

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

var a = 1
var c = { name: 'jirengu', age: 2 }

function f1(n){
  ++n
}
function f2(obj){
  ++obj.age
}

f1(a) 
f2(c) 
f1(c.age) 
console.log(a) 
console.log(c)     

1;object{name: "jirengu",age: 3}
原因:a的值传递给n,n的内存和a的内存不是一个地址,所以n自增后不改变a,同样执行f1(c.age)时也不改变c.age。而对象作为参数时,传递的是地址值,f(2)执行的语句改变了该地址下的内容,使age自增1。所以f2(c)执行结束后改变了c.age由2变为了3。

你可能感兴趣的:(lesson20 JS作用域链 & JS引用类型)