js中的函数

282 阅读5分钟

定义

W3school定义:函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。

匿名函数

匿名函数的基本形式为(function(){...})();

前面的括号包含函数体,后面的括号就是给匿名函数传递参数并立即执行。匿名函数将函数声明并直接赋值给一个事件或一个变量。

window.onload = function() { return 1 }
var fn = function(){ return 1 }
var fn2 = fn;
fn2.name;  // fn
fn.name;   // fn

上述fn记录的是函数的地址,而不是函数本身。同理,fn2 赋值的是 fn 的地址;虽是匿名函数,但是有 name。

具名函数

字面意思,具有名字的函数。

function f3(){ return 3; } // fn3的作用域是整个域
var fn5 = function fn4(){}    // fn5的作用域只有在fn4这个函数本身中

箭头函数

MDN解释:箭头函数表达式的语法比函数表达式更简洁,并且没有自己的 thisargumentssupernew.target。这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数。引入箭头函数有两个方面的作用:更简短的函数并且不绑定 this

var fn6 = i => i+1     // fn6(7)则返回7
var fn7 = (i, j) => i+j
var fn8 = (i, j) => {
    console.log(1);
    return i+j;
}   // 若箭头函数加了{},则需要显式的return出来

词法作用域

var global1 = 1;
function fn1(param1) {
    var local1 = 'local1';
    var local2 = 'local2';

    function fn2(param2) {
        var local2 = 'inner local2';
        console.log(local1);   // local1 是 fn1 中的 local1
        console.log(local2);   // local2 是 fn2 本身的 local2
    }

    function fn3() {
        var local2 = 'fn3 local2';
        fn2(local2)
    }
}

抽象语法树确定的是两个变量之间的关系,跟变量的值没有关系。上述 fn2 中的 local1fn1 中的 local1 ,但是值不一定是 local1 的值。比如:

var a = 1;
function b(){
    console.log(a);
}
a=2; b(); // 此时打印出来的应该是2,但是b中的a是外面的a

call stack

call stack 是函数调用堆栈,就是程序运行时函数的调用过程,例如 A 函数调用了 B 函数,那么程序执行到 B 函数的时候,call stack 里就会有A函数,因为函数调用时需要把当前函数入栈,在 B 函数执行完毕后再从堆栈里将 A 函数取出,以让程序指针回到A函数继续运行。

调用堆栈是一个方法列表,按照调用顺序保存所有在运行期被调用的方法。

this & arguments

进入函数后,第一个要记录的是函数的地址,放到 call stack 中,第二个记录的是传入函数的参数(this,arguments)。

chrome浏览器显示 window 为 Window。

function f() {
    console.log(this);    // window
    console.log(arguments);
}
f.call();   // this: window;  arguments: []
f.call({name: 'John'});     // this: {name: 'John'};  arguments: []
f.call({name: 'John'}, 1);  // this: {name: 'John'};  arguments: [1]
f.call({name: 'John'}, 1, 2); // this: {name: 'John'};  arguments: [1,2]

综上,this 就是 call 的第一个参数,不传的话,thiswindow(严格模式下,为 undefined);后面的所有参数,都会放入 arguments 中包裹成一个数组。

f() 是阉割版的 f.call()f() 无法指定 this,传入的参数都放入 arguments

--- 为什么this必须是对象

--- 因为this就是函数与对象之间羁绊,如果传入的不是对象,则会转换成对象。

person.sayHi() 等价于 person.sayHi.call(person)fn() 等价于 fn.call()

call / apply

call 的第一个参数永远是 thiscall 的第二个参数是1,2,3,4。 apply 的第二个参数是[1,2,3,4]。

因此,在 arguments 的长度不确定或太长的时候,使用 apply

bind

callapply 直接调用函数,而 bind 则是返回一个新函数(并没有调用原来的函数),这个新函数会 call 原来的函数,call 的参数由你指定。

var view = {
    element: $("#div1"),
    bindEvents: function() {
        var _this = this;
        this.element.onclick = function() {
            _this.onClick.call(_this)
        }
    },
    onClick: function() {
        this.element.addClass('active')
    }
}

var view = {
    element: $("#div1"),
    bindEvents: function() {
        this.element.onclick = this.onClick.bind(this)
    },
    onClick: function() {
        this.element.addClass('active')
    }
}

var view = {
    element: $("#div1"),
    bindEvents: function() {
        this.onClick.bind = function(x, y, z) {
            var oldFn = this   // 也就是外面的 this.onClick
            return  function(){ 
                oldFn.call(x, y, z)
            }
        }
        this.element.onclick = this.onClick.bind(this)
    },
    onClick: function() {
        this.element.addClass('active')
    }
}

柯里化

把一个含有多个参数的函数中的其中一部分参数固定下来的叫做柯里化函数。

function sum(x, y) {
    return x+y
}

// addOne 为 sum 的一个柯里化函数
function addOne(y) {
    return sum(1, y)
}

高阶函数

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

接受一个或多个函数作为输入 输出一个函数 不过它常常同时满足上述两个条件

array.sort(function(a, b){a-b});   // array.sort.call(array, fn)
array.forEach(function(){});    // array.forEach.call(array, fn)
array.map(function(){});    // array.map.call(array, fn)
array.filter(function(){});   // array.filter.call(array, fn)
array.reduce(function(){});   // array.reduce.call(array, fn)

标准的高阶函数可以将函数任意的组合。

// 求数组中偶数的和
var array = [3, 2, 1, 4, 5, 6, 7, 8];
var sum = 0;
for(var i=0; i<array.length; i++) { 
    if(array[i] % 2 === 0) {
        sum += array[i] 
    }
}

// 可以使用如下高阶函数实现上述的功能
array.filter(function(n){return n%2===0})    // 2,4,6,8
    .reduce(function(prev, next){return prev+next}, 0)

/** 函数形式做如下改造
  * 其中 array为要过滤的数组;function(n){return n%2===0}为传入filter中的函数
  * filter(array, function(n){return n%2===0})得到的结果为一个数组[2,4,6,8]
  * 将上述filter得到的数组作为reduce的数组
  * function(prev, next){return prev+next} 作为传入reduce中的函数
  * reduce的初始值为0
**/ 
reduce(filter(array, function(n){return n%2===0}), function(prev, next){return prev+next}, 0)

// 同理,对数组中的奇数进行排序可以写成如下形式
sort(filter(array, function(n){n%2===1}), function(a, b){return a-b})

回调 callback

一个函数被当做参数就可以称这个函数为回调函数。

名词解释被当成参数的函数就是回调。 动词解释:调用这个回调。

回调跟异步没有任何关系。

构造函数

返回对象的函数就是构造函数。一般首字母大写。与普通函数相比,构造函数有如下特点:

  1. new 关键字调用;
  2. 函数内部可以使用 this 关键字,this 指向的是构造出的新对象;
  3. 默认不用 return 返回值,默认会返回 this,也就是新的实例对象;
  4. 函数命名一般首字母大写,与一般函数做区分。

箭头函数

箭头函数是没有 this 的,若希望函数内外的 this 相同时可以使用箭头函数

箭头函数不绑定 this,绑定 arguments。 箭头函数中的 this 永远没办法指定,可以将外面的 this 当做里面的 this