函数是 ECMAScript 中最有意思的部分之一,这主要是因为函数实际上是对象。每个函数都是 Function 类型的实例,而 Function 也有属性和方法,跟其他引用类型一样。因为函数是对象,所以函数名就是指向函数对象的指针,而且不一定与函数本身紧密绑定。函数通常以函数声明的方式定义,比如:

function sum (num1, num2) {return num1 + num2;
}

注意函数定义最后没有加分号。

另一种定义函数的语法是函数表达式。函数表达式与函数声明几乎是等价的:

let sum = function(num1, num2) {return num1 + num2;
};

这里,代码定义了一个变量 sum 并将其初始化为一个函数。注意 function 关键字后面没有名称,因为不需要。这个函数可以通过变量 sum 来引用。注意函数定义最后没有加分号。

注意这里的函数末尾是有分号的,与任何变量初始化语句一样。

还有一种定义函数的方式与函数表达式很像,叫作“箭头函数”(arrow function),如下所示:

let sum = (num1, num2) => {return num1 + num2;
};

最后一种定义函数的方式是使用 Function 构造函数。这个构造函数接收任意多个字符串参数,最后一个参数始终会被当成函数体,而之前的参数都是新函数的参数。来看下面的例子:

let sum = new Function("num1", "num2", "return num1 + num2");  // 不推荐

我们不推荐使用这种语法来定义函数,因为这段代码会被解释两次:第一次是将它当作常规 ECMAScript 代码,第二次是解释传给构造函数的字符串。这显然会影响性能。不过,把函数想象为对象,把函数名想象为指针是很重要的。而上面这种语法很好地诠释了这些概念。

注意 这几种实例化函数对象的方式之间存在微妙但重要的差别,本章后面会讨论。无论如何,通过其中任何一种方式都可以创建函数。

箭头函数

ECMAScript 6 新增了使用胖箭头(=>)语法定义函数表达式的能力。很大程度上,箭头函数实例化的函数对象与正式的函数表达式创建的函数对象行为是相同的。任何可以使用函数表达式的地方,都可以使用箭头函数:

let arrowSum = (a, b) => {return a + b;
};let functionExpressionSum = function(a, b) {return a + b;
};console.log(arrowSum(5, 8)); // 13
console.log(functionExpressionSum(5, 8)); // 13

箭头函数简洁的语法非常适合嵌入函数的场景:

let ints = [1, 2, 3];console.log(ints.map(function(i) { return i + 1; }));  // [2, 3, 4]
console.log(ints.map((i) => { return i + 1 }));        // [2, 3, 4]

如果只有一个参数,那也可以不用括号。只有没有参数,或者多个参数的情况下,才需要使用括号:

// 以下两种写法都有效
let double = (x) => { return 2 * x; };
let triple = x => { return 3 * x; };// 没有参数需要括号
let getRandom = () => { return Math.random(); };// 多个参数需要括号
let sum = (a, b) => { return a + b; };// 无效的写法:
let multiply = a, b => { return a * b; };

箭头函数也可以不用大括号,但这样会改变函数的行为。使用大括号就说明包含“函数体”,可以在一个函数中包含多条语句,跟常规的函数一样。如果不使用大括号,那么箭头后面就只能有一行代码,比如一个赋值操作,或者一个表达式。而且,省略大括号会隐式返回这行代码的值:

// 以下两种写法都有效,而且返回相应的值
let double = (x) => { return 2 * x; };
let triple = (x) => 3 * x;// 可以赋值
let value = {};
let setName = (x) => x.name = "Matt";
setName(value);
console.log(value.name); // "Matt"// 无效的写法:
let multiply = (a, b) => return a * b;

箭头函数虽然语法简洁,但也有很多场合不适用。箭头函数不能使用 arguments、super 和 new.target,也不能用作构造函数。此外,箭头函数也没有 prototype 属性。

函数名

因为函数名就是指向函数的指针,所以它们跟其他包含对象指针的变量具有相同的行为。这意味着一个函数可以有多个名称,如下所示:

function sum(num1, num2) {return num1 + num2;
}console.log(sum(10, 10));         // 20let anotherSum = sum;
console.log(anotherSum(10, 10));  // 20sum = null;
console.log(anotherSum(10, 10));  // 20

以上代码定义了一个名为 sum() 的函数,用于求两个数之和。然后又声明了一个变量 anotherSum,并将它的值设置为等于 sum。注意,使用不带括号的函数名会访问函数指针,而不会执行函数。此时,anotherSum 和 sum 都指向同一个函数。调用 anotherSum() 也可以返回结果。把 sum 设置为 null 之后,就切断了它与函数之间的关联。而 anotherSum() 还是可以照常调用,没有问题。

ECMAScript 6 的所有函数对象都会暴露一个只读的 name 属性,其中包含关于函数的信息。多数情况下,这个属性中保存的就是一个函数标识符,或者说是一个字符串化的变量名。即使函数没有名称,也会如实显示成空字符串。如果它是使用 Function 构造函数创建的,则会标识成"anonymous":

function foo() {}
let bar = function() {};
let baz = () => {};console.log(foo.name);               // foo
console.log(bar.name);               // bar
console.log(baz.name);               // baz
console.log((() => {}).name);        //(空字符串)
console.log((new Function()).name);  // anonymous

如果函数是一个获取函数、设置函数,或者使用 bind() 实例化,那么标识符前面会加上一个前缀:

function foo() {}console.log(foo.bind(null).name);    // bound foolet dog = {years: 1,get age() {return this.years;
},
set age(newAge) {this.years = newAge;}
}let propertyDescriptor = Object.getOwnPropertyDescriptor(dog, 'age');
console.log(propertyDescriptor.get.name);  // get age
console.log(propertyDescriptor.set.name);  // set age

理解参数

ECMAScript 函数的参数跟大多数其他语言不同。ECMAScript 函数既不关心传入的参数个数,也不关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一个、三个,甚至一个也不传,解释器都不会报错。

之所以会这样,主要是因为 ECMAScript 函数的参数在内部表现为一个数组。函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么。如果数组中什么也没有,那没问题;如果数组的元素超出了要求,那也没问题。事实上,在使用 function 关键字定义(非箭头)函数时,可以在函数内部访问 arguments 对象,从中取得传进来的每个参数值。

arguments 对象是一个类数组对象(但不是 Array 的实例),因此可以使用中括号语法访问其中的元素(第一个参数是arguments[0],第二个参数是arguments[1])。而要确定传进来多少个参数,可以访问arguments.length属性。

在下面的例子中,sayHi() 函数的第一个参数叫 name:

function sayHi(name, message) {console.log("Hello " + name + ", " + message);
}

可以通过 arguments[0] 取得相同的参数值。因此,把函数重写成不声明参数也可以:

function sayHi() {console.log("Hello " + arguments[0] + ", " + arguments[1]);
}

在重写后的代码中,没有命名参数。name 和 message 参数都不见了,但函数照样可以调用。这就表明,ECMAScript 函数的参数只是为了方便才写出来的,并不是必须写出来的。与其他语言不同,在 ECMAScript 中的命名参数不会创建让之后的调用必须匹配的函数签名。这是因为根本不存在验证命名参数的机制。

也可以通过 arguments 对象的 length 属性检查传入的参数个数。下面的例子展示了在每调用一个函数时,都会打印出传入的参数个数:

function howManyArgs() {console.log(arguments.length);
}howManyArgs("string", 45);  // 2
howManyArgs();              // 0
howManyArgs(12);            // 1

这个例子分别打印出 2、0 和 1(按顺序)。既然如此,那么开发者可以想传多少参数就传多少参数。比如:

function doAdd() {if (arguments.length === 1) {console.log(arguments[0] + 10);} else if (arguments.length === 2) {console.log(arguments[0] + arguments[1]);}
}doAdd(10);      // 20
doAdd(30, 20);  // 50

这个函数 doAdd() 在只传一个参数时会加 10,在传两个参数时会将它们相加,然后返回。因此 doAdd(10) 返回 20,而 doAdd(30,20) 返回 50。虽然不像真正的函数重载那么明确,但这已经足以弥补 ECMAScript 在这方面的缺失了。

还有一个必须理解的重要方面,那就是arguments对象可以跟命名参数一起使用,比如:

function doAdd(num1, num2) {if (arguments.length === 1) {console.log(num1 + 10);} else if (arguments.length === 2) {console.log(arguments[0] + num2);}
}

在这个 doAdd() 函数中,同时使用了两个命名参数和 arguments 对象。命名参数 num1 保存着与 arugments[0] 一样的值,因此使用谁都无所谓。(同样,num2 也保存着跟 arguments[1] 一样的值。)

arguments 对象的另一个有意思的地方就是,它的值始终会与对应的命名参数同步。来看下面的例子:

function doAdd(num1, num2) {arguments[1] = 10;console.log(arguments[0] + num2);
}

这个 doAdd() 函数把第二个参数的值重写为 10。因为 arguments 对象的值会自动同步到对应的命名参数,所以修改 arguments[1] 也会修改 num2 的值,因此两者的值都是 10。但这并不意味着它们都访问同一个内存地址,它们在内存中还是分开的,只不过会保持同步而已。另外还要记住一点:如果只传了一个参数,然后把 arguments[1] 设置为某个值,那么这个值并不会反映到第二个命名参数。这是因为 arguments 对象的长度是根据传入的参数个数,而非定义函数时给出的命名参数个数确定的。

对于命名参数而言,如果调用函数时没有传这个参数,那么它的值就是 undefined。这就类似于定义了变量而没有初始化。比如,如果只给 doAdd() 传了一个参数,那么 num2 的值就是 undefined。

严格模式下,arguments 会有一些变化。首先,像前面那样给 arguments[1] 赋值不会再影响 num2 的值。就算把 arguments[1] 设置为 10,num2 的值仍然还是传入的值。其次,在函数中尝试重写 arguments 对象会导致语法错误。(代码也不会执行。)

箭头函数中的参数

如果函数是使用箭头语法定义的,那么传给函数的参数将不能使用 arguments 关键字访问,而只能通过定义的命名参数访问。

function foo() {console.log(arguments[0]);
}
foo(5); // 5let bar = () => {console.log(arguments[0]);
};
bar(5);  // ReferenceError: arguments is not defined

虽然箭头函数中没有 arguments 对象,但可以在包装函数中把它提供给箭头函数:

function foo() {let bar = () => {console.log(arguments[0]); // 5};bar();}foo(5);

注意 ECMAScript 中的所有参数都按值传递的。不可能按引用传递参数。如果把对象作为参数传递,那么传递的值就是这个对象的引用。

没有重载

ECMAScript 函数不能像传统编程那样重载。在其他语言比如 Java 中,一个函数可以有两个定义,只要签名(接收参数的类型和数量)不同就行。如前所述,ECMAScript 函数没有签名,因为参数是由包含零个或多个值的数组表示的。没有函数签名,自然也就没有重载。

如果在 ECMAScript 中定义了两个同名函数,则后定义的会覆盖先定义的。来看下面的例子:

function addSomeNumber(num) {return num + 100;
}function addSomeNumber(num) {return num + 200;
}let result = addSomeNumber(100); // 300

这里,函数 addSomeNumber() 被定义了两次。第一个版本给参数加 100,第二个版本加 200。最后一行调用这个函数时,返回了 300,因为第二个定义覆盖了第一个定义。

前面也提到过,可以通过检查参数的类型和数量,然后分别执行不同的逻辑来模拟函数重载。

把函数名当成指针也有助于理解为什么 ECMAScript 没有函数重载。在前面的例子中,定义两个同名的函数显然会导致后定义的重写先定义的。而那个例子几乎跟下面这个是一样的:

let addSomeNumber = function(num) {
return num + 100;
};addSomeNumber = function(num) {
return num + 200;
};let result = addSomeNumber(100); // 300

看这段代码应该更容易理解发生了什么。在创建第二个函数时,变量 addSomeNumber 被重写成保存第二个函数对象了。

默认参数值

在 ECMAScript5.1 及以前,实现默认参数的一种常用方式就是检测某个参数是否等于 undefined,如果是则意味着没有传这个参数,那就给它赋一个值:

function makeKing(name) {name = (typeof name !== 'undefined') ? name : 'Henry';return `King ${name} VIII`;
}console.log(makeKing());         // 'King Henry VIII'
console.log(makeKing('Louis'));  // 'King Louis VIII'

ECMAScript 6 之后就不用这么麻烦了,因为它支持显式定义默认参数了。下面就是与前面代码等价的ES6写法,只要在函数定义中的参数后面用=就可以为参数赋一个默认值:

function makeKing(name = 'Henry') {return `King ${name} VIII`;
}console.log(makeKing('Louis'));  // 'King Louis VIII'
console.log(makeKing());         // 'King Henry VIII'

给参数传 undefined 相当于没有传值,不过这样可以利用多个独立的默认值:

function makeKing(name = 'Henry', numerals = 'VIII') {return `King ${name} ${numerals}`;
}console.log(makeKing());                 // 'King Henry VIII'
console.log(makeKing('Louis'));          // 'King Louis VIII'
console.log(makeKing(undefined, 'VI'));  // 'King Henry VI'

在使用默认参数时,arguments 对象的值不反映参数的默认值,只反映传给函数的参数。当然,跟 ES5 严格模式一样,修改命名参数也不会影响 arguments 对象,它始终以调用函数时传入的值为准:

function makeKing(name = 'Henry') {name = 'Louis';return `King ${arguments[0]}`;
}console.log(makeKing());         // 'King undefined'
console.log(makeKing('Louis'));  // 'King Louis'

默认参数值并不限于原始值或对象类型,也可以使用调用函数返回的值:

let romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI'];
let ordinality = 0;function getNumerals() {// 每次调用后递增return romanNumerals[ordinality++];
}function makeKing(name = 'Henry', numerals = getNumerals()) {return `King ${name} ${numerals}`;
}console.log(makeKing());                // 'King Henry I'
console.log(makeKing('Louis', 'XVI'));  // 'King Louis XVI'
console.log(makeKing());                // 'King Henry II'
console.log(makeKing());                // 'King Henry III'

函数的默认参数只有在函数被调用时才会求值,不会在函数定义时求值。而且,计算默认值的函数只有在调用函数但未传相应参数时才会被调用。

箭头函数同样也可以这样使用默认参数,只不过在只有一个参数时,就必须使用括号而不能省略了:

let makeKing = (name = 'Henry') => `King ${name}`;console.log(makeKing()); // King Henry

默认参数作用域与暂时性死区 

因为在求值默认参数时可以定义对象,也可以动态调用函数,所以函数参数肯定是在某个作用域中求值的。

给多个参数定义默认值实际上跟使用 let 关键字顺序声明变量一样。来看下面的例子:

function makeKing(name = 'Henry', numerals = 'VIII') {return `King ${name} ${numerals}`;
}console.log(makeKing()); // King Henry VIII

这里的默认参数会按照定义它们的顺序依次被初始化。可以依照如下示例想象一下这个过程:

function makeKing() {let name = 'Henry';let numerals = 'VIII';return `King ${name} ${numerals}`;
}

因为参数是按顺序初始化的,所以后定义默认值的参数可以引用先定义的参数。看下面这个例子:

function makeKing(name = 'Henry', numerals = name) {return `King ${name} ${numerals}`;
}console.log(makeKing()); // King Henry Henry

参数初始化顺序遵循“暂时性死区”规则,即前面定义的参数不能引用后面定义的。像这样就会抛出错误:

// 调用时不传第一个参数会报错
function makeKing(name = numerals, numerals = 'VIII') {return `King ${name} ${numerals}`;
}

参数也存在于自己的作用域中,它们不能引用函数体的作用域:

// 调用时不传第二个参数会报错
function makeKing(name = 'Henry', numerals = defaultNumeral) {let defaultNumeral = 'VIII';return `King ${name} ${numerals}`;
}

参数扩展与收集

ECMAScript 6 新增了扩展操作符,使用它可以非常简洁地操作和组合集合数据。扩展操作符最有用的场景就是函数定义中的参数列表,在这里它可以充分利用这门语言的弱类型及参数长度可变的特点。扩展操作符既可以用于调用函数时传参,也可以用于定义函数参数。

扩展参数

在给函数传参时,有时候可能不需要传一个数组,而是要分别传入数组的元素。假设有如下函数定义,它会将所有传入的参数累加起来:

let values = [1, 2, 3, 4];function getSum() {let sum = 0;for (let i = 0; i < arguments.length; ++i) {sum += arguments[i];}return sum;}

这个函数希望将所有加数逐个传进来,然后通过迭代 arguments 对象来实现累加。如果不使用扩展操作符,想把定义在这个函数这面的数组拆分,那么就得求助于 apply() 方法:

console.log(getSum.apply(null, values)); // 10

但在 ECMAScript 6 中,可以通过扩展操作符极为简洁地实现这种操作。对可迭代对象应用扩展操作符,并将其作为一个参数传入,可以将可迭代对象拆分,并将迭代返回的每个值单独传入。

比如,使用扩展操作符可以将前面例子中的数组像这样直接传给函数:

console.log(getSum(...values)); // 10

因为数组的长度已知,所以在使用扩展操作符传参的时候,并不妨碍在其前面或后面再传其他的值,包括使用扩展操作符传其他参数:

console.log(getSum(-1, ...values));          // 9
console.log(getSum(...values, 5));           // 15
console.log(getSum(-1, ...values, 5));       // 14
console.log(getSum(...values, ...[5,6,7]));  // 28

对函数中的 arguments 对象而言,它并不知道扩展操作符的存在,而是按照调用函数时传入的参数接收每一个值:

let values = [1,2,3,4]function countArguments() {console.log(arguments.length);
}countArguments(-1, ...values);          // 5
countArguments(...values, 5);           // 5
countArguments(-1, ...values, 5);       // 6
countArguments(...values, ...[5,6,7]);  // 7

arguments 对象只是消费扩展操作符的一种方式。在普通函数和箭头函数中,也可以将扩展操作符用于命名参数,当然同时也可以使用默认参数:

function getProduct(a, b, c = 1) {return a * b * c;
}let getSum = (a, b, c = 0) => {return a + b + c;
}console.log(getProduct(...[1,2]));      // 2
console.log(getProduct(...[1,2,3]));    // 6
console.log(getProduct(...[1,2,3,4]));  // 6console.log(getSum(...[0,1]));          // 1
console.log(getSum(...[0,1,2]));        // 3
console.log(getSum(...[0,1,2,3]));      // 3

收集参数

在构思函数定义时,可以使用扩展操作符把不同长度的独立参数组合为一个数组。这有点类似 arguments 对象的构造机制,只不过收集参数的结果会得到一个Array实例。

function getSum(...values) {// 顺序累加values中的所有值// 初始值的总和为0return values.reduce((x, y) => x + y, 0);
}console.log(getSum(1,2,3)); // 6

收集参数的前面如果还有命名参数,则只会收集其余的参数;如果没有则会得到空数组。因为收集参数的结果可变,所以只能把它作为最后一个参数:

// 不可以
function getProduct(...values, lastValue) {}// 可以
function ignoreFirst(firstValue, ...values) {console.log(values);
}ignoreFirst();       // []
ignoreFirst(1);      // []
ignoreFirst(1,2);    // [2]
ignoreFirst(1,2,3);  // [2, 3]

箭头函数虽然不支持arguments对象,但支持收集参数的定义方式,因此也可以实现与使用 arguments 一样的逻辑:

let getSum = (...values) => {return values.reduce((x, y) => x + y, 0);
}console.log(getSum(1,2,3)); // 6

另外,使用收集参数并不影响 arguments 对象,它仍然反映调用时传给函数的参数:

function getSum(...values) {console.log(arguments.length);  // 3console.log(arguments);         // [1, 2, 3]console.log(values);            // [1, 2, 3]
}console.log(getSum(1,2,3));

函数声明与函数表达式

本文到现在一直没有把函数声明和函数表达式区分得很清楚。事实上,JavaScript 引擎在加载数据时对它们是区别对待的。JavaScript 引擎在任何代码执行之前,会先读取函数声明,并在执行上下文中生成函数定义。而函数表达式必须等到代码执行到它那一行,才会在执行上下文中生成函数定义。来看下面的例子:

// 没问题
console.log(sum(10, 10));function sum(num1, num2) {return num1 + num2;
}

以上代码可以正常运行,因为函数声明会在任何代码执行之前先被读取并添加到执行上下文。这个过程叫作函数声明提升(function declaration hoisting)。在执行代码时,JavaScript 引擎会先执行一遍扫描,把发现的函数声明提升到源代码树的顶部。因此即使函数定义出现在调用它们的代码之后,引擎也会把函数声明提升到顶部。如果把前面代码中的函数声明改为等价的函数表达式,那么执行的时候就会出错:

// 会出错
console.log(sum(10, 10));
let sum = function(num1, num2) {return num1 + num2;
};

上面的代码之所以会出错,是因为这个函数定义包含在一个变量初始化语句中,而不是函数声明中。这意味着代码如果没有执行到加粗的那一行,那么执行上下文中就没有函数的定义,所以上面的代码会出错。这并不是因为使用let而导致的,使用var关键字也会碰到同样的问题:

console.log(sum(10, 10));
var sum = function(num1, num2) {return num1 + num2;
};

除了函数什么时候真正有定义这个区别之外,这两种语法是等价的。

注意 在使用函数表达式初始化变量时,也可以给函数一个名称,比如let sum = function sum() {}。

函数作为值

因为函数名在 ECMAScript 中就是变量,所以函数可以用在任何可以使用变量的地方。这意味着不仅可以把函数作为参数传给另一个函数,而且还可以在一个函数中返回另一个函数。来看下面的例子:

function callSomeFunction(someFunction, someArgument) {return someFunction(someArgument);
}

这个函数接收两个参数。第一个参数应该是一个函数,第二个参数应该是要传给这个函数的值。任何函数都可以像下面这样作为参数传递:

function add10(num) {return num + 10;
}let result1 = callSomeFunction(add10, 10);
console.log(result1);  // 20function getGreeting(name) {return "Hello, " + name;
}let result2 = callSomeFunction(getGreeting, "Nicholas");
console.log(result2);  // "Hello, Nicholas"

callSomeFunction() 函数是通用的,第一个参数传入的是什么函数都可以,而且它始终返回调用作为第一个参数传入的函数的结果。要注意的是,如果是访问函数而不是调用函数,那就必须不带括号,所以传给 callSomeFunction() 的必须是 add10 和 getGreeting,而不能是它们的执行结果。

从一个函数中返回另一个函数也是可以的,而且非常有用。例如,假设有一个包含对象的数组,而我们想按照任意对象属性对数组进行排序。为此,可以定义一个 sort() 方法需要的比较函数,它接收两个参数,即要比较的值。但这个比较函数还需要想办法确定根据哪个属性来排序。这个问题可以通过定义一个根据属性名来创建比较函数的函数来解决。比如:

function createComparisonFunction(propertyName) {return function(object1, object2) {let value1 = object1[propertyName];let value2 = object2[propertyName];if (value1 < value2) {return -1;} else if (value1 > value2) {return 1;} else {return 0;}};
}

这个函数的语法乍一看比较复杂,但实际上就是在一个函数中返回另一个函数,注意那个 return 操作符。内部函数可以访问 propertyName 参数,并通过中括号语法取得要比较的对象的相应属性值。取得属性值以后,再按照 sort() 方法的需要返回比较值就行了。这个函数可以像下面这样使用:

let data = [{name: "Zachary", age: 28},{name: "Nicholas", age: 29}
];data.sort(createComparisonFunction("name"));
console.log(data[0].name);  // Nicholasdata.sort(createComparisonFunction("age"));
console.log(data[0].name);  // Zachary

在上面的代码中,数组 data 中包含两个结构相同的对象。每个对象都有一个 name 属性和一个 age 属性。默认情况下,sort() 方法要对这两个对象执行 toString(),然后再决定它们的顺序,但这样得不到有意义的结果。而通过调用 createComparisonFunction("name") 来创建一个比较函数,就可以根据每个对象 name 属性的值来排序,结果 name 属性值为 "Nicholas"、age 属性值为 29 的对象会排在前面。而调用 createComparisonFunction("age") 则会创建一个根据每个对象 age 属性的值来排序的比较函数,结果 name 属性值为 "Zachary"、age 属性值为 28 的对象会排在前面。

怎么样?看到这里是不是觉得文章内容似曾相识。不知是否为你解惑一二。

看过的读者应该已经猜到了,本文内容正是来自“ JavaScript 红宝书”第10章,更多前端相关内容,小伙伴可以继续翻阅红宝书查缺补漏哦!

世界读书日,红宝书终终终终于5折啦!等等党,可以剁手了!

☟☟☟ 红宝书全年最低,只要64.5

一文探索 JavaScript 最强大特性—函数表达式相关推荐

  1. JavaScript对象和函数表达式

    JavaScript对象和函数表达式 理解对象 创建自定义对象的最简单方式就是创建一个 Object 的实例,然后再为它添加属性和方法,如下所示 var person = new Object(); ...

  2. JavaScript中的函数表达式

    在JavaScript中,函数是个非常重要的对象,函数通常有三种表现形式:函数声明,函数表达式和函数构造器创建的函数. 本文中主要看看函数表达式及其相关的知识点. 函数表达式 首先,看看函数表达式的表 ...

  3. js调用php函数兵每秒刷新,深入理解JavaScript立即调用函数表达式(IIFE)

    立即调用函数 目录一.了解立即调用函数表达式 二.立即调用函数表达式报错了? 三.使用立即调用函数的正确姿势 四.常见使用场景 写在最后 一.了解立即调用函数表达式 1.1 思维导图 1.2 什么是立 ...

  4. 一篇文章带你了解JavaScript中的函数表达式,递归,闭包,变量,this对象,模块作用域

    作者 | Jeskson 来源 | 达达前端小酒馆 定义函数的方式: 第一种为 函数声明: 第二种为 函数表达式. 语法: function functionName(arg0, arg1, arg2 ...

  5. 深入理解JavaScript系列(2):揭秘命名函数表达式(转)

    前言 网上还没用发现有人对命名函数表达式进去重复深入的讨论,正因为如此,网上出现了各种各样的误解,本文将从原理和实践两个方面来探讨JavaScript关于命名函数表达式的优缺点. 简 单的说,命名函数 ...

  6. JavaScript 函数声明,函数表达式,匿名函数,立即执行函数之区别

    函数声明:function fnName () {-};使用function关键字声明一个函数,再指定一个函数名,叫函数声明. 函数表达式 var fnName = function () {-};使 ...

  7. JS中的函数声明和函数表达式

    JS中的函数声明和函数表达式 1. 函数声明定义一个具有指定参数的函数 函数声明提升, 函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用 hoist ...

  8. Javascript 函数声明和函数表达式的区别

    2019独角兽企业重金招聘Python工程师标准>>> Javascript Function无处不在,而且功能强大!通过Javascript函数可以让JS具有面向对象的一些特征,实 ...

  9. 探索 webpack5 新特性 Module federation 引发的javascript共享模块变革

    webpack5 新特性 Module federation 引言 什么是Module federation "模块联邦" Module federation "模块联邦 ...

最新文章

  1. 继天元开源后,旷视又亮出“看家本领”:推出Brain++商业版
  2. python 程序流程控制结构-python之流程控制语句
  3. SQL Server timeout分析和总结
  4. Docker最全教程——从理论到实战(三)
  5. Python制作词云图根据蒙板图像确定形状和文字颜色
  6. linux 检测蓝牙 rssi,蓝牙LE信号强度Linux
  7. scrapy常用设置参考手册
  8. mybatis 高级映射和spring整合之与Spring整合(6)
  9. Java——常用类(String)
  10. mysql的server_id怎么设置_MySQL Server-id踩到的坑
  11. Linux C语言编程学习材料
  12. CRT、CER、PEM、DER编码、JKS、KeyStore等格式证书说明及转换
  13. apt-get 安装失败:Unable to correct problems, you have held broken packages
  14. vn.py2-行情记录的使用
  15. inux内核态文件操作filp_open/filp_close/vfs_read/vfs_write
  16. SAP 登录的一些参数
  17. layui文件上传接口后端具体实现SpringMVC
  18. 【SIM】MCC(移动国家码)和 MNC(移动网络码)
  19. 计算机重启恢复系统怎么操作,电脑只要关机重启系统就会自动还原?-电脑怎么系统还原...
  20. 【echarts实现】中国地图 世界地图

热门文章

  1. mysql基础搭建_MySql基础-构建MySql数据库:安装MySql-server、MySql-client
  2. access在哪里可以设主键_access利用DAO设置数据表的主键
  3. hadloop大数据平台论文_企业大数据平台建设过程中的问题和建议
  4. 全能音乐助手序列号_颜控最爱的全能本 惠普星14全能轻薄本深度解析
  5. tomcat 连接oracle重连,JSP+Tomcat连接Oracle数据库
  6. winscp是什么软件_winscp ftp,winscp ftp是什么软件?软件介绍
  7. Java数据类型缓存池
  8. qt 一定要指定大小吗 数组_Qt中数组的初始化 | 学步园
  9. matlab估计arma残差,写给你的金融时间序列分析:补完篇
  10. java+script+当前日期_javascript获取当前时间