10.3 函数特性
10.3.1 函数声明与函数表达式 函数声明和函数表达式的主要区别就在于是否会进行函数声明提升(function
函数声明会进行函数声明提升
函数声明提升 即 函数声明会在任何代码执行之前先被读取并添加到执行上下文。
1 2 3 4 console .log (sum (1 , 2 )); function sum (num1, num2 ) { return num1 + num2; }
函数表达式 必须要等到代码执行到那一行才会执行函数定义
这和let还是var声明无关。
1 2 3 4 5 console .log (sum (10 , 20 )); let sum = function (num1, num2 ) { return num1 + num2; }
10.3.2 函数作为值 因为函数名本身就是变量 ,因此函数可以作为参数 和返回值 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function callSomeFunction (someFunction, someArgument ) { return someFunction (someArgument); } function add10 (num ) { return num + 10 ; } console .log (callSomeFunction (add10, 10 )); function getGreeting (name ) { return 'Hello, ' + name; } console .log (callSomeFunction (getGreeting, 'Nicholas' ));
从一个函数返回另一个函数可以非常有用
下面的程序中创建了一个可以按照对象数组中任意对象属性对数组进行排序的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function createComparisonFunction (propertyName ) { return function (object1, object2 ) { let value2 = object2[propertyName]; let value1 = object1[propertyName]; if (value1 < value2) { return -1 ; } else if (value1 > value2) { return 1 ; } else { return 0 ; } }; } let data = [ { name : 'Zachary' , age : 28 }, { name : 'Nicholas' , age : 29 } ]; data.sort (createComparisonFunction ('name' )); console .log (data[0 ].name );
10.3.3 函数内部
arguments
arguments.callee 是一个指向arguments对象所在函数的指针。
经典阶乘函数
1 2 3 4 5 6 7 8 function factorial (num ) { if (num <= 1 ) { return 1 ; } else { return num * factorial (num - 1 ); } } console .log (factorial (10 ));
使用arguments.callee重写迭代
这样的话就可以让函数逻辑与函数名解耦,解决了硬编码带来的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function factorial (num ) { if (num <= 1 ) { return 1 ; } else { return num * arguments .callee (num - 1 ); } } console .log (factorial (5 )); let trueFactorial = factorial;factorial = function ( ) { return 0 ; } console .log (trueFactorial (5 )); console .log (factorial (10 ))
this
标准函数中的this
引用的是把函数当成方法调用的上下文对象, 全局上下文中调用函数,this指向 windows 。
1 2 3 4 5 6 7 8 9 10 window .color = 'red' ;let o = { color : 'blue' }; function sayColor ( ) { console .log (this .color ); } sayColor (); o.sayColor = sayColor; o.sayColor ();
箭头函数中的this
引用的是定义箭头函数的上下文。
1 2 3 4 5 6 7 8 9 10 window .color = 'red' ;let o = { color : 'blue' } let sayColor = ( ) => { console .log (this .color ); } sayColor (); o.sayColor = sayColor; o.sayColor ();
因此在使用事件回调 或定时回调 时, 为了保证回调函数内this值指向定义函数的上下文, 常使用箭头函数定义回调函数。
1 2 3 4 5 6 7 8 9 10 function King ( ) { this .royaltyName = 'Henry' ; setTimeout (() => console .log (this .royaltyName ), 1000 ); } function Queen ( ) { this .royaltyName = 'Elizabeth' ; setTimeout (function ( ) { console .log (this .royaltyName ); }, 1000 ); } new King (); new Queen ();
caller
caller 引用的是调用当前函数的函数 。
1 2 3 4 5 6 7 function outer ( ) { inner (); } function inner ( ) { console .log (inner.caller ); } outer ();
为了降低耦合度,可以引用arguments.callee.caller 。
1 2 3 4 5 6 7 function outer ( ) { inner (); } function inner ( ) { console .log (arguments .callee .caller ); } outer ();
严格模式下
访问arguments.callee 会报错 不能给caller 属性赋值
new.target(ES6)
用于检测函数是否是使用new关键字调用的。
正常调用 new.target = undefined new关键字调用 new.target = 被调用的构造函数 1 2 3 4 5 6 7 8 function King ( ) { if (!new .target ) { throw 'King must be instantiated using "new"' } console .log ('King instantiated using "new"' ); } new King (); King ();
10.3.4 函数属性与方法 函数固有属性(2个)
length length保存函数定义的命名参数个数 。
1 2 3 4 5 6 7 8 9 10 11 12 function sayName (name ) { console .log (name) } function sum (num1, num2 ) { return num1 + num2; } function sayHi ( ) { console .log ('hi' ) } console .log (sayName.length ) console .log (sum.length ) console .log (sayHi.length )
prototype prototype用于保存引用类型所有实例方法 , 在自定义类型时特别重要。 注意prototype不可枚举。
函数方法(apply,call,bind)
这3种方法都是用于 设置调用函数时函数体内this对象的值 。
strict模式下,没有指定上下文的调用函数,函数的this不会指向window,而会变成undefined
apply() 接收2个参数:
param1 指定thisparam2 参数数组1 2 3 4 5 6 7 8 9 10 11 function sum (num1, num2 ) { return num1 + num2; } function callSum1 (num1, num2 ) { return sum.apply (this , arguments ); } function callSum2 (num1, num2 ) { return sum.apply (this , [num1, num2]); } console .log (callSum1 (10 , 20 )); console .log (callSum2 (20 , 30 ));
call() call方法和apply的不同之处在于:需要传递的参数是逐个传递的
1 2 3 4 5 6 7 8 function sum (num1, num2 ) { return num1 + num2; } function callSum (num1, num2 ) { return sum.call (this , ...arguments ); } console .log (callSum (10 , 20 ));
apply和call最强大就是控制函数调用上下文this的能力。
1 2 3 4 5 6 7 8 9 10 11 window .color = 'red' let o = { color :'blue' } function sayColor ( ) { console .log (this .color ) } sayColor ();sayColor.call (this ); sayColor.call (window ); sayColor.call (o);
bind(ES5)
bind和call、apply的区别在于bind会创建一个绑定了指定this的函数实例 。
1 2 3 4 5 6 7 8 9 window .color = 'red' ;var o = { color :'blue' } function sayColor ( ) { console .log (this .color ); } let objectSayColor = sayColor.bind (o);objectSayColor ();
提示
toLocaleString() 和 toString() 始终返回函数代码, 但是返回代码的具体格式会因浏览器而异, 因此应该在重要功能是避免依赖这些方法的返回值。
10.3.5 函数表达式 1 let functionName = function (arg1, arg2, arg3 ) { };
像这样创建的函数叫做 匿名函数(anonymous function) , 有时也被称为兰姆达(λ)函数
函数声明提升案例
使用函数声明会进行声明提升,如下面这段危险的代码:
1 2 3 4 5 6 7 8 9 10 11 let condition = true ;if (condition) { function sayHi ( ) { console .log ('Hi!' ) } } else { function sayHi ( ) { console .log ('Yo!' ) } } sayHi ()
也许浏览器会帮你纠正这段声明, 关键在于不同浏览器纠正问题的方式不一致, 所以尽量不要这么写。