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

第 1 题

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

1. 立即执行函数是什么

立即执行函数就是

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


    立即执行函数

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

  • 首先声明一个匿名函数 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,如下:


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<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

第 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<arr.length;i++)
    arr[i] = arr[i] * arr[i];
}
squireArr(arr);
console.log(arr);  // [9, 16, 36]

第 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。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容