函数的独特之处
浏览器的事件轮询是单线程的
函数声明
函数name属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| function isNimble() { return true; } console.log(isNimble.name === 'isNimble');
var canFly = function() { return true; }; console.log(canFly.name === 'canFly');
window.wieldsSword = function swingsSword() { return true; }; console.log(window.wieldsSword.name === 'swingsSword');
var object2 = { someMethod1: function() {} }; object2.someMethod2 = function() {}
console.log(object2.someMethod1.name); console.log(object2.someMethod2.name);
var object = { someMethod: function() { } }; object.someMethod.name = "otherMethod"; console.log(object.someMethod.name);
function foo() {}; console.log(foo.bind({}).name);
console.log((new Function).name);
|
更多请参考:Function.name - JavaScript|MDN
函数声明提升
1 2 3 4 5 6
| function outer() { console.log(typeof inner === 'function'); function inner() {} console.log(window.inner === undefined); } outer();
|
作用域和函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function outer() { console.log(c); console.log(d);
if(false) { var c = 3; }
var d = 4; var d; console.log(c); console.log(d); }
outer();
|
- 变量定义的作用域在整个函数内,但变量赋值的作用域开始于被赋值的地方,结束于所在函数的结尾,都与代码嵌套无关。
- 命名函数的作用域是指声明该函数的整个函数范围,与代码嵌套无关(机制提升)
- 对于作用域声明,全局上下文就像一个包含页面所有代码的超大型函数
函数调用
1 2 3 4 5 6 7
| function foo() { return this; } console.log(foo() === global);
var bar = foo; console.log(bar() === global);
|
使用bar变量调用该函数,该函数也是作为函数进行调用的,而且函数上下文依然是global(在浏览器是window);
小结
- 函数的形参列表和实际参数的长度可以是不同的。
未赋值的参数被设置为undefined。
多出的参数是不会绑定到参数名称的。
- 每个函数调用都会传入两个隐式参数:
arguments,实际传入的参数集合
this,作为函数上下文的对象引用
- 可以用不同的方法进行函数调用,不同的调用机制决定了函数上下文的不同。
作为普通函数进行调用时,其上下文的全局对象(global/window)。
作为方法进行调用时,其上下文是拥有该方法的对象。
作为构造器进行调用时,其上下文是一个新分配的对象。
通过函数的apply()或call()方法进行调用时,上下文可以设置成任何值。
递归
内联命名函数
1 2 3 4 5 6
| var bar = function foo() { console.log(foo === bar); } bar();
console.log(typeof foo);
|
- 尽管可以给内联函数进行命名,但这些名称只能在自身函数内部才是可见的。
!!
构造是一个可以将任意JavaScript表达式转化为其等效布尔值的简单方式。eg,!!"a" === true
和!!0 === false
可变长度的参数列表
函数的length属性
函数的length属性等于该函数声明时所要传入的形参数量
1 2 3 4 5
| function bar(a) {} function foo(a, b, c) {}
console.log(bar.length); console.log(foo.length);
|
- 通过其length属性,可以知道声明了多少命名参数
- 通过
arguments.length
,可以知道在调用时传入了多少参数
- 利用参数个数的差异创建重载函数
利用参数的个数进行函数重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| function addMethod(object, name, fn) { var old = object[name]; object[name] = function() { if (fn.length == arguments.length) { return fn.apply(this, arguments); } else if (typeof old === 'function') { return old.apply(this, arguments); } } }
var myobj = {};
addMethod(myobj, 'a', function() { console.log('0个参数'); }); addMethod(myobj, 'a', function(n) { console.log('1个参数', n); }); addMethod(myobj, 'a', function(n1, n2) { console.log('两个参数', n1, n2); });
myobj.a(); myobj.a(1); myobj.a(1, 2);
|
函数判断
1 2 3 4 5 6 7 8 9 10
| function foo() {}
console.log(typeof foo);
function isFunction(fn) { return Object.prototype.toString.call(fn) === '[object Function]'; } console.log(isFunction(foo));
|