Javascript_函数

JavaScript 三种声明函数的方法

1. function 命令 
function print(s) {
    console.log(s);
}

2. 函数表达式,这种写法将一个匿名函数赋值给变量。

var print = function(s) {
    console.log(s);
}

3. Function 构造函数,几乎无人使用

var add = new Function(
    'x',
    'y',
    'return x + y'

);

圆括号运算符,return 语句和递归
function fib(num) {
  if (num === 0) return 0;
  if (num === 1) return 1;
  return fib(num - 2) + fib(num - 1);
}

第一等公民
function add(x, y) {
  return x + y;
}

// 将函数赋值给一个变量
var operator = add;

// 将函数作为参数和返回值
function a(op){
  return op;
}
a(add)(1, 1)
// 2



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

函数的属性和方法

 function fib(num) {
  if (num === 0) return 0;
  if (num === 1) return 1;
  return fib(num - 2) + fib(num - 1);
}
undefined
fib.name
//"fib"
fib.length
//1





函数作用域

var v = 1; //全局变量
//
function f() {
    var v = 2; //局部变量,函数内部变量会覆盖全局变量
}

//注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。

if (true) {
    var x = 5;  // 这里的 x 是全局变量,因为它不在函数中
    
}


函数内部的变量提升

//与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
function foo(x) {
  if (x > 100) {
    var tmp = x - 100;
  }
}

// 等同于
function foo(x) {
  var tmp;
  if (x > 100) {
    tmp = x - 100;
  };
}

函数本身的作用域
//函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();   //函数x是在函数f的外部声明的,所以它的作用域绑定外层,内部变量a不会到函数f体内取值,所以输出1,而不是2。
}

f() // 1


function foo() {
  var x = 1;
  function bar() {
    console.log(x);
  }
  return bar;    
}

var x = 2;
var f = foo();
f() // 1


参数
//函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。这意味着,在函数体内修改参数值,不会影响到函数外部。
var p = 2;

function f(p) {
  p = 3;
}
f(p);

p // 2
//但是,如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。


var obj = { p: 1 };

function f(o) {
  o.p = 2;
}
f(obj);

obj.p // 2

//注意,如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。

var obj = [1, 2, 3];

function f(o) {
  o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]

同名参数


如果有同名的参数,则取最后出现的那个值。

function f(a, a) {
  console.log(a);
}

f(1, 2) // 2

//调用函数f()的时候,没有提供第二个参数,a的取值就变成了undefined。这时,如果要获得第一个a的值,可以使用arguments对象。
function f(a, a) {
  console.log(a);
}

f(1) // undefined

function f(a, a) {
  console.log(arguments[0]);
}

f(1) // 1


arguments 对象

//由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。

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


var f = function (one) {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
}

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


var f = function(a, b) {
  arguments[0] = 3;
  arguments[1] = 2;
  return a + b;
}

f(1, 1) // 5


//上面代码中,函数f()调用时传入的参数,在函数内部被修改成3和2。

//严格模式下,arguments对象与函数参数不具有联动关系。也就是说,修改arguments对象不会影响到实际的函数参数。



var f = function(a, b) {
  'use strict'; // 开启严格模式
  arguments[0] = 3;
  arguments[1] = 2;
  return a + b;
}

f(1, 1) // 2

上面代码中,函数体内是严格模式,这时修改arguments对象,不会影响到真实参数a和b。

通过arguments对象的length属性,可以判断函数调用时到底带几个参数。


function f() {
  return arguments.length;
}

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




函数的其他知识点


//闭包
//闭包(closure)是 JavaScript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。

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

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

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

//闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
//闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。



function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);   // 将 createIncrementor 内部方法赋值给变量 inc 

inc() // 5
inc() // 6
inc() // 7

闭包的另一个用处,是封装对象的私有属性和私有方法
function Person(name) {
  var _age;
  function setAge(n) {
    _age = n;
  }
  function getAge() {
    return _age;
  }

  return {    // return 通过 对象这个数据类型返回 函数内部的方法
    name: name,
    getAge: getAge,
    setAge: setAge
  };
}

var p1 = Person('张三');
p1.setAge(25);
p1.getAge() // 25


立即调用的函数表达式(IIFE)

var f = function f(){ return 1}();
f // 1

//函数定义后立即调用的解决方法,就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。

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




eval 命令

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

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


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

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

a // 2

(function f() {
  'use strict';
  eval('var foo = 123');
  console.log(foo);  // ReferenceError: foo is not defined
})()


eval 的别名调用,不推荐使用
原文地址:https://www.cnblogs.com/zy09/p/14340223.html