Web开发技术梳理 0x5 JavaScript(0x2) 函数

定义函数

    函数声明

    一个函数定义(也称为函数声明,或函数语句)由一系列的function关键字组成,依次为:

  • 函数的名称。
  • 函数参数列表,包围在括号中并由逗号分隔。
  • 定义函数的 JavaScript 语句,用大括号{}括起来。

     原始参数(比如一个具体的数字)被作为传递给函数;值被传递给函数,如果被调用函数改变了这个参数的值,这样的改变不会影响到全局或调用函数。

    如果传递一个对象(即一个非原始值,例如Array或用户自定义的对象)作为参数,而函数改变了这个对象的属性,这样的改变对函数外部是可见的,如下

function myFunc(theObject) {
  theObject.make = "Toyota";
}

var mycar = {make: "Honda", model: "Accord", year: 1998};
var x, y;

x = mycar.make;     // x获取的值为 "Honda"

myFunc(mycar);
y = mycar.make;     // y获取的值为 "Toyota"
                    // (make属性被函数改变了)

    函数表达式

    函数也可以由函数表达式创建。这样的函数可以是匿名的;它不必有一个名称。

    然而,函数表达式也可以提供函数名,并且可以用于在函数内部代指其本身,或者在调试器堆栈跟踪中识别该函数:

var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)};

console.log(factorial(3));//阶乘

     当将函数作为参数传递给另一个函数时,函数表达式很方便。下面的例子演示了一个叫map的函数如何被定义,而后使用一个表达式函数作为其第一个参数进行调用:

function map(f, a) {
  var result = []; // 创建一个数组
  var i; // 声明一个值,用来循环
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
      return result;
}
var f = function(x) {
   return x * x * x; 
}
var numbers = [0,1, 2, 5,10];
var cube = map(f,numbers);
console.log(cube);// [0, 1, 8, 125, 1000]

     在 JavaScript 中,可以根据条件来定义一个函数。

var myFunc;
if (num == 0){
  myFunc = function(theObject) {
    theObject.make = "Toyota"
  }
}

调用函数

    函数一定要处于调用它们的域中,但是函数的声明可以被提升(出现在调用语句之后)。

函数作用域

    在函数内定义的变量不能在函数之外的任何地方访问,因为变量仅仅在该函数的域的内部有定义。相对应的,一个函数可以访问定义在其范围内的任何变量和函数。

作用域和函数堆栈 

    递归

    一个函数可以指向并调用自身。有三种方法:

  1. 函数名
  2. arguments.callee
  3. 作用域下的一个指向该函数的变量名
var foo = function bar() {
   // statements go here
   /*在这个函数体内,以下语句是等价的:
   bar()
   arguments.callee()
   foo()
   */
};

    嵌套函数和闭包

    一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。它自身也形成了一个闭包。一个闭包是一个可以自己拥有独立的环境与变量的的表达式(通常是函数)。并且内部函数包含外部函数的作用域。

  • 内部函数只可以在外部函数中访问。
  • 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。
/*内部函数形成了闭包,因此你可以调用外部函数并为外部函数和内部函数指定参数:*/
function outside(x) {
  function inside(y) {
    return x + y;
  }
  return inside;
}
fn_inside = outside(3); // Think of it like: give me a function that adds 3 to whatever you give it
result = fn_inside(5); // returns 8

result1 = outside(3)(5); // returns 8

    保存变量

    注意到上例中 inside 被返回时 x 是怎么被保留下来的。一个闭包必须保存它可见作用域中所有参数和变量。因为每一次调用传入的参数都可能不同,每一次对外部函数的调用实际上重新创建了一遍这个闭包。只有当返回的 inside 没有再被引用时,内存才会被释放。

    多层嵌套函数

    函数可以嵌套,那闭包就可以包含多个作用域,递归式包含函数作用域,称作用域链。

function A(x) {
  function B(y) {
    function C(z) {
      console.log(x + y + z);
    }
    C(3);
  }
  B(2);
}
A(1); // logs 6 (1 + 2 + 3)

    命名冲突

    命名冲突,越近的作用域优先权越高,链的第一个元素就是最里面的作用域,最后一个元素就是最外层的作用域。

function outside() {
  var x = 5;
  function inside(x) {
    return x * 2;
  }
  return inside;
}
/*这里的作用链域是{inside, outside, 全局对象}*/

outside()(10); // returns 20 instead of 10

闭包

    此当内部函数生存周期大于外部函数时,外部函数中定义的变量和函数将的生存周期比内部函数执行时间长。当内部函数以某一种方式被任何一个外部函数作用域访问时,一个闭包就产生了。

var pet = function(name) {          //外部函数定义了一个变量"name"
  var getName = function() {            
    //内部函数可以访问 外部函数定义的"name"
    return name; 
  }
  //返回这个内部函数,从而将其暴露在外部函数作用域
  return getName;               
};
myPet = pet("Vivie");
    
myPet();                            // 返回结果 "Vivie"

     外部函数的变量对内嵌函数可获取,但是内嵌函数的变量除了本身无法获取,相对安全。

     注意!!!如果内嵌函数有和外部函数同名的变量,那外部函数域将再也无法指向该变量。

使用arguments对象

    函数的实际参数会保存在一个类似数组的arguments对象中,

arguments[i]

使用arguments对象,可以处理比声明的更多的参数来调用函数。这在事先不知道会需要将多少参数传递给函数时十分有用。

function myConcat(separator) {
   var result = ''; // 把值初始化成一个字符串,这样就可以用来保存字符串了!!
   var i;
   // iterate through arguments
   for (i = 1; i < arguments.length; i++) {
      result += arguments[i] + separator;
   }
   return result;
}
// returns "red, orange, blue, "
myConcat(", ", "red", "orange", "blue");

// returns "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// returns "sage. basil. oregano. pepper. parsley. "
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

函数参数

    默认参数

    函数默认参数是undefined,某些情况下需设置不同默认值。

    在过去,用于设定默认的一般策略是在函数的主体测试参数值是否为undefined

function multiply(a, b) {
  b = (typeof b !== 'undefined') ?  b : 1;

  return a*b;
}

multiply(5); // 5

    使用默认参数,在函数体的检查就不再需要了。

function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5

    剩余参数

    剩余参数语法允许将不确定数量的参数表示为数组。

function multiply(multiplier, ...theArgs) {
  return theArgs.map(x => multiplier * x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

箭头函数

    箭头函数表达式(也称胖箭头函数)相比函数表达式具有较短的语法并以词法的方式绑定 this。箭头函数总是匿名的。

     更简洁的函数

var a = [
  "Hydrogen",
  "Helium",
  "Lithium",
  "Beryllium"
];

var a2 = a.map(function(s){ return s.length });

console.log(a2); // logs [ 8, 6, 7, 9 ]

var a3 = a.map( s => s.length );

console.log(a3); // logs [ 8, 6, 7, 9 ]

    this的词法

    在箭头函数出现之前,每一个新函数都重新定义了自己的 this 值(在严格模式下,一个新的对象在构造函数里是未定义的,以“对象方法”的方式调用的函数是上下文对象等)。

function Person() {
  // The Person() constructor defines `this` as itself.
  this.age = 0;

  setInterval(function growUp() {
    // In nonstrict mode, the growUp() function defines `this` 
    // as the global object, which is different from the `this`
    // defined by the Person() constructor.
    this.age++;
  }, 1000);
}

var p = new Person();

    在ECMAScript 3/5里,通过把this的值赋值给一个变量可以修复这个问题。另外,创建一个约束函数可以使得 this值被正确传递给 growUp() 函数。

function Person() {
  var self = this; // Some choose `that` instead of `self`. 
                   // Choose one and be consistent.
  self.age = 0;

  setInterval(function growUp() {
    // The callback refers to the `self` variable of which
    // the value is the expected object.
    self.age++;
  }, 1000);
}

    箭头函数捕捉闭包上下文的this值,所以下面的代码工作正常。

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| properly refers to the person object
  }, 1000);
}

var p = new Person();

预定义函数

  • eval()函数会将传入的字符串当做 JavaScript 代码进行执行。
  • uneval()函数会将传入的字符串当做 JavaScript 代码进行执行。
  • isFinite()函数判断传入的值是否是有限的数值。 如果需要的话,其参数首先被转换为一个数值。
  • isNaN()函数判断一个值是否是NaN。注意:isNaN函数内部的强制转换规则十分有趣; 另一个可供选择的是ECMAScript 6 中定义Number.isNaN() , 或者使用 typeof来判断数值类型。
  • parseFloat() 函数解析字符串参数,并返回一个浮点数。
  • parseInt() 函数解析字符串参数,并返回指定的基数(基础数学中的数制)的整数。
  • decodeURI() 函数对先前经过encodeURI函数或者其他类似方法编码过的字符串进行解码。
  • decodeURIComponent()方法对先前经过encodeURIComponent函数或者其他类似方法编码过的字符串进行解码。
  • encodeURI()方法通过用以一个,两个,三个或四个转义序列表示字符的UTF-8编码替换统一资源标识符(URI)的某些字符来进行编码(每个字符对应四个转义序列,这四个序列组了两个”替代“字符)。
  • encodeURIComponent() 方法通过用以一个,两个,三个或四个转义序列表示字符的UTF-8编码替换统一资源标识符(URI)的每个字符来进行编码(每个字符对应四个转义序列,这四个序列组了两个”替代“字符)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值