愿你坚持不懈,努力进步,进阶成自己理想的人

—— 2017.09, 写给3年后的自己

Javascript学习总结——函数

函数

一、函数基础知识

1、javascript中,函数也是对象,函数名可以视为指向该函数对象的变量。因此,定义函数除了用 function funcName(params...) 的形式外,还可以用 var funcName = function(params...) { functionBody }; 的形式
2、arguments 参数可非常灵活地运用(可变参数等),它只在函数内部起作用。它的使用类似于数组,函数的第一个参数是 arguments[0],第二个参数是 arguments[1],以此类推,但它的类型是object,不是Array,它有一个 arguments.length的属性,可以用之做出很多灵活的设计
3、由于采用 arguments 时,即使已经定义了参数,要使用这些参数仍然需要从0开始索引,比如 function add(a, b)的情况下,使用 add(1, 2, 3, 4),要获取第三个、第四个参数需要用 arguments[2]arguments[3]。为了解决这个麻烦的问题,ES6中引入了rest写法,上述例子可以改写为 function add(a, b, ...rest),此时rest[

function foo() {
  return
  {
    name: "foo"
  }
}

由于javascript自动添加分号,则上述例子会变为类似于:

function foo() {
  return; // 这里自动添加了分号,导致下面的语句无法得到执行
  {
    name: "foo"
  }
}



二、变量的作用域

1、函数体内声明的变量,只在函数体内有效
2、函数可以嵌套,嵌套函数可以访问它上级函数作用域的变量
3、若函数内部声明的变量和全局变量同名,则全局变量被屏蔽
4、Javascript具有“变量提升”特性,即对于函数体内声明的变量,都会自动提升到函数体的头部。如:

'use strict';
function foo() {
  var x = "Hello " + y;
  console.log(x); // 输出:Hello undefined
  var y = "World";
}

这样子的情况下,即使使用了strict模式,也并不会报错,因为变量会自动提升,相当于:

'use strict';
function foo() {
  var y;
  var x = "Hello " + y;
  console.log(x);
  y = "World";
}

5、全局作用域的变量,将成为window对象的一个熟属性,即在全局范围声明的 var xxx 等同于 window.xxx,而其实,全局函数也可以用形如 window.foo() 来访问
6、名称空间,使用对象来实现,如:

var MYSPACE = {};
MYSPACE.name = "RuphiLau";
MYSPACE.foo  = function() {
  return true;
}



三、let和const

1、使用let来声明变量,具有更严格的行为,如:let具有块级作用域,仅仅在当前块级作用域内有效,如:

function foo() {
  for(var i = 1; i<= 100; i++) {

  }
  console.log(i);
}
foo(); // 输出101

function foo() {
  for(let i = 1; i<= 100; i++) {

  }
  console.log(i);
}

foo(); // 报错

2、let不存在var中的变量提升现象,也不允许重复声明
3、ES6中使用const来声明常量,具有块级作用域


四、高阶函数

由于函数指向某个变量(如var fn = function(){ ...}),而函数可以接收变量,故而函数也可以接收一个函数作为参数,这样子的函数叫做高阶函数,如:

function add(x, y, fn) {
  return fn(x) + fn(y);
}

add(-3, 7, Math.abs); // 返回10



五、map/reduce、filter、sort

1、map实现对数组中的所有数据执行同一操作,例如:

function pow(x) {
  return x*x;
}

var arr = [1, 2, 3, 4, 5];
var newArr = arr.map(pow); // 返回[1, 4, 9, 16, 25]

2、reduce实现将结果和上一次的结果进行累积,如:

function fn(x, y) {
  return x + y;
}
var arr = [1, 2, 3, 4];
arr.reduce(fn); // 相当于 fn(fn(fn(1, 2), 3), 4),返回10

3、综合练习:使用map/reduce,实现string转int

function string2int(s) {
  var arr = s.split("").map(x => {
    return x - 0;
  });
  
  return arr.reduce((x, y) => {
    return x*10 + y;
  });
}

4、filter用于实现过滤,对于回调函数返回true的元素进行保留,false的剔除,如:

var arr = [1, 2, 3, 4, 5, 6];
var newArr = arr.filter(x => {
  return x%2 != 0;
})
// newArr返回[1, 3, 5]

注意,回调函数可以接收三个参数,即 (elem, index, self)
5、sort用于排序,如果认为回调函数的参数(x, y)中,x应该在y前面,返回-1,x在y后面,返回1,否则返回0。同时,sort默认是转化成string形式根据ASCII码排序的,所以,形如:

[15, 2, 20].sort();

会返回[15, 2, 20],因此,需要注意这么一个坑。此外,还需要注意的是,sort是直接对当前数组进行修改,而非返回一个新的数组


六、箭头函数

箭头函数是ES6新增的语法,它可以简化匿名函数的书写,同时也修正了this指向的问题

1、最简单的箭头函数:

var fn = x => x*x;
// 上面的语句相当于
var fn = function(x) {
  return x*x;
}

2、箭头函数的其他写法:

// 语句体有多条语句时
x => {
  //statement
}
// 多个参数
(x, y) => {
  //statement
}
// 可变参数
(x, y, ...rest) => {
  // statement
}
// 无参数:
() => {
  // statement
}

当语句体只有一条return语句时,可以省略{}和return,而当要返回一个对象的时候,由于对象的{}与之冲突,故需要改为:

(name) => ({yourName:name})

3、箭头函数修复了对this绑定的错误(但是需要遵守:除了对象上的直接函数属性值用function语法外,其它函数都用箭头函数),因此,比较下面的例子:

'use strict';

var Student = {
  name: "Ruphi",
  birth: 1996,
  getAge: function() {
    var fn = function() {
      return new Date().getFullYear() - this.birth;
    }
    return fn();
  }
}

Student.getAge();
/*
报错:Uncaught TypeError: Cannot read property 'birth' of undefined
*/

如果改为:

'use strict';

var Student = {
  name: "Ruphi",
  birth: 1996,
  getAge: function() {
    var fn = () => {
      return new Date().getFullYear() - this.birth;
    }
    return fn();
  }
}

Student.getAge(); // 正常返回21

4、箭头函数在使用call和apply时,将不能进行this的绑定,故第一个参数失效