JavaScript < ES5、ES6、ES7、… >ECMAScript 6

ES6(六):函数扩展

2018-11-25  本文已影响25人  CodeMT

前面的话


函数是所有编程语言的重要组成部分,在ES6出现前,JS的函数语法一直没有太大的变化,从而遗留了很多问题,导致实现一些基本的功能经常要编写很多代码。ES6大力度地更新了函数特性,在ES5的基础上进行了许多改进,使用JS编程可以更少出错,同时也更加灵活。本文将详细介绍ES6函数扩展

形参默认值

Javascript函数有一个特别的地方,无论在函数定义中声明了多少形参,都可以传入任意数量的参数,也可以在定义函数时添加针对参数数量的处理逻辑,当已定义的形参无对应的传入参数时为其指定一个默认值

【ES5模拟】

functionmakeRequest(url, timeout, callback) {
timeout = timeout || 2000;
callback = callback ||function() {}; // 函数的剩余部分}

functionmakeRequest(url, timeout, callback) {
timeout = (typeoftimeout !== "undefined") ? timeout : 2000;
callback = (typeof callback !== "undefined") ? callback : function() {}; // 函数的剩余部分}

【ES6默认参数】

function makeRequest(url, timeout = 2000, callback = function() {}) { // 函数的剩余部分}

// 使用默认的 timeout 与 callbackmakeRequest("/foo");// 使用默认的
callbackmakeRequest("/foo", 500);// 不使用默认值makeRequest("/foo", 500, function(body) {
doSomething(body);
});

【触发默认值】

functionmakeRequest(url, timeout = 2000, callback) {
console.log(url);
console.log(timeout);
console.log(callback);
}

functionmakeRequest(url, timeout = 2000, callback) {
console.log(timeout);
}
makeRequest("/foo");//2000makeRequest("/foo", undefined);//2000makeRequest("/foo", null);//nullmakeRequest("/foo", 100);//100

// SyntaxError: Duplicate parameter name not allowed in this contextfunctionfoo(x, x, y = 1) { // ...}

let x = 99;functionfoo(p = x + 1) {
console.log(p);
}
foo() // 100x = 100;
foo() // 101

【length属性】

(function (a) {}).length // 1(function (a = 5) {}).length // 0(function (a, b, c = 5) {}).length // 2

(function(...args) {}).length // 0

(function (a = 0, b, c) {}).length // 0(function (a, b = 1, c) {}).length // 1

【arguments】

functionmixArgs(first, second) {
console.log(first === arguments[0]);//true
console.log(second === arguments[1]);//true
first = "c";
second = "d";
console.log(first === arguments[0]);//true
console.log(second === arguments[1]);//true
}
mixArgs("a", "b");

function mixArgs(first, second) { "use strict";
console.log(first === arguments[0]);//true
console.log(second === arguments[1]);//true
first = "c";
second = "d"
console.log(first === arguments[0]);//false
console.log(second === arguments[1]);//false
}
mixArgs("a", "b");

// 非严格模式functionmixArgs(first, second = "b") {
console.log(first);//a
console.log(second);//b
console.log(arguments.length);//1
console.log(arguments[0]);//a
console.log(arguments[1]);//undefined
first = 'aa';
arguments[1] = 'b';
console.log(first);//aa
console.log(second);//b
console.log(arguments.length);//1
console.log(arguments[0]);//a
console.log(arguments[1]);//b}
mixArgs("a");

【默认参数表达式】

function getValue() { return5;
}functionadd(first, second = getValue()) { returnfirst +second;
}
console.log(add(1, 1));// 2console.log(add(1)); // 6

let value = 5;function getValue() { returnvalue++;
}functionadd(first, second = getValue()) { returnfirst +second;
}
console.log(add(1, 1));// 2console.log(add(1)); // 6console.log(add(1)); // 7

function add(first, second = first) { returnfirst +second;
}
console.log(add(1, 1));// 2console.log(add(1)); // 2

function getValue(value) { returnvalue + 5;
}functionadd(first, second = getValue(first)) { returnfirst +second;
}
console.log(add(1, 1));// 2console.log(add(1)); // 7

function add(first = second, second) { returnfirst +second;
}
console.log(add(1, 1));// 2console.log(add(undefined, 1)); // 抛出错误

【临时死区】

function getValue(value) { returnvalue + 5;
}functionadd(first, second = getValue(first)) { returnfirst +second;
}
console.log(add(1, 1));// 2console.log(add(1)); // 7

// JS 调用 add(1, 1) 可表示为let first = 1;
let second = 1;// JS 调用 add(1) 可表示为let first = 1;
let second = getValue(first);

function add(first = second, second) { returnfirst +second;
}
console.log(add(1, 1));// 2console.log(add(undefined, 1)); // 抛出错误

// JS 调用 add(1, 1) 可表示为let first = 1;
let second = 1;// JS 调用 add(1) 可表示为let first =second;
let second = 1;

【形参与自由变量】

let x = 1;functionf(y = x) {}
f() // 1

let x = 1;functionf(y = x,x) {}
f()// ReferenceError: x is not defined

类似地,下列代码也报错

let x = 1;functionfoo(x = x) {}
foo() // ReferenceError: x is not defined

不定参数

【ES5】

functionpick(object) {
let result = Object.create(null); // 从第二个参数开始处理
for(let i = 1, len = arguments.length; i < len; i++) {
result[arguments[i]] =object[arguments[i]];
} returnresult;
}
let book ={
title: "ES6",
author: "huochai",
year: 2017};
let bookData = pick(book, "author", "year");
console.log(bookData.author); // "huochai"console.log(bookData.year); // 2017

【ES6】

functionpick(object, ...keys) {
let result = Object.create(null); for(let i = 0, len = keys.length; i < len; i++) {
result[keys[i]] =object[keys[i]];
} returnresult;
}

【使用限制】

1、每个函数最多只能声明一个不定参数,而且一定要放在所有参数的末尾

// 语法错误:不能在剩余参数后使用具名参数functionpick(object, ...keys, last) {
let result = Object.create(null); for(let i = 0, len = keys.length; i < len; i++) {
result[keys[i]] =object[keys[i]];
} returnresult;
}

2、不定参数不能在对象字面量的setter 属性中使用

let object = {
// 语法错误:不能在 setter 中使用剩余参数 set name(...value) { // 一些操作}
};

【arguments】

functioncheckArgs(n,...args) {
console.log(args.length);//2
console.log(arguments.length);//3
console.log(args);//['b','c']
console.log(arguments);//['a','b','c']}
checkArgs("a", "b", "c");

【应用】

// arguments变量的写法function sortNumbers() {
returnArray.prototype.slice.call(arguments).sort();
}// 不定参数的写法const sortNumbers = (...numbers) => numbers.sort();

展开运算符

let value1 = 25,
value2 = 50;
console.log(Math.max(value1, value2)); // 50

let values = [25, 50, 75, 100]
console.log(Math.max.apply(Math, values)); // 100

let values = [25, 50, 75, 100]// 等价于 console.log(Math.max(25, 50, 75, 100));console.log(Math.max(...values)); // 100

let values = [-25, -50, -75, -100] console.log(Math.max(...values, 0));// 0

严格模式

function doSomething(a, b) { 'use strict'; // code}

// 报错functiondoSomething(a, b = a) { 'use strict'; // code}// 报错const doSomething = function ({a, b}) { 'use strict'; // code};// 报错const doSomething = (...a) => { 'use strict'; // code};
const obj = { // 报错 doSomething({a, b}) { 'use strict'; // code} };

// 报错functiondoSomething(value = 070) { 'use strict'; returnvalue; }

'use strict';functiondoSomething(a, b = a) { // code}

2、把函数包在一个无参数的立即执行函数里面

const doSomething = (function () { 'use strict'; return function(value = 42) { returnvalue; }; }());

构造函数

var add = new Function("first", "second", "return first + second"); console.log(add(1, 1)); // 2

var add = new Function("first", "second = first","return first + second");
console.log(add(1, 1)); // 2
console.log(add(1)); // 2

var pickFirst = new Function("...args", "return args[0]");
console.log(pickFirst(1, 2)); // 1

参数尾逗号

functionclownsEverywhere(
param1,
param2
) { /* ... */}
clownsEverywhere( 'foo', 'bar');

functionclownsEverywhere(
param1,
param2,
) { /* ... */}
clownsEverywhere( 'foo', 'bar',
);

name属性

function doSomething() { // ...}var doAnotherThing = function() { // ...};
console.log(doSomething.name); // "doSomething"console.log(doAnotherThing.name); //
"doAnotherThing"

【特殊情况】

var doSomething = function doSomethingElse() { // ...};varperson ={
get firstName() { return"huochai"
},
sayName: function() {
console.log(this.name);
}
}
console.log(doSomething.name); // "doSomethingElse"console.log(person.sayName.name); // "sayName"vardescriptor = Object.getOwnPropertyDescriptor(person, "firstName"); console.log(descriptor.get.name); // "get firstName"

var doSomething = function() { // ...};
console.log(doSomething.bind().name); // "bound doSomething"console.log((new Function()).name); // "anonymous"

判断调用

function Person(name) { this.name =name; }var person = newPerson("huochai");varnotAPerson = Person("huochai"); console.log(person); // "[Object object]"console.log(notAPerson); // "undefined"

【ES5判断函数被调用】

function Person(name) { if (this instanceof Person) { this.name = name; // 使用 new }else { throw newError("You must use new with Person.") }
}var person = newPerson("huochai");var notAPerson = Person("huochai"); // 抛出错误

function Person(name) { if (this instanceof Person) { this.name = name; // 使用 new }else { throw newError("You must use new with Person.") }
}var person = newPerson("huochai");var notAPerson = Person.call(person, "huochai"); // 不报错

【元属性new.target】

function Person(name) { if (typeof new.target !== "undefined") { this.name = name; // 使用 new }else { throw newError("You must use new with Person.") }
}var person = newPerson("huochai");var notAPerson = Person.call(person, "match"); // 出错!

function Person(name) { if (new.target === Person) { this.name = name; // 使用 new }else { throw newError("You must use new with Person.") }
}functionAnotherPerson(name) { Person.call(this, name); }var person = newPerson("huochai");var anotherPerson = new AnotherPerson("huochai"); // 出错!

[注意]在函数外使用new.target是一个语法错误

块级函数

"use strict";if (true) { // 在 ES5 会抛出语法错误, ES6 则不会 function doSomething() { // ...} }

"use strict";if (true) { console.log(typeof doSomething); // "function"
function doSomething() { // ...}
doSomething();
}
console.log(typeof doSomething); // "undefined"

【使用场景】

"use strict";if (true) { console.log(typeof doSomething); // 抛出错误 let doSomething =function () { // ...} doSomething(); }
console.log(typeofdoSomething);

【非严格模式】

// ES6 behaviorif (true) { console.log(typeof doSomething); // "function" function doSomething() { // ...} doSomething(); }
console.log(typeof doSomething); // "function"

箭头函数

【语法】

var reflect = value => value;// 有效等价于:var reflect = function(value) { returnvalue; };

var sum = (num1, num2) => num1 + num2;// 有效等价于:var sum = function(num1, num2) { returnnum1 +num2; };

vargetName = () => "huochai";// 有效等价于:var getName = function() { return"huochai"; };

var sum = (num1, num2) => { returnnum1 +num2; };// 有效等价于:var sum = function(num1, num2) { returnnum1 +num2; };

var doNothing = () => {};// 有效等价于:var doNothing = function() {};

vargetTempItem = id => ({ id: id, name: "Temp" });// 有效等价于:var getTempItem = function(id) { return{ id: id, name: "Temp" }; };

【IIFE】

let person = function(name) { return{ getName: function() { returnname; } }; }("huochai");
console.log(person.getName()); // "huochai"

let person = ((name) => { return{ getName: function() { returnname; } }; })("huochai");
console.log(person.getName()); // "huochai"

【this】

varPageHandler ={
id: "123456",
init: function() {
document.addEventListener("click",function(event) { this.doSomething(event.type); // 错误
},false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " +this.id);
}
};

varPageHandler ={
id: "123456",
init: function() {
document.addEventListener("click", (function(event) { this.doSomething(event.type); // 错误
}).bind(this), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " +this.id);
}
};

varPageHandler ={
id: "123456",
init: function() {
document.addEventListener("click",
event =>this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " +this.id);
}
};

var MyType = () =>{}, object =new MyType(); // 错误:不能对箭头函数使用 'new'

【数组】

var result = values.sort(function(a, b) { returna -b; });

只想实现一个简单功能,但这些代码实在太多了。用箭头函数简化如下

varresult = values.sort((a, b) => a - b);

// 正常函数写法[1,2,3].map(function (x) { returnx *x;
});// 箭头函数写法[1,2,3].map(x => x * x);

【arguments】

function createArrowFunctionReturningFirstArg() { return() => arguments[0];
}vararrowFunction = createArrowFunctionReturningFirstArg(5);
console.log(arrowFunction()); // 5

【辨识方法】

varcomparator = (a, b) => a -b;
console.log(typeof comparator); // "function"console.log(comparator instanceof Function); // true

varsum = (num1, num2) => num1 +num2;
console.log(sum.call(null, 1, 2)); // 3console.log(sum.apply(null, [1, 2])); // 3var boundSum = sum.bind(null, 1, 2);
console.log(boundSum()); // 3

【函数柯里化】

function add(x){ return function(y){ returny +x; }; } varaddTwo = add(2); addTwo(3);
// => 5add(10)(11); // => 21

使用ES6的语法来写,如下所示

varadd = (x) => (y) => x+y

*尾调用优化

function doSomething() { return doSomethingElse(); // 尾调用}

"use strict";function doSomething() { // 被优化 returndoSomethingElse(); }

"use strict";function doSomething() { // 未被优化:缺少 returndoSomethingElse(); }

"use strict";function doSomething() { // 未被优化:在返回之后还要执行加法 return1 +doSomethingElse(); }

"use strict";function doSomething() { // 未被优化:调用并不在尾部 varresult = doSomethingElse(); returnresult; }

"use strict";function doSomething() { varnum = 1, func = () => num; // 未被优化:此函数是闭包 returnfunc(); }

【应用】

function factorial(n) { if(n <= 1) { return1;
} else { // 未被优化:在返回之后还要执行乘法
returnn * factorial(n - 1);
}
}

function factorial(n, p = 1) { if(n <= 1) { return1 *p;
} else{
let result = n * p; // 被优化
returnfactorial(n - 1, result);
}
}

function Fibonacci (n) { if ( n <= 1 ) {return 1}; returnFibonacci(n - 1) + Fibonacci(n - 2); }
Fibonacci(10)// 89Fibonacci(100) // 堆栈溢出Fibonacci(500) // 堆栈溢出

尾递归优化过的Fibonacci 数列实现如下

function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; returnFibonacci2 (n - 1, ac2, ac1 +ac2); }
Fibonacci2(100)// 573147844013817200000Fibonacci2(1000) // 7.0330367711422765e+208Fibonacci2(10000) // Infinity

上一篇 下一篇

猜你喜欢

热点阅读