Function javascript
通常而言,一个函数是一个子程序,他们可以被外部代码调用(亦或被滴管函数在内部调用)。和程序一样,函数是由一系列声明(被称为函数体function body)组合而成。值可以传递给函数,并且函数可以返回一个值。
在javascript中,函数是一个对象,因为她可以拥有属性和方法,就像对象一样。将函数和对象区分的是函数可以被调用,他们是函数对象(Function objects)。
描述
js中的所有函数都是函数对象Function object
函数和程序不一样,函数总是返回一个值,但是程序不一定。
为了返回一个值,一个班函数必须要有return的声明。一个没有return声明的函数将会返回一个默认值。
在用new关键字调用的构造函数中,默认值是this参数。对于其他的函数而言,默认值返回的是undefined。
函数调用个参数是函数的arguments属性。参数按值传递给函数,所以函数内部改变argument的值将不会影响函数外的参数的值。
然而对象的引用作为值传递给函数时,函数内部对于对象的改变将会影响到函数外部。
/* Declare the function 'myFunc' */ function myFunc(theObject) { theObject.brand = "Toyota"; } /* * Declare variable 'mycar'; * create and initialize a new Object; * assign reference to it to 'mycar' */ var mycar = { brand: "Honda", model: "Accord", year: 1998 }; /* Logs 'Honda' */ console.log(mycar.brand); /* Pass object reference to the function */ myFunc(mycar); /* * Logs 'Toyota' as the value of the 'brand' property of the object, as changed to by the function. */ console.log(mycar.brand);
关键字this并不指向当前正在执行的函数(指向的是windows对象),所以你必须通过名字将其指向函数对象。
定义函数
有以下方式来定义函数:
函数声明(function
声明)
function name([param[, param[, ... param]]]) {
statements
}
name
- 函数名
param
- 传递给函数的参数.参数个数的上限是255个。
statements
- 函数主体
函数表达式 (function
表达式)
function [name]([param[, param[, ... param]]]) {
statements
}
name
- 函数名. 可以省略,省略后被叫做匿名函数.
param
- 参数,上限255个参数
statements
- 函数主体
函数构造器
注意!使用Function构造器来创建函数不被推荐,因为它需要将函数主体作为一个字符串,这将阻止JS引擎优化并且会造成其他的问题。
new Function (arg1, arg2, ... argN, functionBody)
arg1, arg2, ... argN
- 0个或者多个参数名
functionBody
- 一个包含函数主体的字符串
还有一些新式的ECMAScript 6创建函数的方式,姐姐我不说了,等什么时候流行了再说 (✿◡‿◡)
函数属性
arguments对象
你可以在函数中使用arguments对象来指代函数的arguments属性。
arguments
:一个类数组对象,包含了传递给正在执行的函数的参数。arguments.callee
: 正在执行的函数(API说不赞成这个属性被使用,但是仍然可以使用)arguments.caller
: 废弃了,我不说了,和上面的作用一样arguments.length
: 传递给函数的参数个数
定义函数方法
getter和setter方法
你可以在任何支持新属性添加的内置对象或用户定义对象中定义getter(accessor方法)和 setter(mutator方法) 。定义setters和getters使用了对象字面量语法。
函数构造器VS函数声明VS函数表达式
比较如下:
定义一个函数使用Function构造函数,并且赋值给变量multiply
function multiply(x, y) { return x * y; }
一个匿名的函数表达式,并赋值给变量 multiply:
var multiply = function(x, y) { return x * y; };
一个函数表达式,并将函数命名为func_name,并赋值给变量
multiply:
var multiply = function func_name(x, y) { return x * y; };
不同:
上述所有好像都在做着差不多的事情,但是有一些细微的不同。
在函数名和将函数赋值给的变量之间存在不同。
函数名是不可以改变的,但是变量是可以被再次赋值。
函数名只能在函数体里面使用。当尝试在函数外部使用时将会导致一个错误(如果函数名事先声明将build报错undefined)
例如:
var y = function x() {}; alert(x); // throws an error
在函数通过Function的toString方法序列化的时候同样也可以出现。
就像第四个例子展示的一样,函数名可以和函数赋值的变量不一样。他们之间没有相互关系。一个函数声明也可以创建一个拥有同样名字的变量。所以,和通过函数表达式被定义的函数不同,通过函数声明定义的函数可以在他们被定义的范围内通过他们的名字来获取函数本身。
通过new Function定义的函数没有函数名。但是在SpiderMonkey的js引擎中,函数序列化后的格式表示他们拥有anonymous的函数名。
例如:alert(new Function())的输出结果是:
function anonymous() {
}
因为函数的确没有一个名称,anonymous并不是一个可以在函数内可以使用的变量(函数声明或者函数表达式中的函数名可以在函数体内使用),例如,下面的例子将会报错:
var foo = new Function("alert(anonymous);"); foo();
和用函数表达式或者Function构造器定义函数不同的是,通过函数声明定义的函数可以在函数声明前使用(使用了一个叫做函数声明提前的机制),例如:
foo(); // alerts FOO! function foo() { alert('FOO!'); }
一个通过函数表达式定义的函数继承了当前的环境。也就是说,这个函数形成了一个闭包。
然而,通过Function构造器定义的函数没有继承除了全局环境(所有函数都继承)以外的任何环境。
通过函数表达式和函数声明定义的函数只被解析一次,但是被函数构造器定义的函数被解析不仅一次。就是说,函数体字符串传递给Function 构造函数的时候必须被解析,此时将会调用构造函数(所以每次在使用函数构造器定义的函数的函数体的时候,都会调用构造函数)。虽然函数表达式会每次创建一个闭包,但是函数在调用个时候不会被每次解析,所以函数表达式仍然比"new Function(...)"要快,因此,函数构造器通常会尽可能的避免被使用。
需要注意的是,函数表达式和函数声明中嵌套着函数构造器定义的函数将不会被重复解析。如下图:
var foo = (new Function("var bar = \'FOO!\';\nreturn(function() {\n\talert(bar);\n});"))(); foo(); // The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.
函数声明非常容易且常常是无意识的变成一个函数表达式。一个函数声明在下面任何一种情况将变成函数表达式:
1.成为表达式的一部分
2.不再是一个函数或者程序的“源元素(source element)”。一个source element是一个在程序亦或函数体中不内嵌的声明。
-
var x = 0; // source element if (x == 0) { // source element x = 10; // not a source element function boo() {} // not a source element } function foo() { // source element var y = 20; // source element function bar() {} // source element while (y == 10) { // source element function blah() {} // not a source element y++; // not a source element } }
举例:
//函数声明 function foo() {} // 函数表达式 (function bar() {}) // 函数表达式 x = function hello() {} if (x) { //函数表达式 function world() {} } //函数声明 function a() { //函数声明 function b() {} if (0) { // 函数表达式 function c() {} } }
块级函数(block-level functions)
在严格模式下,从 ES2015 (ES6)开始,在一个块(花括号)里面的函数,它的作用域将属于那个块。
在ES6之前,块级函数禁止在严格模式下使用。
'use strict'; function f() { return 1; } { function f() { return 2; } } f() === 1; // true // f() === 2 in non-strict mode
块级函数在非严格模式下使用
简而言之:不要用!
在非严格模式下,在花括号中的函数声明将会表现的非常奇怪。如下:
if (shouldDefineZero) { function zero() { // DANGER: compatibility risk console.log("This is zero."); } }
ES2015定义了如果shouldDefineZero(应该定义zero么)是false,那么说明zero没有被定义,那么花括号里面的代码也不会执行。
但是,这是新标准的一部分。历史上是遗留的没有定义的标准,并且一些浏览器将定义zero不管判断语句执行成功与否。
在严格模式下,所有的浏览器都支持ES2015标准,所以他们都将按照一样的方式执行:zero在只有shouldDefineZero为true的时候才能执行,并且只在if语句块中执行。
一个更加安全的做法是将将函数表达式赋值给一个变量。
var zero; if (0) { zero = function() { console.log("This is zero."); }; }
判断一个函数方法是否存在
你可以通过typeof判断一个函数是否存在。
在下面的方法中,进行了一个用于判断window对象是否有noFunc的属性,并且是noFunc是一个函数类型的测试。
if ('function' == typeof window.noFunc) { // use noFunc() } else { // do something else }
注意在if的条件判断中,只是引用了noFunc函数名,但是函数名后面没有(),也就是说实际上没有执行。
原文链接:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions