JavaScript学习笔记-函数

函数

1.函数本身的作用域

函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() // 1

上面代码中,函数x是在函数f的外部声明的,所以它的作用域绑定外层,内部变量a不会到函数f体内取值,所以输出1,而不是2。
总之,函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。
很容易犯错的一点是,如果函数A调用函数B,却没考虑到函数B不会引用函数A的内部变量。

2.函数名的提升 (变量提升)

JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错。

f();

function f() {}

表面上,上面代码好像在声明之前就调用了函数f。但是实际上,由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript 就会报错。

f();
var f = function (){};
// TypeError: undefined is not a function

上面的代码等同于下面的形式。

var f;
f();
f = function () {};

上面代码第二行,调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。因此,如果同时采用function命令和赋值语句声明同一个函数,最后总是采用赋值语句的定义。

    var f = function () {
      console.log('1');
    }
    
    function f() {
      console.log('2');
    }
    
    f() // 1

4.arguments 对象

定义:arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。

function f() {
  return arguments.length;
}

f(1, 2, 3) // 3
f(1) // 1
f() // 0

5.闭包

理解闭包,首先必须理解变量作用域。前面提到,JavaScript 有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); // 999

上面代码中,函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了。

定义:能够读取其他函数内部变量的函数,定义在一个函数内部的函数。
特点:“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。
用处:一个是可以读取函数内部的变量。另一个是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在
注意:外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

6. eval命令

eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。

eval('var a = 1;');
a // 1

eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。

var a = 1;
eval('a = 2');
a // 2

使用场景:解析 JSON 数据的字符串,不过正确的做法应该是使用原生的JSON.parse方法
注意:当eval是别名调用时,它的作用域讲变成全局作用域。如下

var a = 1;
function f() {
  var a = 2;
  var e = eval;
  e('console.log(a)');
}

f() // 1`

7.Number()函数

使用Number函数,可以将任意类型的值转化成数值
1. 原始类型值

// 数值:转换后还是原来的值
Number(324) // 324

// 字符串:如果可以被解析为数值,则转换为相应的数值
Number('324') // 324

// 字符串:如果不可以被解析为数值,返回 NaN
Number('324abc') // NaN

// 空字符串转为0
Number('') // 0

// 布尔值:true 转成 1,false 转成 0
Number(true) // 1
Number(false) // 0

// undefined:转成 NaN
Number(undefined) // NaN

// null:转成0
Number(null) // 0

对比parseInt函数,Number函数严格很多,有一个字符不能转化为数值,则整个字符串就被转为NaN

parseInt('42 cats') // 42
Number('42 cats') // NaN

2. 对象

转换规则:Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。

Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5

转换步骤:
1. 用对象自身的valueOf方法。如果返回原始类型的值,则直接对该值使用Number函数,不再进行后续步骤。
2. 如果valueOf方法返回的还是对象,则改为调用对象自身的toString方法。如果toString方法返回原始类型的值,则对该值使用Number函数,不再进行后续步骤。
3. 如果toString方法返回的是对象,就报错。

8. String()函数

1. 原始类型值

String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"

2. 对象
String方法的参数若是对象,则返回一个类型字符串,若是数组,则返回数组的字符串形式

String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"

转换规则:先执行valueOf方法,再执行toString方法
转换步骤:
1. 先调用对象自身的toString方法。如果返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
2. 如果toString方法返回的是对象,再调用原对象的valueOf方法。如果valueOf方法返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
3. 如果valueOf方法返回的是对象,就报错。

参考:阮一峰的JavaScript 教程

猜你喜欢

转载自blog.csdn.net/qq_34997906/article/details/82986986