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中的深拷贝和浅拷贝?
什么是立即执行函数?有什么作用?

你可能感兴趣的:(JS函数以及作用域问题)