深入学习javascript函数系列之一——函数概述
2017-03-30 00:23
267 查看
目前对编程函数的理解只是停留在函数可以执行一些操作,虽说也能写出一些方法,但是缺少系统的认识,今天先初步深入一下,虽说也不能完全理解,总归是比以前有了更进一步的认识。相信不断的深入,会有更多的收获。
函数的定义
总共有3中函数的定义:
1:函数声明
使用function关键字,后面接上参数和函数体,
funcname是要声明的函数名称的标识符。函数名之后的圆括号中是参数列表,参数之间使用逗号分隔。当调用函数时,这些标识符则指代传入函数的实参。
[注意]function语句里的花括号是必需的,这和while循环和其他一些语句所使用的语句块是不同的,即使函数体内只包含一条语句,仍然必须使用花括号将其括起来。
提升
函数名称和函数体都提升。
上面这个代码片段之所以能够在控制台输出1,就是因为foo()函数声明进行了提升,如下所示:
重复
变量的重复声明是无用的,但是函数的重复声明会覆盖前面的声明。
和变量的声明一样,函数的声明语句创建的变量是无法删除的。
函数定义表达式
以表达式的方式定义函数,函数的名称是可以省略的,
匿名函数也叫兰姆达函数,书function后面没有表示符的函数, 通常而言,以表达式方式定义函数时都不需要名称,这会让定义它们的代码更加紧凑。函数定义表达式特别适合用来定义那些只会使用一次的函数。
而一个函数定义表达式包含名称,函数的局部作用域将包含一个绑定到函数对象的一个名称,实际上函数的名称将会成为函数内部的一个局部变量。
个人理解,对于具名的函数表达式来说,函数名称相当于函数对象的形参,只能在函数内部使用;而变量名称相当于函数对象的实参,在函数内部和函数外部都可以使用
函数定义了一个非标准的name属性,通过这个属性可以访问到给定函数指定的名字,这个属性的值永远等于跟在function关键字后面的标识符,匿名函数的name属性为空
Function构造函数
Function构造函数可以接受任意数量的参数,但是最后一个参数都被看成一个函数整体,而前面的参数则是枚举出了新函数的参数。
注意的是Function无法指定函数名称,所以只能是匿名函数。
Function()构造函数创建的函数,其函数体的编译总是会在全局作用域中执行。于是,Function()构造函数类似于在全局作用域中执行的eval()
并不是所有的函数都可以成为构造函数
并不是函数中return语句后的所有语句都不执行,finally语句是例外,return语句不会阻止finally子句的。
[注意]由于javascript可以自动插入分号,因此在return关键字和它后面的表达式之间不能有换行。
一个函数中可以有多个return语句。
return语句可以单独使用而不必带有expression,这样的话也会向调用程序返回undefined。
return语句经常作为函数内的最后一条语句出现,这是因为return语句可用来使函数提前返回。当return被执行时,函数立即返回而不再执行余下的语句。
如果函数调用时在前面加上了new前缀,且返回值不是一个对象,则返回this(该新对象)。
函数的调用
只有函数被调用时,才会执行。调用运算符是跟在任何产生一个函数值的表达式之后的一对圆括号,圆括号内可包含零个或多个用逗号隔开的表达式。每个表达式产生一个参数值,每个参数值被赋予函数声明时定义的形参名。
1:函数调用模式
当一个函数并非一个对象的属性时,那么它就是被当做一个函数调用的。对于普通的函数来说,函数的返回值就是调用函数的表达式。
使用函数调用模式调用函数时,非严格模式下,this被绑定到全局对象;在严格模式下,this是undefined。
重写
因为函数调用模式的函数中的this绑定到全局对象,所以会发生全局属性被重写的现象
2:方法调用模式
当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。如果调用表达式包含一个提取属性的动作,那么它就是被当做一个方法来调用。
方法可以使用this访问自己所属的对象,他能从对象中取值,或者是对对象进行比较,this到对象的绑定发生在函数调用的时候,,通过this可以取得所属上下文的方法称之为公共方法。
任何函数只要作为方法调用实际上都会传入一个隐式的实参——这个实参是一个对象,方法调用的母体就是这个对象,通常来讲,基于那个对象的方法可以执行多种操作,方法调用的语法已经很清晰地表明了函数将基于一个对象进行操作.
假设上面两行代码的功能完全一样,它们都作用于一个假定的对象rect。可以看出,第一行的方法调用语法非常清晰地表明这个函数执行的载体是rect对象,函数中的所有操作都将基于这个对象
和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用,其this的值指向调用它的对象。如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下).
如果想访问这个外部函数的this值,需要将this的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。通常使用变量self或that来保存this.
3: 构造函数调用模式
如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。
如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内。
如果构造函数没有形参,javascript构造函数调用的语法是允许省略实参列表和圆括号的。凡是没有形参的构造函数调用都可以省略圆括号。
尽管构造函数看起来像是一个方法调用,依然会使用新对象作为调用上下文,如下。
构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值.
如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果.
如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象.
间接调用模式
javascript中函数也是对象,函数对象也可以包含方法。call()和apply()方法可以用来间接地调用函数
这两个方法都允许显式指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。两个方法都可以指定调用的实参。call()方法使用它自有的实参列表作为函数的实参,apply()方法则要求以数组的形式传入参数。
啊,真有感觉,读一遍有一边的感觉,过段时间再读一遍,相信还会有不一样的收获。
函数的定义
总共有3中函数的定义:
1:函数声明
使用function关键字,后面接上参数和函数体,
function funcname([arg1 [,arg2 [...,argn]]]){ statement; }
funcname是要声明的函数名称的标识符。函数名之后的圆括号中是参数列表,参数之间使用逗号分隔。当调用函数时,这些标识符则指代传入函数的实参。
[注意]function语句里的花括号是必需的,这和while循环和其他一些语句所使用的语句块是不同的,即使函数体内只包含一条语句,仍然必须使用花括号将其括起来。
function test()//SyntaxError: Unexpected end of input function test(){};//不报错 while(true);//不报错
提升
函数名称和函数体都提升。
foo(); function foo(){ console.log(1);//1 }
上面这个代码片段之所以能够在控制台输出1,就是因为foo()函数声明进行了提升,如下所示:
function foo(){ console.log(1); } foo();
重复
变量的重复声明是无用的,但是函数的重复声明会覆盖前面的声明。
//变量的重复声明无用 var a = 1; var a; console.log(a);//1
//由于函数声明提升优先于变量声明提升,所以变量的声明无作用 var a; function a(){ console.log(1); } a();//1
//后面的函数声明会覆盖前面的函数声明 a();//2 function a(){ console.log(1); } function a(){ console.log(2); }
和变量的声明一样,函数的声明语句创建的变量是无法删除的。
function foo(){ console.log(1); } delete foo;//false console.log(foo());//1
函数定义表达式
以表达式的方式定义函数,函数的名称是可以省略的,
var functionName = function([arg1 [,arg2 [...,argn]]]){ statement; } var functionName = function funcName([arg1 [,arg2 [...,argn]]]){ statement; }
匿名函数也叫兰姆达函数,书function后面没有表示符的函数, 通常而言,以表达式方式定义函数时都不需要名称,这会让定义它们的代码更加紧凑。函数定义表达式特别适合用来定义那些只会使用一次的函数。
var test = (function(x){return x*x}(10))
而一个函数定义表达式包含名称,函数的局部作用域将包含一个绑定到函数对象的一个名称,实际上函数的名称将会成为函数内部的一个局部变量。
var test = function fn(){ return fn; } console.log(test);//fn(){return fn;} console.log(test());//fn(){return fn;} console.log(test()());//fn(){return fn;}
个人理解,对于具名的函数表达式来说,函数名称相当于函数对象的形参,只能在函数内部使用;而变量名称相当于函数对象的实参,在函数内部和函数外部都可以使用
var test = function fn(){ return fn === test; } console.log(test());//true console.log(test === fn);//ReferenceError: fn is not defined
函数定义了一个非标准的name属性,通过这个属性可以访问到给定函数指定的名字,这个属性的值永远等于跟在function关键字后面的标识符,匿名函数的name属性为空
//IE11-浏览器无效,均输出undefined //chrome在处理匿名函数的name属性时有问题,会显示函数表达式的名字 function fn(){}; console.log(fn.name);//'fn' var fn = function(){}; console.log(fn.name);//'',在chrome浏览器中会显示'fn' var fn = function abc(){}; console.log(fn.name);//'abc'
Function构造函数
Function构造函数可以接受任意数量的参数,但是最后一个参数都被看成一个函数整体,而前面的参数则是枚举出了新函数的参数。
var functionName = new Function(['arg1' [,'arg2' [...,'argn']]],'statement;');
注意的是Function无法指定函数名称,所以只能是匿名函数。
var sum = new Function ed88 ('num1','num2','return num1 + num2'); //等价于 var sum = function(num1,num2){ return num1+num2; }
Function()构造函数创建的函数,其函数体的编译总是会在全局作用域中执行。于是,Function()构造函数类似于在全局作用域中执行的eval()
var test = 0; function fn(){ var test = 1; return new Function('return test'); } console.log(fn()());//0
并不是所有的函数都可以成为构造函数
var o = new Math.min();//Uncaught TypeError: Math.min is not a constructor
并不是函数中return语句后的所有语句都不执行,finally语句是例外,return语句不会阻止finally子句的。
function testFinnally(){ try{ return 2; }catch(error){ return 1; }finally{ return 0; } } testFinnally();//0
[注意]由于javascript可以自动插入分号,因此在return关键字和它后面的表达式之间不能有换行。
var test = function fn(){ return 2; }; console.log(test());//undefined
一个函数中可以有多个return语句。
function diff(iNum1, iNum2) { if (iNum1 > iNum2) { return iNum1 - iNum2; } else { return iNum2 - iNum1; } }
return语句可以单独使用而不必带有expression,这样的话也会向调用程序返回undefined。
var test = function fn(){ return; }; console.log(test());//undefined
return语句经常作为函数内的最后一条语句出现,这是因为return语句可用来使函数提前返回。当return被执行时,函数立即返回而不再执行余下的语句。
//并没有弹出1 var test = function fn(){ return; alert(1); }; console.log(test());//undefined
如果函数调用时在前面加上了new前缀,且返回值不是一个对象,则返回this(该新对象)。
function fn(){ this.a = 2; return {a:1}; } var test = new fn(); console.log(test);//{a:1} console.log(test.constructor);//Object() { [native code] }
函数的调用
只有函数被调用时,才会执行。调用运算符是跟在任何产生一个函数值的表达式之后的一对圆括号,圆括号内可包含零个或多个用逗号隔开的表达式。每个表达式产生一个参数值,每个参数值被赋予函数声明时定义的形参名。
1:函数调用模式
当一个函数并非一个对象的属性时,那么它就是被当做一个函数调用的。对于普通的函数来说,函数的返回值就是调用函数的表达式。
function add(x,y){ return x+y; } var sum = add(3,4); console.log(sum)//7
使用函数调用模式调用函数时,非严格模式下,this被绑定到全局对象;在严格模式下,this是undefined。
function add(x,y){ console.log(this);//window } add();
function add(x,y){ 'use strict'; console.log(this); } add();//undefined
重写
因为函数调用模式的函数中的this绑定到全局对象,所以会发生全局属性被重写的现象
var a = 0; function fn(){ this.a = 1; } fn(); console.log(this,this.a,a);//window 1 1
2:方法调用模式
当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。如果调用表达式包含一个提取属性的动作,那么它就是被当做一个方法来调用。
var o = { m: function(){ console.log(1); } }; o.m();//1
方法可以使用this访问自己所属的对象,他能从对象中取值,或者是对对象进行比较,this到对象的绑定发生在函数调用的时候,,通过this可以取得所属上下文的方法称之为公共方法。
var o = { a: 1, m: function(){ return this; }, n: function(){ this.a = 2; } }; console.log(o.m().a);//1 o.n(); console.log(o.m().a);//2
任何函数只要作为方法调用实际上都会传入一个隐式的实参——这个实参是一个对象,方法调用的母体就是这个对象,通常来讲,基于那个对象的方法可以执行多种操作,方法调用的语法已经很清晰地表明了函数将基于一个对象进行操作.
rect.setSize(width,height); setRectSize(rect,width,height);
假设上面两行代码的功能完全一样,它们都作用于一个假定的对象rect。可以看出,第一行的方法调用语法非常清晰地表明这个函数执行的载体是rect对象,函数中的所有操作都将基于这个对象
和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用,其this的值指向调用它的对象。如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下).
var o = { m: function(){ function n(){ return this; } return n(); } } console.log(o.m());//window
var o = { m: function(){ function n(){ 'use strict'; return this; } return n(); } } console.log(o.m());//undefined
如果想访问这个外部函数的this值,需要将this的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。通常使用变量self或that来保存this.
var o = { m: function(){ var self = this; console.log(this === o);//true function n(){ console.log(this === o);//false console.log(self === o);//true return self; } return n(); } } console.log(o.m() === o);//true
3: 构造函数调用模式
如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。
function fn(){ this.a = 1; }; var obj = new fn(); console.log(obj.a);//1
如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内。
function fn(x){ this.a = x; }; var obj = new fn(2); console.log(obj.a);//2
如果构造函数没有形参,javascript构造函数调用的语法是允许省略实参列表和圆括号的。凡是没有形参的构造函数调用都可以省略圆括号。
var o = new Object(); //等价于 var o = new Object;
尽管构造函数看起来像是一个方法调用,依然会使用新对象作为调用上下文,如下。
var o = { m: function(){ return this; } } var obj = new o.m(); console.log(obj,obj === o);//{} false console.log(obj.constructor === o.m);//true
构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值.
function fn(){ this.a = 2; } var test = new fn(); console.log(test);//{a:2}
如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果.
function fn(){ this.a = 2; return; } var test = new fn(); console.log(test);//{a:2}
如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象.
var obj = {a:1}; function fn(){ this.a = 2; return obj; } var test = new fn(); console.log(test);//{a:1}
间接调用模式
javascript中函数也是对象,函数对象也可以包含方法。call()和apply()方法可以用来间接地调用函数
这两个方法都允许显式指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。两个方法都可以指定调用的实参。call()方法使用它自有的实参列表作为函数的实参,apply()方法则要求以数组的形式传入参数。
var obj = {}; function sum(x,y){ return x+y; } console.log(sum.call(obj,1,2));//3 console.log(sum.apply(obj,[1,2]));//3
啊,真有感觉,读一遍有一边的感觉,过段时间再读一遍,相信还会有不一样的收获。
相关文章推荐
- 深入理解javascript函数系列第一篇——函数概述
- 深入理解javascript函数系列第二篇——函数参数
- 深入学习Web Service系列之异步开发模式
- 深入学习的热潮:嵌入式系统方向概述
- 深入C#学习系列二:不可小瞧的using关键字[转]
- [C++再学习系列] C++编译器的函数编译流程
- Lua程序设计(第2版)第六章学习随笔——深入函数
- SQL深入学习(1)--时间函数
- [C++再学习系列] 深入new/delete:Operator new的全局重载
- WF学习系列之一:WF基本知识点概述
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之一--中断概述
- [C++再学习系列] 虚函数的4条规则
- STL学习笔记一(深入VC之STL系列)
- 深入学习Web Service系列之异步开发模式(转载)
- C/C++学习笔记1 - 深入了解scanf()/getchar()和gets()等函数(原创)
- WF学习系列之二:开发工作流知识点概述
- [zz]深入学习的热潮:嵌入式系统方向概述
- 深入了解Asp.net系列 概述
- LINQ To SQL深入学习系列之一(C#3.0为LINQ的加强之一Lambda 表达式)
- HTML全面深入学习-table系列