万物皆是对象,一切存为数据/值。对象是值,函数也是值。
行为对象 - 函数
函数是可以被调用的“行为/动作对象”,一个函数就是一个行为/动作。作为对象的基本操作都适用,如增/删属性,按引用传递等。
属性 name
name
属性就是函数名;- 匿名函数(包括箭头函数)本身没有函数名,
name=''
,但可以通过赋值给变量,使变量成为函数,此时函数名就是变量名了; - 如果将非匿名函数赋值给变量,则函数名函数声明时的名称;
Function
构造函数返回的函数,name
属性的值为anonymous
;bind
返回的函数,name
属性值会加上bound
前缀;- 对象里的方法,也有
name
属性·。
function func() {}
func.name // 'func'let func2 = function () {}
func2.name // 'func2'let g = function func3() {}
g.name // 'func3' !!(new Function).name // anonymousfunc.bind({}).name // "bound func"const o = { sayHi() {} }
o.sayHi.name // 'sayHi'
属性 length
函数的 length
属性反映参数数量,计算原则:
- 一个普通参数的贡献是 1;
- 一个解构参数的贡献是 1;
- 剩余参数的贡献是 0;
- 默认参数的贡献是0,且截断其后的参数贡献。
(function (a) {}).length // 1
(function ([a, b]) {}).length // 1
(function ({ a, b }) {}).length // 1
(function (a, ...rest) {}).length // 1
(function (a, b=0) {}).length // 1
(function (a=0, b) {}).length // 0 默认参数后的贡献被截断
自定义属性
作为一个对象,函数可以自定义任何属性,并且可以在函数体中使用。
函数属性有时会用来替代闭包。闭包的环境更隐蔽,而函数的属性可以更直观的访问。
function sayHi() { console.log(`第 ${++sayHi.callCount} 次说你好。`) }
sayHi.callCount = 0;
sayHi() // 第 1 次说你好。
sayHi() // 第 2 次说你好。/* 使用函数属性建立计数器,而不是闭包:闭包需要在外部函数中声明计算值的变量,函数属性直接绑定在函数上。 */
function makeCounter(init=0) {// let val = init;function counter() {// return val++;return counter.val++;}counter.val = init; // 属性初始化return counter;
}
let counter = makeCounter();
counter(); // 0
counter(); // 1
函数的构造函数 Function
任何对象都有构造函数,函数作为一个对象也不例外,但函数对象的构造函数却很少使用,是不得已才使用的创建函数的方法。
基本语法 let func = new Function ([arg1, arg2, ...argN], functionBody)
。
- 参数都是字符串,最后一个参数是函数体字符串;
- 通过执行字符串来构建函数,会遇到和
eval()
类似的的安全问题和(相对较小的)性能问题,不同的是,Function
构造函数创建的函数只能在全局作用域中运行,所以不能用来创建闭包; - 应用场景特殊,比如在复杂的 Web 应用程序中,当需要从服务器获取代码、动态地从模板编译函数时才会使用。
let x = 10; // 一个全局变量function gen1() {let x = 20;return new Function("return x;"); // 这个 x 指向全局 x,不能构成闭包
}function gen2() {let x = 20;function f() { return x; } // 这个 x 指向上面的局部 xreturn f; // 此函数与内部环境构成闭包!!
}const f1 = gen1();
f1() // 10
const f2 = gen2();
f2() // 20