JavaScript函数(二)

目录

1.函数的声明

2.函数的属性和方法

3.函数的作用域

4. 函数的重载

5.闭包知识点

6.小结


关于函数,可以从以下3个方面去理解:
首先,数据类型上看:函数在JavaScript中是一种数据类型,是对象的一种;
其次,从功能上看:函数本质上是一段反复调用的代码块;
最后,从地位上看:函数在JavaScript中和其他基本数据类型一样,可以作为参数和赋值,是“第一等公民”


1.函数的声明

声明函数的方式有三种:

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,否则返回定义的值;函数会在执行完return语句后就立即退出,位于return语句的代码将不会执行;

var a  = function(){
    console.log(1)
}
var b = function(){
    return 2;
}
a() === undefined//true

2.函数的属性和方法

函数作为一个对象,同样拥有属性和方法,下面主要归纳一下比较常用和重要的属性和方法:


  • name属性:返回该函数名的字符串;
  • length属性:返回形参的个数,即预期传入参数的个数;
function fn (a,b){
    console.log(fn.name)
    console.log(fn.length)
}
fn()
//"fn"  
//2
  • arguments对象:是包含传入函数实参的类数组对象,只有在函数执行阶段并且存在参数才会有值,未调用函数是为null
function fn (a,b){
  console.log(arguments)
}
fn(1,2)
//[1,2]


arguments对象的length属性代表实参的个数,注意和函数的length属性的区别,函数的length代表形参的个数,arguments的length属性代表实参个数;
arguments对象有一个callee属性,返回arguments对象所在的函数指针

可以利用callee实现函数的递归,例如累加或阶乘操作:

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不是函数;

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方法

函数提供callapplybind3种方法可以改变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

更多this的相关介绍,详见【what's this???】

3.函数的作用域

作用域指的是变量存在的范围,作用域可分为全局作用域和局部作用域,变量在全局范围可访问到;局部作用域由函数所构造,变量只能在函数内部可访问到;

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

4.函数的重载

JavaScript的函数无法实现重载,所谓重载是为同一个函数编写两个不同的定义签名(接受的参数的类型和数量);
JavaScript函数之所以没有重载,是因为其参数受包含零个或多个值的arguments对象表示的,也就是说函数不存在函数签名的特性;

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

上述代码声明了同一个函数,虽然二者的函数签名不同,但是js引擎在解析时会把后面的函数覆盖前面的函数;
实际上,在JavaScript中参数在内部是用arguments对象存储,它不介意你定义时设定1个、2个或多个参数,它只在意函数在执行阶段时传递的实际参数;



函数定义时设定参数唯一目的是为了对开发者编写代码和维护更加友好,即使你不定义函数签名照样可以引用参数;



但是,一般在定义函数时是建议命名形参的,这样更加符合编程规范;
可以利用JavaScript函数的以上特性,模拟出函数的重载功能;
function fn(){
  if(arguments.length === 1){
      return arguments[0]
  };
  if(arguments.length ===2){
      return arguments[0]+arguments[1]    
  }
}
fn(5);
fn(5,10)

5. 闭包知识点

关于闭包的知识点,将会单独开一章节详谈,具体请看《JavaScript闭包(三)》


6.小结

通过《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的函数是无法实现重载功能的,但可以通过函数的结合arguments对象的特性模拟出重载的效果;

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容