函数

函数知识

函数是JavaScript世界里第一等公民,在这个世界里到处都是函数;而函数带有一个特别重要的绝招——定义 作用域,在 ECMAScript 6 之前,只有函数才有这个绝招

函数常见的四种形态

  • 函数的声明形态

function func(){
  console.log("函数的声明形态");
}
  • 函数的表达式形态

let func0 = function(){
  console.log("函数的表达式形态一")
}

(function func1(){console.log("函数的表达式形态二")})
  • 函数嵌套形态

let func2 = function(){
  console.log("函数的嵌套形态");
  let func3 = function(){
      console.log("func2嵌套在func1里")
  }
  func3();
}
  • 函数的闭包形态

 let func4 = function(){
    var n = "ow";
    return function(){
        console.log("我是以闭包形态存在的函数:"+ n)
    }
}

函数声明提升

只有声明形态的函数,才具有提升的特性。所谓的提升,意思就是代码的执行顺序提升排到最前面。
console.log(fn) //function(){return:0}
console.log(fn1) // undefined
//函数的声明形态
function fn(){
  return 0;
}
//函数的表达式形态
var fn1 = function(){
  return 1;
}

IIFE 与 匿名函数、有名函数

IIFE(Immediately-Invoked Function Expression, 立即执行函数)形式的函数调用方式,非常适合匿名函数调用。如一下代码,两种表达方式
(function(){console.log("我是立即执行的匿名函数")})();
(function(){console.log("我也是立即执行的匿名函数")}());

既然匿名函数让代码这么简洁,为什么还需要给函数起名--有名函数呢? 首先,最好的理由之一当然是为了方便递归,递归需要函数调用自身,函数如果没有名字,就无法有效地通过一个标志符(名字)找到函数自身以便供调用。函数的名字可以通过 name 属性读取到。

//函数调用自身称为递归,函数名为“func”
(function fn(i){
  console.log("函数名为"+func.name+",第"+i+"次调用")
  if(i<3){//递归出口
      fn(++i);//递归
  }
})(1);

其次,匿名函数不利于调试栈追踪,有名函数根据名字可以很快在调试的时候定位代码位置。

作用域(Scope)

作用域即函数或者变量的可见区域。通俗来讲,函数或者变量不在这个区域内,就无法访问到。

函数作用域 ES6块级作用域 为什么要引进块级作用域? 作用域链(scope chain) 变量函数的查找机制 ......见下篇介绍

箭头函数

用(参数)=>{表达式}这种写法声明一个函数,就叫箭头函数

(function(i){
console.log(i);
})(1)

((i)=>console.log(i))

箭头函数一个明显作用就是可以保持this的指向,总是指向定义它时所在的上下文环境。关于this内容会单独详述

高阶函数

如果某个函数可以接受另外一个函数作为参数,该函数就称之为高阶函数。
函数作为参数?这似乎太奇怪了,其实是因为javascript里的函数可以赋值给某个变量,而变量可以作为参数传递给函数,因此函数也可以作为参数传递函数。
function fn(){
  if(callback){
      callback();
  }
}
fn1(function(){
  console.log("高阶函数")
})

函数重载

学习地址

重载是面向对象编程语言(比如Java、C#)里的特性,JavaScript语言并不支持该特性。所谓重载(overload),就是函数名称一样,但是随着传入的参数个数不一样,调用的逻辑或返回的结果会不一样。

简单的实现一个重载的方法

function overLoading(){
  //根据arguments.length,对不同的值进行不同的操作
  switch(arguments.length){
      case 0 :
      //操作0的代码写在这里
      brank;
      case 1:
      //操作 1的代码写在这里
      brank;
      default:
      //默认执行这里
  }
}

在JQuery之父John Resig写的《secrets of the JavaScript ninja》找到了一个绝佳巧妙的方法!那种方法充分的利用了闭包的特性!

function addMethod(object, name, fn) {
  var old = object[name]; //把前一次添加的方法存在一个临时变量old里面
  object[name] = function() { // 重写了object[name]的方法
    // 如果调用object[name]方法时,传入的参数个数跟预期的一致,则直接调用
    if(fn.length === arguments.length) {
      return fn.apply(this, arguments);
    // 否则,判断old是否是函数,如果是,就调用old
    } else if(typeof old === "function") {
      return old.apply(this, arguments);
    }
  }
}

现在有这样一个需求,有一个people对象,里面存着一些名人,如下:

var people = {
values: ["Dean Edwards", "Sam Stephenson", "Alex Russell", "Dean Tom"]
};

我们希望people对象拥有一个find方法,当不传任何参数时,就会把people.values里面的所有元素返回来;当传一个参数时,就把first-name跟这个参数匹配的元素返回来;当传两个参数时,则把first-name和last-name都匹配的才返回来。因为find方法是根据参数的个数不同而执行不同的操作的,所以,我们希望有一个addMethod方法,能够如下的为people添加find的重载

addMethod(people, "find", function() {}); /*不传参*/
addMethod(people, "find", function(a) {}); /*传一个*/
addMethod(people, "find", function(a, b) {}); /*传两个*/

OK,现在这个addMethod方法已经实现了,我们接下来就实现people.find的重载啦!

//addMethod
function addMethod(object, name, fn) {
  var old = object[name];
  object[name] = function() {
    if(fn.length === arguments.length) {
      return fn.apply(this, arguments);
    } else if(typeof old === "function") {
      return old.apply(this, arguments);
    }
  }
}


var people = {
  values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};

/* 下面开始通过addMethod来实现对people.find方法的重载 */

// 不传参数时,返回peopld.values里面的所有元素
addMethod(people, "find", function() {
  return this.values;
});

// 传一个参数时,按first-name的匹配进行返回
addMethod(people, "find", function(firstName) {
  var ret = [];
  for(var i = 0; i < this.values.length; i++) {
    if(this.values[i].indexOf(firstName) === 0) {
      ret.push(this.values[i]);
    }
  }
  return ret;
});

// 传两个参数时,返回first-name和last-name都匹配的元素
addMethod(people, "find", function(firstName, lastName) {
  var ret = [];
  for(var i = 0; i < this.values.length; i++) {
    if(this.values[i] === (firstName + " " + lastName)) {
      ret.push(this.values[i]);
    }
  }
  return ret;
});

// 测试:
console.log(people.find()); //["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(people.find("Dean")); //["Dean Edwards", "Dean Tom"]
console.log(people.find("Dean Edwards")); //["Dean Edwards"]

 早上好

posted @ 2020-09-17 09:21  大瑶瑶  阅读(329)  评论(0)    收藏  举报