持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
在前面值类型和引用类型这篇文章中我们已经简单介绍了函数的概念,现在开始详细介绍一下函数(Function对象)的用法。
基本介绍
每个 JavaScript 函数实际上都是一个 Function 对象。可以通过运行 (function(){}).constructor === Function // true 验证。像其他对象一样,Function对象也有自己的构造函数,实例属性和实例方法
构造函数
创建一个新的
Function对象。直接调用此构造函数可以动态创建函数,Function构造函数创建的函数只能在全局作用域中运行。
1. 语法
new Function(functionBody)
new Function(arg0, functionBody)
new Function(arg0, arg1, functionBody)
new Function(arg0, arg1, /* … ,*/ argN, functionBody)
Function(functionBody)
Function(arg0, functionBody)
Function(arg0, arg1, functionBody)
Function(arg0, arg1, /* … ,*/ argN, functionBody)
2. 用法
const recursiveFn = new Function("count", `
(function recursiveFn(count) {
if (count < 0) {
return;
}
console.log(count);
recursiveFn(count - 1);
})(count);
`);
实例属性
1. Function.arguments
function.arguments属性代表传入函数的实参,它是一个类数组对象。这个实例属性已经被弃用了,就不详细介绍了。
2. Function.caller
返回调用指定函数的函数。这个实例属性不建议使用,了解一下概念就可以。
3. Function.displayName
function.displayName属性获取函数的显示名称。这个实例属性也不建议使用,了解一下概念就可以。
4. Function.length
length属性指明函数的形参个数。这个实例属性是比较重要的一个属性,我们调用函数的时候一般都会涉及到参数。需要注意的是Functioneference/Global_Objects/Function)。它的length属性值为 1。Function.prototype对象的length属性值为 0。
console.log(Function.length); // 1
console.log((() => {}).length); // 0
console.log(((a) => {}).length); // 1
console.log(((a, b) => {}).length); // 2 etc.
console.log(((...args) => {}).length);
// 0, 剩余参数不计算在内
console.log(((a, b = 1, c) => {}).length);
// 1, 只有第一个具有默认值的参数之前的参数才会被计算
5. Function.name
function.name属性返回函数实例的名称。name属性是只读的,不能用赋值操作符修改, 需要借助Object.defineProperty().
function doSomething() { }
doSomething.name; // "doSomething"
(new Function).name; // "anonymous"
实例方法
下面介绍的这几个实例方法在平时的开发中经常用到。
1. Function.prototype.apply()
apply()方法调用一个具有给定this值的函数,以及以一个数组(或一个类数组对象的形式提供的参数。
1.1 语法
apply(thisArg)
apply(thisArg, argsArray)
1.2 参数说明
thisArg:在func函数运行时使用的this值。argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给func函数。如果该参数的值为null或undefined,则表示不需要传入任何参数。
1.3 应用
1.3.1 将数组各项添加到另一个数组
const array = ['a', 'b'];
const elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
1.3.2 apply替代内置函数,避免循环
// 使用 Math.min/Math.max 以及 apply 函数时的代码
let max = Math.max.apply(null, numbers); // 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..)
let min = Math.min.apply(null, numbers);
1.3.3 链接构造器(改变this指向)
Function.prototype.construct = function (aArgs) {
let oNew = Object.create(this.prototype);
this.apply(oNew, aArgs);
return oNew;
};
2. Function.prototype.call()
call()方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数。和上面的apply类似,只是参数不同。
2.1 语法
function.call(thisArg, arg1, arg2, ...)
call() 允许为不同的对象分配和调用属于一个对象的函数/方法。
call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
2.2 参数说明
thisArg:可选的。在function函数运行时使用的this值。arg1, arg2, ...:指定的参数列表。
2.3 应用
2.3.1 调用父类构造函数
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
2.3.2 调用匿名函数
var animals = [
{ species: 'Lion', name: 'King' },
{ species: 'Whale', name: 'Fail' }
];
for (var i = 0; i < animals.length; i++) {
(function(i) {
this.print = function() {
console.log('#' + i + ' ' + this.species
+ ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}
2.3.3 改变this上下文
function greet() {
var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
console.log(reply);
}
var obj = {
animal: 'cats', sleepDuration: '12 and 16 hours'
};
greet.call(obj); // cats typically sleep between 12 and 16 hours
2.3.4 调用函数并不指定第一个参数
var sData = 'Wisen';
function display() {
console.log('sData value is %s ', this.sData);
}
display.call(); // sData value is Wisen
3. Function.prototype.bind()
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
bind() 函数会创建一个新的绑定函数,具有以下内部属性:
- [[BoundTargetFunction]] - 包装的函数对象
- [[BoundThis]] - 在调用包装函数时始终作为 this 值传递的值。
- [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
- [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。
3.1 语法
function.bind(thisArg[, arg1[, arg2[, ...]]])
3.2 参数说明
thisArg:调用绑定函数时作为this参数传递给目标函数的值。如果使用new运算符构造绑定函数,则忽略该值。当使用bind在setTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果bind函数的参数列表为空,或者thisArg是null或undefined,执行作用域的this将被视为新函数的thisArg。arg1, arg2, ...:当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
3.3 应用
3.3.1 创建绑定函数
this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX();
// 返回 9 - 因为函数是在全局作用域中调用的
// 创建一个新函数,把 'this' 绑定到 module 对象
// 新手可能会将全局变量 x 与 module 的属性 x 混淆
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81
3.3.2 在定时器中使用
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// 在 1 秒钟后声明 bloom
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom(); // 一秒钟后,调用 'declare' 方法 I am a beautiful flower with 9 petals!
4. Function.prototype.toString()
toString()方法返回一个表示当前函数源代码的字符串。在Function需要表示为字符串时,JavaScript 会自动调用函数的toString方法.
function test(fn) {
console.log(fn.toString());
}
function f() {}
class A { a() {} }
function* g() {}
test(f); // "function f() {}"
test(A); // "class A { a() {} }"
test(g); // "function* g() {}"
test((a) => a); // "(a) => a"
test({ a() {} }.a); // "a() {}"
test({ *a() {} }.a); // "*a() {}"
test({ [0](){} }[0]); // "[0]() {}"
test(Object.getOwnPropertyDescriptor({
get a() {},
}, "a").get); // "get a() {}"
test(Object.getOwnPropertyDescriptor({
set a(x) {},
}, "a").set); // "set a(x) {}"
test(Function.prototype.toString); // "function toString() { [native code] }"
test(function f() {}.bind(0)); // "function () { [native code] }"
test(Function("a", "b")); // function anonymous(a\n) {\nb\n}