JS函数以及作用域问题

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

立即执行函数表达式就是

  • 声明一个匿名函数
  • 马上执行这个匿名函数

典型写法:( function(){alert('匿名函数')} )()

为什么要用一对括号把匿名函数包起来呢?
因为不加括号,写成function(){alert('匿名函数')} ()会报错,原因是function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
解决方法就是不要让function出现在行首,因此可以加点东西,有以下写法

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

那么立即执行函数表达式有什么作用呢?
作用是创建一个独立的作用域。这个作用域里面的变量,外面访问不到,这样可以避免变量污染

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

function recursion(n) {
  if (n === 1) {
    return 1;
  }
  return n * recursion(n-1);
}
var result = recursion(10);  
console.log(result);     //以n等于10为列子,结果: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('男');

调用getInfo('饥人谷', 2, '男')输出:

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

调用getInfo('小谷', 3)输出

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

调用getInfo('男')输出:

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

注:
1.给函数传入参数时是按顺序传入,没有传入参数则为undefined。
2.在函数内部,可以使用arguments对象获取到该函数的所有传入参数

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

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代表传入实参的个数

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

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

输出:

undefined
error(b is not defined)

注:JS中变量声明会被提前,因此上述代码就相当于

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中函数声明和变量声明一样也会被提前,因此上述代码就相当于

function sayName(name){
    console.log('hello ', name);
}
var sayAge
sayName('world');          //输出hello world
sayAge(10);                //报错,sayAge不是函数类型
sayAge = function(age){
    console.log(age);
};

7.如下代码输出什么? 为什么

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

输出结果为:10
理由:
1.首先foo()和bar()函数的声明会被提前,因此bar()可以正常执行
2.接着bar()调用了foo(),而foo()输出了个x
3.在foo()函数的内部并未找到x变量,这时候JS会从声明该函数所在的作用域去找, 以此往上(上述代码中也就是最外层的x,即为10)

8.如下代码输出什么? 为什么

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

输出结果为: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() //输出多少

输出结果为: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() //输出多少

输出结果为: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() //输出多少

输出结果为:undefined
这题尤其注意,这题和上面的不同的点在于,由于变量提前导致fn2()的执行是在a变量声明之后,赋值之前(a此时只声明了,还没有赋值,因此为undefined),即函数fn3的实际执行是以下的

  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

理由:
对象的相等,当且仅当他们的引用指向内存中的相同区域时才相等,即他们在栈内存中的引用地址相同
obj1和obj2分别是两个不同的对象引用,里面存放的地址是不同的,即他们指向的区域是不同的,即时区域中的内容是一样的也不相等
而把通过obj1=obj2,将obj2中存放的地址赋给了obj1之后,obj1和obj2指向了同一块区域,所以最后两者相等了

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作为实参传给了f1的形参n,相当于把a的值赋给了n,然后进行++n,及n的值变成了2,而a的值仍然是1
c作为实参传给了f2的形参obj,c是一个对象,里面存放的是指向它里面的内容所在内存中区域的地址,在f2中对这个地址指向的内存区域里面的age进行了前++,所以改变了这块内存区域中age的实际的值,即age最后得到3
至于将c.age作为实参传给了f1的形参n,仍然是把age作为一个值赋给了n,并没有改变age本身
注:值的传递并不能改变本身,引用的传递才可以

14.写一个深拷贝函数

浅拷贝:对于字符串类型,浅拷贝是对值的复制,对于对象来说,浅拷贝是对对象地址的复制,并没 有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变。对于浅拷贝,子对象以及子对象下面的对象都是共用的。
浅拷贝实现函数:

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()函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性。如果有,返回true,否则返回false
      newObj[i] = oldObj[i]
    }
  }
  return newObj
}
var newObject = shallowCopy(oldObject)
console.log(newObject)                                 //newObject里面的内容和oldObject一样
console.log(newObject.wife == oldObject.wife)          //true 
console.log(newObject.friends == oldObject.friends)     //true,两个true说明newObject的子对象并没有开辟新的内存,和oldObject的子对象共用一块堆内存                      

深拷贝:深拷贝是开辟新的栈,两个对象对应两个不同的地址,即指向堆中不同的空间,修改一个对象的属性,不会改变另一个对象的属性。深拷贝会递归复制子对象及子对象下面的对象,并且新对象和旧对象不是共用一块堆区域。
深拷贝实现函数:

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])  //是对象的递归拷贝
    } else {
      newObj[key] = oldObj[key]   //非对象直接赋值
    }
  }
  return newObj
}
var newObject = deepCopy(oldObject)
console.log(newObject)                                 //newObject里面的内容和oldObject一样
console.log(newObject.wife == oldObject.wife)          //false
console.log(newObject.friends == oldObject.friends)     //false,两个false说明newObject的子对象开辟新的内存,和oldObject的子对象的堆内存不一样了  

参考
知乎回答 javascript中的深拷贝和浅拷贝?
什么是立即执行函数?有什么作用?

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

推荐阅读更多精彩内容