JavaScript基本---函数

一、JavaScript函数没有函数重载

1.函数参数arguments对象,类数组对象

恰是由于函数体内运用arguments对象吸收通报的参数,所以即使你定义的函数只吸收两个参数,在挪用函数时也未必一定要通报两个参数。

2.函数重载

在其他语言中如Java,所谓函数重载就是要领名相同参数差别的一切要领,由于在Java中只需函数署名(接收的参数范例和数目)差别,就认为是差别的函数。然则在JavaScript中函数没有署名,所以做不到真正的函数重载

3.运用arguments对象来填补没有函数重载的缺憾

function doAdd(){
    if(arguments.length == 1){
        return arguments[0]+10;
    }else if(arguments.length == 2){
        return arguments[0]+arguments[1];
    }else{
        let sum = 0;
        for(let i = 0; i < arguments.length; i++){
            sum += arguments[i];
        }
        return sum;
    }
}

二、函数声明与函数表达式

1.函数声明语法

function 函数名(参数:可选) {
    函数体
}

假如函数在函数体内,或许位于顺序的最顶部的话,那它就是一个函数声明

2.函数表达式

function 函数名(可选)(参数:可选) {
    函数体
}

假如函数作为表达式的一部分那末该函数就是函数表达式;
建立一个function症结字后没有标识符的函数,这类状况建立的函数叫匿名函数

3.函数声明提拔

剖析器会领先读取函数声明,并使其在实行任何代码之前可接见;而函数表达式则必需比及实行器实行到它地点的代码行,才会真正被诠释实行

sayHello();  //hello
function sayHello(){
    console.log('hello');
}

上述代码不会报错,由于在代码最先实行之前,剖析器就已经由历程一个名为函数声明提拔的历程读取并将函数声明添加到实行环境中

sayHello();  //报错
var sayHello = function(){
    console.log('hello');
}

上述代码会发生毛病,由于函数位于一个赋值语句中,在实行到函数地点语句之前,变量sayHello中不会保留对函数的援用

三、递归

1.什么是递归函数

递归函数是在一个函数中挪用自身所组成的

function recursion(num){
    if(num <= 1){
        return 1;
    }
    else{
        return num * recursion(num-1);
    }
}

上述函数表面上看没什么题目,然则假如做以下操纵就会出题目

var temp = recursion;
recursion = null;
temp(10); //报错:recursion不是个函数

第一句代码temp指向了原始函数,recursion不再指向原始函数,挪用temp(10)的时刻用到了recursion(num-1),然则recursion已不再指向原始函数,所以运转报错

2.arguments.callee

上述状况运用arguments.callee来解决题目,arguments.callee是一个指向正在实行的函数的指针,运用arguments.callee来替代函数名更保险

function recursion(num){
    if(num <= 1){
        return 1;
    }
    else{
        return num * arguments.callee(num-1);
    }
}

3.定名函数表达式

var recursion = (function recur(num){
    if(num <= 1){
        return 1;
    }
    else{
        return num * recur(num-1);
    }
})

上述代码中即使把函数赋值给另一个变量,函数的名字recur依然有用,所以不会影响递归

四、闭包

1.闭包的作用域链

闭包是指有权接见另一个函数作用域中的变量的函数

function createComparisonFunction(propertyName){
    return function(object1,object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if(value1 < value2){
            return -1;
        } else if(value1 > value2){
            return 1;
        } else{
            return 0;
        }
    }
}
var compare = createComparisonFunction('name');
var result = compare({name:'Nicholas'},{name:'Greg'});
compare = null;

在匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化为包括createComparisonFunction()函数的运动对象和全局变量对象。createComparisonFunction()函数实行终了后,其实行环境的作用域链会被烧毁然则它的运动对象不会被烧毁,由于匿名函数的作用域链依然在援用这个运动对象;直到匿名函数被烧毁后,createComparisonFunction()的运动对象才会被烧毁
《JavaScript基本---函数》

2.闭包与变量

闭包只能获得包括函数中任何变量的末了一个值,闭包所保留的是全部变量对象,而不是某个特别的变量

function createFunctions(){
    var result = new Array();
    for(var i = 0; i < 10; i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}
var result = createFunctions();
result.forEach(function(func){
    console.log(func()); //10
})

上述代码每一个函数都邑返回10,由于每一个函数的作用域链中都保留着createFunctions()函数的运动对象,所以它们援用的都是同一个变量i。当createFunctions()函数返回后,变量i的值是10,此时每一个函数都援用着保留变量i的同一个变量对象,所以每一个函数内部的i的值都是10。假如上述代码中的var i改成let i就会涌现预期的结果,由于let声明的变量i属于for轮回块级作用域中不属于createFunctions()函数的运动对象

function createFunctions(){
    var result = new Array();
    for(var i = 0; i < 10; i++){
        result[i] = function(num){
            return function(){
                return num;
            }
        }(i);
    }
    return result;
}
var result = createFunctions();
result.forEach(function(func){
    console.log(func()); //10
})

把i作为参数传给num,使得result数组中的每一个函数都有自身的num变量副本

五、关于this

1.this对象

this对象是在运转时基于函数的实行环境绑定的,在全局函数中this即是window,而当函数作为某个对象的要领挪用时,this即是谁人对象。不过,匿名函数的实行环境具有全局性,因而其this对象一般指向window

var name = "The Window";
var object = {
    name: "The Object",
    getNameFunc: function(){
        return function(){
            return this.name;
        }
    }
}
console.log(object.getNameFunc()()); //The Window
var name = "The Window";
var object = {
    name: "The Object",
    getNameFunc: function(){
        var that = this;
        return function(){
            return that.name;
        }
    }
}
console.log(object.getNameFunc()()); //The Object

2.this绑定划定规矩

默许绑定

最经常使用的函数挪用范例:自力函数挪用

function foo(){
    console.log(this.a);
}
var a = 2;
foo(); //2

上述代码中,this.a被剖析成了全局变量a,这是由于函数挪用时运用了this的默许绑定,因而this指向全局对象。虽然this的绑定划定规矩完整取决于挪用位置,然则只要foo()运转在非严厉形式下,默许绑定才绑定到全局对象;严厉形式下与foo()挪用位置无关,this会绑定到undefined

隐式绑定

function foo(){
    console.log(this.a);
}
var obj = {
    a:2,
    foo:foo
};
obj.foo(); //2

当foo函数被挪用时,挪用位置会运用obj上下文来援用函数,当函数援用有上下文对象时,隐式绑定划定规矩会把函数挪用中的this绑定到这个上下文对象,因而this.a和obj.a是一样的

隐式丧失

一个罕见的this绑定题目就是隐式绑定的函数会丧失绑定对象,从而运用默许绑定划定规矩

function foo(){
    console.log(this.a);
}
var obj = {
    a:2,
    foo:foo
};
var bar = obj.foo; //函数别号
var a = "global";
bar(); //"global"

虽然bar是obj.foo的一个援用,然则实际上它援用的是foo函数自身,因而此时的bar()实际上是一个不带任何润饰的函数挪用,因而运用了默许绑定

显式绑定

运用call,apply以及bind要领举行显现绑定

function foo(){
    console.log(this.a);
}
var obj = {
    a: 2
}
foo.call(obj); //2

new绑定

运用new来挪用函数被称为组织函数挪用,这类函数挪用会自动实行以下操纵:

  • 建立一个全新的对象
  • 这个新对象会被举行原型衔接
  • 这个新对象会绑定到函数挪用的this
  • 假如函数没有返回其他对象,那末new表达式中的函数挪用会自动返回这个新对象
function foo(a){
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); //2

3.推断this

  • 函数是不是在new中挪用(new绑定)?假如是的话,this绑定的是新建立的对象。var bar = new foo();
  • 函数是不是经由历程call,apply,bind(显现绑定)挪用?假如是的话,this绑定的是指定的对象。var bar = foo.call(obj);
  • 函数是不是在某个上下文对象中挪用(隐式绑定)?假如是的话,this绑定的是谁人上下文对象。var bar = obj.foo();
  • 假如都不是的话运用默许绑定。假如在严厉形式下,就绑定到undefined,不然绑定到全局对象。var bar = foo();

参考文章:
深切明白JavaScript系列
JavaScript高等顺序设计
你不知道的JavaScript

    原文作者:SuRuiGit
    原文地址: https://segmentfault.com/a/1190000016296052
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞