ES5之前的函数声明及表达式
1,函数的声明
function fn(){
dosomething;
};
2,函数表达式
var fn = function(){}
//也可以var fn = function newFn(){},此时的newFn只是这个匿名函数的名字,和变量没有关系;
3,构造函数
var fn = new Function('num1','num2','return num1 ,num2') //这种方法会运行两次代码
//先实例后执行函数体
ES5数的默认值
function fn(arg1,arg2){
arg2 = arg2 || 'the value'; //如果没有传入参数就使用默认值,利用||的短路特性
return arg1+arg2;
};
fn('hello') ->hello the value;
fn('hello','world') ->hello world;
fn('obama',"")->obama the value //传入的参数为falsey值会忽略
typeof arg2 === 'undefined' //判断是否传参,如果没有实参的话,就是undefined;
arg2=(typeof arg2 !=='undefined')?arg2:'the value';//判断是否有传入值
arguments.length ===1 //判断传递的参数个数
ES6的参数默认赋值
function fn(arg1,arg2 = 'value'){
let arg2 = 34; //此处的arg2 默认是已经声明,所以内部使用let const会语法报错!
console.log(arg1,arg2)
}
//内部的arguments对象也默认和严格模式一样的行为,不在动态的反应变化;默认值不计入arguments对象
//函数的参数,也可以是其他值,默认参数只有在调用的时候才会求值,因此可以用第一个参数作为默认的值
function fn(a , b =a){};
//参数的作用域和ES6之前的一样,和其他的参数都是同一作用域,如果参数的值是一个变量,那么这个参数的作用域先是局部(确认是否也是形参)后是全局;
let a = 34;
function fn(b = a){ //尤其注意,参数b赋值的是变量a,那么就默认变量a已经声明,此时的a,如果不是参数,就是全局的变量,如果是参数,就会报错,重复赋值,使用let和const要注意语法错误
let a= 11;
console.log(b);
}
fn() ->34 //当参数b赋值变量a的时候,a是全局注册的,因此,fn()是34,如果没有let a = 34;就会报错,
fn(22) ->22 //此时参数的作用域是局部
let a = 23;
function (a,b=a){ //参数b的值是a,相当于再次给a赋值,由于参数是函数内部变量,所以和 let a = 12冲突;会报错
let a = 12;
console.log(b);
}
function fn(arg1,arg2){
console.log(arg1 === arguments[0]); //true
console.log(arg2 === arguments[1]); //true
arg1 = newarg1;
arg2 = newarg2;
console.log(newarg1 === arguments[0]); //true
console.log(new arg2 === arguments[1]); //true
}
rest参数(...rest)不定参数;只能放在形参的最后面,否则会报错,这是一个数组对象
function fn(...value){
console.dir(value);
value.slice() //可以使用数组的方法,区分arguments
}
fn(1,2,3,4,4)->[1,2,3,4,4]返回的是数组,可以使用数组的方法;
//无论形参是否有不定参数,arguments对象正常使用,反应传入的参数的数量
...扩展运算符 把运算符后面的数组,转为用逗号分开的参数序列;可以用在所有使用数组的的方法中
console.log(1,2,3,4) //1 2 3 4;
console.log(...[1,2,3,4])//同上
//利用call的属性,转化数组
判断函数的调用,是直接调用还是new
function A(a){this.a = a;}
var b = new A(3); //生成了对象
var c = A(4); //此时给全局属性添加了a=4
function A(a){
if(this instanceof A){this.a =a} //判断是否是new调用
else{return new A(a)}
}
var d = A.call(b,333) //通过call强制的指定this
//正确的写法
function B(b){
if(typeof new.target ! == 'undefined'){this.b =b} //新的元属性
else{return new B(b)};
}
//es6可以在块级代码中,声明和使用函数,但是会作为全局函数
箭头函数 =>
//当函数没有参数时;
var f1 = function(){ };//默认函数返回一个undefined;
var f1 = ( )=>{}; //空函数
var f2= ()=>'the value you want';
var f3 = ()=>{ //如果需要在函数体内执行多条代码,就必须用大括号包裹
doSomethings;
doantherthings;
return value; //必须用return 返回值;否则默认返回undefined;
}
//箭头函数有一个函数的时候
var f1 = function(arg1){
};
var f1 = arg1 =>undefined;
//箭头函数有多个参数的时候;
var f1 = function (arg1,arg2,arg3){
};
var f1 = (arg1,arg2,arg3)=> undefined;
var f2 = (arg1,arg2,arg3)=>arg1-arg2-arg3;
//注意如果箭头函数只是单独的返回一个对象要在大括号的外面加个小括号;
var f1 = ()=>{
doSomething;
return { //此时不是单独返回所以不需要
name : 'name',
value : 'value'
}
};
var f2 = ()=>({ //此时为了区分{}是否是函数体,所以要加上小括号;
name : 'name',
value : 'value'
})
//箭头函数的函数体内正常和一般函数一样,但是没有arguments数组对象,
//IIFE关于立即执行的函数
var f1 = (function(arg){
return {
getArg :function(){return arg}
}
})(arg) //返回对象的方法
var f1 = (arg=>{
return {
getArg :function(){return arg}
}
})('arg') //同上;
箭头函数的this指向问题 (this仅仅是外一层非箭头函数的函数this,否则就是undefined)
一般函数的this是根据调用的对象来指定的或者用new ,bind ,call 等操作符或者方法来绑定;
1,默认的this 指向
function fn(){console.dir(this)} //此时默认的指向是window
2,隐式的this 指向
var obj = {
name : 'obama',
getName : function(){
return this.name //此时的指向是对象
}
}
3,new操作符
function Name(name){
this.name = name;
}
var man = new Name('obama'); //此时指向的是new调用的对象
4,硬指定的this
var getName= function(){
console.log(this.name)
}
var name = 'bush';
var predents = {
name : 'obama',
}
getName(); //bush
getName.call(preddents) //obama;
ES6的箭头函数this是和变量的作用域一样的词法指定(写在哪里就是那里的引用),不能更改,也不可以用new操作符,本着用完即丢的思想
var a = ()=>this; //此时返回的是this对象是undefined;
var obj = {age : 45};
obj.b = a; //把函数a当作obj的方法;
obj.b() //正常不使用箭头函数的话, this的指向应该是调用的obj对象,但是这里显示的 是原来定义的window对象
a.call(obj) //显试的绑定,也不能改变this 的作用域;
var newa = new a() //报错,a不是一个构造函数;
如果要使用箭头函数的绑定的话在ES7里双冒号::左边是对象,右边的是方法,这样就可以绑定,
var foo = {age : 34};
var bar = ()=>this.age;
foo::bar
//以上仅作了解
尾调用优化特指函数的最后一步(一般函数最后一步默认是return undefined)这一步不能有其他任何的操作,比如赋值,算术运算 属于引擎的优化
一般JS引擎 在执行中,会进入执行上下文,全局,函数和eval上下文,三者中的一个,
保存这个上下文的地方就是在调用栈,栈的顶端就是一个变量对象,保存当前函数的使用的变量;
函数调用会在内存形成一个调用记录称之为调用帧,保存位置和内部变量,所有的调用帧就形成了一个调用栈;
尾调用因为是函数的一步,所以返回这个函数后,原函数就不必记住自己的变量,尾调用的函数会保存这个信息,所以内层的函数取代外部的函数,这样就行成优化
function fn(){
let a = 1;
let b = 2;
return f2(a + b);
}
fn();
f2(3) //在函数fn里f2是尾调用,fn执行后,就不必记住自身可调用的变量,因为f2会记住执行;最终取代原函数;
function fn(num){ ///一般递归函数
if(num ===1){return 1};
return num+fn(num-1)
}
注意只有使用use strict 尾调用优化才会生效,而且argument 和caller 不能使用
function fn(n,total=1){ //尾调用优化
if(n ===1) return total;
return fn(n-1,n+total)
}
//柯里化,将多参数的函数层层的使用单参数的函数调用
function add(n){
return curry(n,1)
};
function curry(n,total){
if(n ===1) return total;
return curry(n-1,n+total)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。