JS作用域链 & JS引用类型

2019-03-14  本文已影响0人  antimony

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

在 Javascript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。

有时,我们需要在定义函数之后,立即调用该函数。

JavaScript 引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。

解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。

(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

// 写法一
var tmp = newData;
processData(tmp);
storeData(tmp);

// 写法二
(function () {
  var tmp = newData;
  processData(tmp);
  storeData(tmp);
}());

求n!,用递归来实现。

function factorial(num) {
  if(num>0) {
    return factorial(num-1)*num;
  }
  else return 1;
}

以下代码输出什么?

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

输出:

"name:"
"饥人谷"
"age:"
2
"sex:"
"男"
[object Arguments] {
  0: "饥人谷",
  1: 2,
  2: "男"
}
"name"
"valley"
"name:"
"小谷"
"age:"
3
"sex:"
undefined
[object Arguments] {
  0: "小谷",
  1: 3
}
"name"
"valley"
"name:"
"男"
"age:"
undefined
"sex:"
undefined
[object Arguments] {
  0: "男"
}
"name"
"valley"

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

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

答案:

function sumOfSquares() {
  var result = 0;
   for(var i in arguments){
     result += (arguments[i]*arguments[i]);
   }
  return result;
}

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

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

undefined
VM100:3 Uncaught ReferenceError: b is not defined at <anonymous>:3:17 (anonymous) @ VM100:3
因为变量声明被提前变成:

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

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

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

输出:
hello world
VM1098:2 Uncaught TypeError: sayAge is not a function at <anonymous>:2:5 (anonymous) @ VM1098:2
因为第一个函数声明被提前,变成:

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

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

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

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

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

输出:10
因为变量和函数的声明被提前,所以代码等价于:

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

又因为作用域链,所以:


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

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

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

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

输出:30
因为作用域链。

  • 函数在执行的过程中,先从自己内部找变量
  • 如果找不到,再从创建当前函数所在的作用域去找, 以此往上
  • 注意找的是变量的当前的状态

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

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,因为作用域链。

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

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,作用域链。


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

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
因为声明提前和作用域链,fn3函数内等价于:

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

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

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
vendor.7ab21e702e7733b6b702.js:1 {a: 1, b: 2}
vendor.7ab21e702e7733b6b702.js:1 true
因为obj1和obj2一开始指向两个地址,在赋值后指向同一地址。


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

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}
因为第一个是值传递,第二个是引用传递。


写一个深拷贝函数。

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;
    }
上一篇下一篇

猜你喜欢

热点阅读