关于函数,能够从如下3个方面去理解:
首先,数据类型上看:函数在JavaScript中是一种数据类型,是对象的一种;
其次,从功能上看:函数本质上是一段反复调用的代码块;
最后,从地位上看:函数在JavaScript中和其余基本数据类型同样,能够做为参数和赋值,是“第一等公民”javascript
声明函数的方式有三种:java
1.声明式 function fn(){ console.log(1) } fn()//1
2.表达式 var fn = function(args){ console.log(args) } fn(a)//a
3.构造函数式 var fn = new Function("arg1","arg2","return arg1+arg2") fn(1,2)//3
函数本质上是对象的一种,因此函数名保存的其实是指向函数对象的指针,第3种构造函数的方法对于理解“函数是对象,函数名是指针”的概念更加直观,可是第3种方法解析效率较低并且书写不简洁,因此通常不用构造函数方法去声明函数;
当声明式和表达式同时声明同一个函数时,表达式会覆盖声明式,缘由是函数做为变量在js解析阶段进行变量提高,声明式和表达式都会提高至当前做用域头部,而后表达式会从新为fn赋值,从而覆盖声明式定义的函数;数组
function fn(){ console.log(1) } var fn = function(){ console.log(2) } fn()//2
关于return语句的理解,若是声明的函数没有return语句,则默认return undefined,不然返回定义的值;浏览器
var a = function(){ console.log(1) } var b = function(){ return 2; } a() === undefined//true
函数做为一个对象,一样拥有属性和方法,下面主要概括一下比较经常使用和重要的属性和方法:闭包
name属性:返回该函数名的字符串;app
length属性:返回形参的个数,即预期传入参数的个数;函数
function fn (a,b){ console.log(fn.name) console.log(fn.length) } fn() //"fn" //2
arguments对象:是包含传入函数实参的类数组对象,只有在函数执行阶段而且存在参数才会有值,未调用函数是为null
;this
function fn (a,b){ console.log(arguments) } fn(1,2) //[1,2]
arguments对象的length属性表明实参的个数,注意和函数的length属性的区别,函数的length表明形参的个数,arguments的length属性表明实参个数;
arguments对象有一个callee属性,返回arguments对象所在的函数指针;
能够利用callee实现函数的递归,例如累加或阶乘操做:spa
function increment(arg){ if(arg === 1){ return 1 } return arg+arguments.callee(arg-1) } function increMultipler(arg){ if(arg === 1){ return 1 } return arg*arguments.callee(arg-1) }
这里另外提一个函数的caller属性,该属性保存调用当前函数的函数的引用,注意的是若是在全局做用域下读取该属性,值为null,由于顶层对象在浏览器中为window不是函数;prototype
function outer(){ inner(); } function inner(){ console.log(arguments.callee.caller) } outer();
this对象:表明函数执行时的环境对象,简单的说就是谁调用了该函数,this的指向是动态的,只有在函数调用时this对象才能肯定;
//在浏览器全局环境下,即window对象下 var print = function(){ console.log(this) } print()//this指向Window,由于这是Window对象调用了print方法 //在特定对象的环境下 var o = { print: function(){ console.log(this) } } o.print()//this指向o,由于这是o对象调用print方法
函数提供call
、apply
和bind
3种方法能够改变this对象;
1.call方法 function fn(){ return this } var o = {} fn() === this//true,this指向window对象 fn.call(o) === o//true,this指向o对象 //call方法还能够传入参数; function add(x,y){ return x+y } add.call(null,1,2)
2.apply方法,与call不一样的是apply传入的参数为数组 var arr = [1,2] function add(x,y){ return x+y } add.apply(null,arr)
实际上,apply和call的区别只在于传递参数的不一样,它们真正强大的地方在于可以扩充函数赖以运行的做用域,好比slice函数本来只存在于数组当中,当中经过使用call方法,能够实现不一样做用域下调用该方法;
function fn(a,b){ console.log(Array.prototype.slice.call(arguments)) } fn(1,2)//[1,2]
bind方法会建立一个函数实例,并将该函数的this对象绑定到传入该方法的参数;
function fn(){ return this } var o = {} var newFn =fn.bind(o) newFn() === o//true
做用域指的是变量存在的范围,做用域可分为全局做用域和局部做用域,变量在全局范围可访问到;局部做用域由函数所构造,变量只能在函数内部可访问到;
var a =1//a处于全局做用域 function fn(){ var a = 2//a处于局部做用域,外部没法访问; return a; } a//1 fn()//2
值的注意的是,函数执行时所在的做用域是定义时所在的做用域,而不是调用时所在的做用域;
var a = 1; function fn(){ console.log(a) } function fn2(){ var a = 2; fn() } fn2()//1
关于闭包的知识点,将会单独开一章节详谈,具体请看《JavaScript闭包(三)》
经过《JavaScript函数(二)》,咱们大体了解关于函数的知识点以下:
函数本质上是一段反复调用的代码块,是对象的一种,在js中做为“第一等公民”,能够赋值和传参;
函数声明的方法有3种:声明式、表达式和构造函数式;其中构造函数是可以直观理解函数的“函数是对象,函数名是指针”的概念;当表达式和声明式同时声明同名函数时,表达式会覆盖声明式,缘由是变量提高的做用;
函数做为对象,一样具备属性和方法;name
返回该函数名的字符串,length返回形参的个数;
arguments对象是包含传入函数的实参的类数组对象,只有在执行阶段该对象才有值,未调用函数时为null
,arguments.length表示实参的个数,arguments.callee返回arguments对象所在的函数的指针;
函数的caller返回调用当前函数的函数的指针,在全局做用域下读取该属性为null;
this对象表明当前函数执行时的环境对象,this对象只有在函数执行阶段才能肯定;
可使用call、apply和bind改变this的指向;call和apply的区别在于两者传递的参数不一样,call为零散的数据,apply为数组,两者最大的用处是扩展函数的做用域;bind方法能够返回一个函数的实例,并绑定this对象至传入bind的参数;
函数能够开辟一个独立的做用域,这使得js当中经典的闭包得以实现提供可能性;此外,函数执行时所在的做用域是在定义时所在的做用域,而不是调用时所在的做用域;
《JavaScript高级程序设计(第3版)》