Function类型

在ES中,函数实际上是对象,和其他引用类型一样具有属性和方法。
函数名实际上是一个指向函数对象的指针,指向函数而不绑定函数,也就是说一个函数可能有多个函数名。

ES中没有函数重载,如果声明了两个同名函数,那后面的函数会覆该前面的函数。

函数的定义:

//声明式定义函数
function sum(num1,num2){
    ……
}

//函数表达式定义
var sum = function(num1,num2){
    ……
}

两种定义方式几乎一样,唯一的区别是:

解析器在执行环境中加载数据时,解析器会率先读取函数声明,并使其在执行任何代码前可用。
而函数表达式,则必须等到解析器执行到它所在的代码行,才会被解释执行。

例:  

//声明式
console.log(sum(10,10));
function sum(num1,num2){
    return num1 + num2;
}
/* 输出 20 */


//函数式
console.log(sum(10,10));
var sum = function(num1,num2){
    return num1 + num2;
}

/* 输出报错  sum is not a function */

作为值的函数
  1. 把一个函数作为参数传递给另一个函数
function callSomeFunction(someFunction, someArgument){
    return someFunction(someArgument);
}

function add10(num){
    return num + 10;
}

function getGreeting(name){
    return "hello, " + name;
}

var result1 = callSomeFunction(add10, 10);
console.log('result1 = ', result1);

var result2 = callSomeFunction(getGreeting, 'cat');
console.log('result2 = ', result2);

/* 输出 
result1 =  20
result2 =  hello, cat
*/
  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 data = [{name:'Tuanzi', age:'1'}, {name:'Weiwei', age: '0.8'}, {name:'Lanlan', age:'0.4'}];

data.sort(createComparisonFunction('age'));
console.log(data);

/* 输出1:
[ { name: 'Lanlan', age: '0.4' },
  { name: 'Weiwei', age: '0.8' },
  { name: 'Tuanzi', age: '1' } ]
*/


data.sort(createComparisonFunction('name'));
console.log(data);

/* 输出2:
[ { name: 'Lanlan', age: '0.4' },
  { name: 'Tuanzi', age: '1' },
  { name: 'Weiwei', age: '0.8' } ]
*/

注:要访问函数的指针而不调用函数就去掉后面的圆括号,函数名加上圆括号就是使用(调用)函数;


函数内部属性

在函数内部,有两个特殊的对象: ①arguments 和 ②this。

arguments

arguments是一个类数组对象,包含着传入函数中的所有参数。
这个对象还有个callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

//例子1:  

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

console.log(factorial(3));
//输出 6
//例子2:

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

console.log(factorial(3));
//输出 6

从以上两种方法可以看出,他们的结果相同。但最好用例2,因为假设在阶乘函数中,如果和函数名紧紧耦合在一起,那么如果后面遇到函数名变了的情况,就会报错或者运算出错,而使用arguments.callee就可以避免这种情况。

this

this引用的是函数据以执行的环境对象——也可以说是this的值。

global.color = 'red';
var o = {color:'blue'};

function sayColor(){
    console.log(this.color);
}

o.sayColor = sayColor;

sayColor();             //输出 'red'
o.sayColor();           //输出 'blue'

在上例中,sayColor()在全局环境中调用就指向全局执行性环境global,o.sayColor()在o对象中调用就指向o;
this——谁调用函数就指向谁,也可以说this指向当前函数的执行环境。


那在看一个额外的点,涉及到作用域问题,如下例:

global.color = 'red';
var o = {color:'blue'};

function sayColor(){
    color = 'yellow'; //这里相当于一个隐式声明,隐式声明定义的是全局变量,var声明定义的是局部变量
    console.log(this.color);
}

o.sayColor = sayColor;

sayColor();             //输出 'yellow'
o.sayColor();           //输出 'blue'

在函数内容,我新增了个赋值,调用后发现,sayColor的值改变了,o.sayColor输出的值还是没变,开始没想通,后来想通了:
相当于是在函数内声明了个隐式变量,隐式声明定义的是全局变量,var声明定义的是局部变量,因此,这里改变的是全局global的color 值,而o对象中的值没受影响。

注:函数名仅仅是一个包含指针的变量,即使不同的环境中执行,它们指向的仍是同一个函数


函数的属性和方法

每个函数都包含两个属性:①length ②prototype

length

表示函数希望接收的命名参数的个数

function sayName(name){
    console.log(name)
}

function sum(num1, num2){
    return num1 + num2;
}

function sayHi(){
    console.log('hi~');
}


console.log('sayName: ', sayName.length);
console.log('sum: ', sum.length);
console.log('sayHi: ', sayHi.length);


/* 输出 
sayName:  1
sum:  2
sayHi:  0
*/

prototype

对于ES中的引用类型而言,prototype是保存它们所有实例方法的真正所在。例如toString()vualeOf()等方法都保存在prototype名下。


每个函数都包含两个非继承而来的方法:①apply() ②call()
用途: 在指定的作用域中调用函数,实际上等于设置函数体内this对象的值

apply()
接收两个参数: ①在其中运行函数的作用域; ②参数数组(可以试array实例,也可以是arguments对象);

//第二个参数的使用例子

function sum(num1,num2){
    return num1 + num2;
}

function callSum(num1,num2){
    return sum.apply(this, arguments);
}

function callSum2(num1,num2){
    return sum.apply(this, [num1,num2]);
}

console.log('callSum = ', callSum(5,5))
console.log('callSum2 = ', callSum2(5,5))

/* 输出 
callSum =  10
callSum2 =  10
*/

从上例可以看出,在传入第二个参数时,无论传入arguments对象还是一个参数数组,两个函数都会正常执行并返回正确结果。

call()

call()的作用和apply()相同,他们唯一的区别是:apply()的第二参数可接收一个参数数组或者arguments对象,而call()的第二参数需要逐个列出来, 例:

//call() 和 apply() 的使用区别  

function sum(num1,num2){
    return num1 + num2;
}
function callSum3(num1,num2){
    return sum.call(this, num1,num2);
}

console.log('callSum3 = ', callSum3(5,5));

/* 输出 
callSum3 =  10
*/

对于call()方法来说,假设你第二个参数传入了一个数组,那么,它只会把这个数组当做第二个参数的第一个元素,剩下的如果没有传入,则会输出undefined

使用 apply() 还是使用 call() ?

取决你采用哪种函数传递参数的方式最方便。
如果你打算直接传入arguments或者接受一个参数数组则用 apply()
否则就用call()


apply()call() 最大的用途是扩充函数赖以运行的作用域:

window.color = 'red';
var o = {color: 'blue'};

function sayColor(){
    console.log(this.color);
}

sayColor.call(this);
sayColor.call(window);
sayColor.call(o);

/* 输出 
red
red
blue
*/

这样就可以直接扩展作用域。
这里需要注意的是: 如果在node中全局调用,由于node和ES6,this返回的当前模块,所以这是this会返回undefined。

global.color = 'red';
var o = {color: 'blue'};

function sayColor(){
    console.log(this.color);
}

sayColor.call(this);
sayColor.call(global);
sayColor.call(o);

/* 输出 
undefined
red
blue
*/
原文地址:https://www.cnblogs.com/mlcat/p/11269036.html