JS函數(shù)以及作用域問(wèn)題

1.立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用

立即執(zhí)行函數(shù)表達(dá)式就是

  • 聲明一個(gè)匿名函數(shù)
  • 馬上執(zhí)行這個(gè)匿名函數(shù)

典型寫(xiě)法:( function(){alert('匿名函數(shù)')} )()

為什么要用一對(duì)括號(hào)把匿名函數(shù)包起來(lái)呢?
因?yàn)椴患永ㄌ?hào),寫(xiě)成function(){alert('匿名函數(shù)')} ()會(huì)報(bào)錯(cuò),原因是function關(guān)鍵字出現(xiàn)在行首,一律解釋成語(yǔ)句。因此,JavaScript引擎看到行首是function關(guān)鍵字之后,認(rèn)為這一段都是函數(shù)的定義,不應(yīng)該以圓括號(hào)結(jié)尾,所以就報(bào)錯(cuò)了。
解決方法就是不要讓function出現(xiàn)在行首,因此可以加點(diǎn)東西,有以下寫(xiě)法

(function(){alert('我是匿名函數(shù)')} ()) // 用括號(hào)把整個(gè)表達(dá)式包起來(lái)
(function(){alert('我是匿名函數(shù)')}) () //用括號(hào)把函數(shù)包起來(lái)
!function(){alert('我是匿名函數(shù)')}() // 求反,我們不在意值是多少,只想通過(guò)語(yǔ)法檢查。
+function(){alert('我是匿名函數(shù)')}()
-function(){alert('我是匿名函數(shù)')}()
~function(){alert('我是匿名函數(shù)')}()
void function(){alert('我是匿名函數(shù)')}()
new function(){alert('我是匿名函數(shù)')}()

那么立即執(zhí)行函數(shù)表達(dá)式有什么作用呢?
作用是創(chuàng)建一個(gè)獨(dú)立的作用域。這個(gè)作用域里面的變量,外面訪(fǎng)問(wèn)不到,這樣可以避免變量污染

2.求n!,用遞歸來(lái)實(shí)現(xiàn)

function recursion(n) {
  if (n === 1) {
    return 1;
  }
  return n * recursion(n-1);
}
var result = recursion(10);  
console.log(result);     //以n等于10為列子,結(jié)果:3628800

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('男');

調(diào)用getInfo('饑人谷', 2, '男')輸出:

name: 饑人谷
age: 2
sex: 男
["饑人谷",2,"男"]
name valley

調(diào)用getInfo('小谷', 3)輸出

name: 小谷
age: 3
sex: undefined
["小谷",3]
name valley

調(diào)用getInfo('男')輸出:

name: 男
age: undefined
sex: undefined
["男"]
name valley

注:
1.給函數(shù)傳入?yún)?shù)時(shí)是按順序傳入,沒(méi)有傳入?yún)?shù)則為undefined。
2.在函數(shù)內(nèi)部,可以使用arguments對(duì)象獲取到該函數(shù)的所有傳入?yún)?shù)

4.寫(xiě)一個(gè)函數(shù),返回參數(shù)的平方和?

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

注:arguments.length代表傳入實(shí)參的個(gè)數(shù)

5.如下代碼的輸出?為什么

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

輸出:

undefined
error(b is not defined)

注:JS中變量聲明會(huì)被提前,因此上述代碼就相當(dāng)于

var a
console.log(a);   //undefined
a = 1;
console.log(b);   //未定義,error

6.如下代碼的輸出?為什么

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

輸出:

hello world
error(sayAge is not a function)

注:JS中函數(shù)聲明和變量聲明一樣也會(huì)被提前,因此上述代碼就相當(dāng)于

function sayName(name){
    console.log('hello ', name);
}
var sayAge
sayName('world');          //輸出hello world
sayAge(10);                //報(bào)錯(cuò),sayAge不是函數(shù)類(lèi)型
sayAge = function(age){
    console.log(age);
};

7.如下代碼輸出什么? 為什么

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

輸出結(jié)果為:10
理由:
1.首先f(wàn)oo()和bar()函數(shù)的聲明會(huì)被提前,因此bar()可以正常執(zhí)行
2.接著bar()調(diào)用了foo(),而foo()輸出了個(gè)x
3.在foo()函數(shù)的內(nèi)部并未找到x變量,這時(shí)候JS會(huì)從聲明該函數(shù)所在的作用域去找, 以此往上(上述代碼中也就是最外層的x,即為10)

8.如下代碼輸出什么? 為什么

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

輸出結(jié)果為:30
理由同上,foo()聲明所在的作用域的x的值為30

9.如下代碼輸出什么? 為什么

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() //輸出多少

輸出結(jié)果為:2
理由同題7,fn2()聲明所在的作用域的a的值為2

10.如下代碼輸出什么? 為什么

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

輸出結(jié)果為:1
理由同題7,fn2()聲明所在的作用域的a的值為1

11.如下代碼輸出什么? 為什么

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() //輸出多少

輸出結(jié)果為:undefined
這題尤其注意,這題和上面的不同的點(diǎn)在于,由于變量提前導(dǎo)致fn2()的執(zhí)行是在a變量聲明之后,賦值之前(a此時(shí)只聲明了,還沒(méi)有賦值,因此為undefined),即函數(shù)fn3的實(shí)際執(zhí)行是以下的

  function fn3(){
    function fn2(){
      console.log(a)
    }
    var a
    fn2()
    a = 4
  }

12.如下代碼輸出什么?為什么

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

理由:
對(duì)象的相等,當(dāng)且僅當(dāng)他們的引用指向內(nèi)存中的相同區(qū)域時(shí)才相等,即他們?cè)跅?nèi)存中的引用地址相同
obj1和obj2分別是兩個(gè)不同的對(duì)象引用,里面存放的地址是不同的,即他們指向的區(qū)域是不同的,即時(shí)區(qū)域中的內(nèi)容是一樣的也不相等
而把通過(guò)obj1=obj2,將obj2中存放的地址賦給了obj1之后,obj1和obj2指向了同一塊區(qū)域,所以最后兩者相等了

13.如下代碼輸出什么? 為什么

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
{name: "jirengu", age: 3}

理由:
a作為實(shí)參傳給了f1的形參n,相當(dāng)于把a(bǔ)的值賦給了n,然后進(jìn)行++n,及n的值變成了2,而a的值仍然是1
c作為實(shí)參傳給了f2的形參obj,c是一個(gè)對(duì)象,里面存放的是指向它里面的內(nèi)容所在內(nèi)存中區(qū)域的地址,在f2中對(duì)這個(gè)地址指向的內(nèi)存區(qū)域里面的age進(jìn)行了前++,所以改變了這塊內(nèi)存區(qū)域中age的實(shí)際的值,即age最后得到3
至于將c.age作為實(shí)參傳給了f1的形參n,仍然是把a(bǔ)ge作為一個(gè)值賦給了n,并沒(méi)有改變age本身
注:值的傳遞并不能改變本身,引用的傳遞才可以

14.寫(xiě)一個(gè)深拷貝函數(shù)

淺拷貝:對(duì)于字符串類(lèi)型,淺拷貝是對(duì)值的復(fù)制,對(duì)于對(duì)象來(lái)說(shuō),淺拷貝是對(duì)對(duì)象地址的復(fù)制,并沒(méi) 有開(kāi)辟新的棧,也就是復(fù)制的結(jié)果是兩個(gè)對(duì)象指向同一個(gè)地址,修改其中一個(gè)對(duì)象的屬性,則另一個(gè)對(duì)象的屬性也會(huì)改變。對(duì)于淺拷貝,子對(duì)象以及子對(duì)象下面的對(duì)象都是共用的。
淺拷貝實(shí)現(xiàn)函數(shù):

var oldObject = {name:"小明",
                 age:25,
                 sex:"男",
                 city:"南京",
                 wife:{name:"小牛", sex:"女", age:24, city:"北京"},
                 friends:["小紅", "小白", "小黃"]
                 }
function shallowCopy(oldObj) {
  var newObj = {}
  for (var i in oldObj) {
    if (oldObj.hasOwnProperty(i)) {  //hasOwnProperty()函數(shù)用于指示一個(gè)對(duì)象自身(不包括原型鏈)是否具有指定名稱(chēng)的屬性。如果有,返回true,否則返回false
      newObj[i] = oldObj[i]
    }
  }
  return newObj
}
var newObject = shallowCopy(oldObject)
console.log(newObject)                                 //newObject里面的內(nèi)容和oldObject一樣
console.log(newObject.wife == oldObject.wife)          //true 
console.log(newObject.friends == oldObject.friends)     //true,兩個(gè)true說(shuō)明newObject的子對(duì)象并沒(méi)有開(kāi)辟新的內(nèi)存,和oldObject的子對(duì)象共用一塊堆內(nèi)存                      

深拷貝:深拷貝是開(kāi)辟新的棧,兩個(gè)對(duì)象對(duì)應(yīng)兩個(gè)不同的地址,即指向堆中不同的空間,修改一個(gè)對(duì)象的屬性,不會(huì)改變另一個(gè)對(duì)象的屬性。深拷貝會(huì)遞歸復(fù)制子對(duì)象及子對(duì)象下面的對(duì)象,并且新對(duì)象和舊對(duì)象不是共用一塊堆區(qū)域。
深拷貝實(shí)現(xiàn)函數(shù):

var oldObject = {name:"小明",
                 age:25,
                 sex:"男",
                 city:"南京",
                 wife:{name:"小牛", sex:"女", age:24, city:"北京"},
                 friends:["小紅", "小白", "小黃"]
                 }
function deepCopy(oldObj) {
  var newObj = {}
  for (var key in oldObj) {
    if (typeof oldObj[key] === 'object') {
      newObj[key] = deepCopy(oldObj[key])  //是對(duì)象的遞歸拷貝
    } else {
      newObj[key] = oldObj[key]   //非對(duì)象直接賦值
    }
  }
  return newObj
}
var newObject = deepCopy(oldObject)
console.log(newObject)                                 //newObject里面的內(nèi)容和oldObject一樣
console.log(newObject.wife == oldObject.wife)          //false
console.log(newObject.friends == oldObject.friends)     //false,兩個(gè)false說(shuō)明newObject的子對(duì)象開(kāi)辟新的內(nèi)存,和oldObject的子對(duì)象的堆內(nèi)存不一樣了  

參考
知乎回答 javascript中的深拷貝和淺拷貝?
什么是立即執(zhí)行函數(shù)?有什么作用?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容