一、Function类型总结
1、函数实际上是对象。
每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。函数是一个指向函数对象的指针。
2、函数没有重载。
3、使用 函数声明语法 定义,可以在代码开始前执行。使用 函数表达式 必须等到解释器执行到它所在的代码行,才会真正被解释执行。
4、函数名本身就是变量,所以函数可以作为值来使用。
5、在函数内部,有两个特殊对象:arguments 和 this。
arguments:类数组对象,包含着传入函数中的所有参数。
this:this引用的是函数执行的环境对象。
6、每个函数包含两个属性 length 和 prototype。
length:表示函数希望接收的命名参数的个数。
prototype:保存引用类型所有实例方法的真正所在。
7、每个函数都包含两个非继承而来的方法:apply() 和 call()。
用途:在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。能够扩充函数赖以运行的作用域。
好处:使用call()和apply()扩充作用域,对象不需要与方法有任何耦合关系。
函数.apply( 运行函数的作用域 , 参数数组 )
函数.call( 运行函数的作用域 , 参数必须逐个列举出来 )
8.ES5还定义了一个方法:bind() 。
bind():该方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。
用途:主要用于事件处理程序以及setTimeout()和setInterval()。
注意:被绑定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所以最好只在必要时使用。
windows.color = "red";
var o = { color: "blue"};
function sayColor(){
console.log(this.color);
}
var objectSayColor = sayColor.bind(o); //注意这里返回的是一个函数
objectSayColor();
二、函数的扩展
1、函数参数的默认值
①基本用法:直接写在参数后面。
function log(x,y='world'){
console.log(x,y);
}
log('Hello'); //=>Hello world
log('Hello','yujinhong'); //=>Hello yujinhong
log('Hello','');// =>Hello
②与解构赋值默认值结合使用
function foo({x,y=5}){
console.log(x,y);
}
foo({}); //=>undefined 5
foo({x:1}); //=>1 5
foo({x:1,y:2}); //=>1 2
foo(); //=>Uncaught TypeError: Cannot destructure property `x` of 'undefined' or 'null'.
③参数默认值的位置
通常情况下,定义了默认值的参数应该是函数的尾参数。
④函数的length属性
函数的length属性返回没有指定默认值的参数个数。如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数。
⑤作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为在不设置参数默认值时是不会出现的。
⑥应用
利用参数默认值可以指定某个参数不得省略,如果省略就抛出一个错误。
function throwIfMissing(){
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()){
return mustBeProvided;
}
console.log(foo()); //=>Uncaught Error: Missing parameter at throwIfMissing
2、rest参数
ES6引入了rest参数(形式为:“...变量名”),用于获取函数多余的参数,这样就不需要arguments。
function add(...values){
let sum = 0;
for(var val of values){
sum += val;
}
return sum;
}
console.log(add(2,3,3)) //=>8
注意:rest参数后不能有其他参数,否则报错。
3、严格模式
ES6规定只要函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能显示设定为严格模式,否则报错。
规避方法:①设定全局性的严格模式。②把函数包在一个无参数的立即执行函数里面。
4、name属性
函数的name属性返回该函数的函数名。ES5返回空字符串,ES6返回实际的函数名。
Function构造函数返回的函数实例,name属性的值为anonymous。
bind返回的函数,name属性值会加上bound前缀。
5、箭头函数
①基本用法:
A、有一个参数,箭头函数的代码块部分只有一条语句
var f = v => v;
B、没有参数或需要多个参数,就要使用圆括号代表参数部分
var f = () => 5;
var sum = (num1,num2) => num1+num2;
C、如果箭头函数的代码块部分多于一条语句,就要使用大括号将其括起来,并使用return语句返回
var sum = (num1,num2) => {return num1+num2};
D、如果箭头函数直接返回一个对象,必须在对象外面加上括号
var getTempItem = id => ({id: id,name: "yujinhong"});
②注意事项
A、函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象。
B、不可以当作构造函数。也就是说,不可以使用new命令,否则会抛出一个错误。
C、不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数。
D、不可以使用yield命令,因此箭头函数不能用作Generator函数。
E、箭头函数没有自己的this,不能用call()、apply()、bind()这些去改变this的指向。
③箭头函数内部还可以使用箭头函数。
6、this绑定
箭头函数可以绑定this函数,大大减少了显示绑定this对象的写法(call、apply、bind)。
ES7提出“函数绑定”运算符并排的双冒号(::)。
7、尾调用
①尾调用(Tail Call)是函数式编程的一个重要概念,指某个函数的最后一步是调用另一个函数。
②尾调用优化:只保留内存函数的调用帧。
③尾递归:尾调用自身就叫尾递归。(尾递归永远不会发生“栈溢出”错误)。
④递归函数的改写:把所有用到的内部变量改写成函数的参数,即可将递归函数改写成尾递归。
为直观的可采用的方法有:A、在尾递归函数之外再提供一个正常形式的函数 B、柯里化 C、采用ES6的函数默认值
⑤ES6的尾调用优化只在严格模式下开启,正常模式下无效。
⑥正常模式下尾递归优化的实现:采用“循环”替换“递归”。
8、函数参数的尾逗号
ES2017允许函数的最后一个参数有尾逗号。