概述
函数
函数是编程语言非常重要的一部分,javascript得函数从最初到现在并没有太大得改变。遗留下来的问题和有差别得行为使得程序员很容易犯错,这可能需要程序员通过编写更多的代码来实现普通得功能。
ECMAScript6的函数有了很大的改进,考虑到了很多编程人员投诉和询问的问题。这样的结果是改进了很多ECMAScript 5 JavaScript函数功能,使得编写javascript程序更少出错,功能更强大。
1、默认参数
javascript中的函数是特殊的,它允许任意多的参数传递给函数而无视函数定义中声明的参数个数。这样你可以定义一个可以操作不同参数个数的函数,通常是为没有传递的参数给定一个默认值。在ECMAScript 5或者之前的版本中,你可能会通过以下的形式来实现此功能:
function makeRequest(url, timeout, callback) {
timeout = timeout || 2000;
callback = callback || function() {};
// the rest of the function
}
在这个例子中,timeout和callback都是可选的参数,如果在调用函数中没有传递参数,就使用默认值。逻辑操作OR(||)当第一个操作数是false时就返回第二个参数。因为函数调用的时候没有提供参数,那么就是隐式的将函数参数设置为undefined,逻辑操作OR经常用来为缺失的参数提供默认值。这种方式有一个缺点,timeout有可能在调用的时候传递参数0,但是由于OR操作符中0等同于false,timeout最终的值会被2000所替代。
另一个方式来定义缺失的参数是通过检查arguments.length来确认传递的参数个数,ranhou再依次检查每个参数的值是否等于undefined。
ECMAScript 6 为定义默认的参数提供了一种更加方便的方法,例如:
function makeRequest(url, timeout = 2000, callback = function() {}) {
// the rest of the function
}
这里,在调用函数的时候只有第一个参数是必须要传递的。另外两个参数有默认值,因为不用再确认参数是否被传递,这样函数体就更加简练。当makeRequest函数调用时,传递了三个参数,那么默认参数就不起任何作用。例如
// uses default timeout and callback
makeRequest("/foo");
// uses default callback
makeRequest("/foo", 500);
// doesn't use defaults
makeRequest("/foo", 500, function(body) {
doSomething(body);
});
任何有默认值的参数都是可选参数,而没有默认值的参数则是必选参数。
可以为任意的参数提供默认值,包括在没有默认值的参数之前声明默认参数,例如
function makeRequest(url, timeout = 2000, callback) {
// the rest of the function
}
在这个例子中,timeout的默认值只有在没有第二个参数传递或者第二个参数显式的传递为undefined的时候,例如:
/ uses default timeout
makeRequest("/foo", undefined, function(body) {
doSomething(body);
});
// uses default timeout
makeRequest("/foo");
// doesn't use default timeout
makeRequest("/foo", null, function(body) {
doSomething(body);
});
在上面例子中,null被认为是传递的确认的值,所以默认参数不会被使用。
也许默认参数最有意思的地方在于默认值不一定是原始值。例如,你可以执行一个函数来返回默认参数:
function getCallback() {
return function() {
// some code
};
}
function makeRequest(url, timeout = 2000, callback = getCallback()) {
// the rest of the function
}
这里,如果最后的参数没有提供,函数getCallback将会被调用来返回正确的默认参数。这可能会给函数带来很多有趣的动态信息的注入。
2、Rest运算符
因为javascript函数可以传递任意多的参数,不需要明确的定义每一个参数。最开始,javascript利用arguments对象来检查函数的没有明确声明的参数,在大多数情况下,这个方法很好用,但是稍显笨重。例如:
function sum(first) {
let result = first,
i = 1,
len = arguments.length;
while (i < len) {
result += arguments[i];
i++;
}
return result;
}
这个函数的功能是将所有传进来的参数相加,调用 sum(1), sum(1,2,3,4)都能很好的工作。关于这个函数有几个方面需要注意,首先,虽然不明显但这个函数可以操作多余一个参数的情况,你可以添加更多的参数,但是你总达不到函数可以操作的参数的数量。第二,由于第一个参数是命名参数可以直接使用,你需要从index下标为1而不是0开始查找arguments对象,牢记使用适当的下标指数查找arguments并不是很难,但这总是多了一件事情需要追踪。ECMAScript6引入了rest运算符来解决这些问题。
在命名参数前增加(...)来表示此参数是Rest运算符。这个命名参数会转变成一个包含剩余参数的数组(这就是为什么叫做“rest”运算符)。例如,sum参数可以利用rest参数按照一下方式重写:
function sum(first, ...numbers) {
let result = first,
i = 0,
len = numbers.length;
while (i < len) {
result += numbers[i];
i++;
}
return result;
}
在这个函数中,numbers是包含了除了第一个参数的剩余参数的数组(不同于arguments,包含第一个参数在内的所有参数)。这就意味着你可以从开始到结尾遍历numbers不需要有任何的担心。更方便的是,看到这个函数的定义形式,你就知道这个函数可以操作任意多的参数。
rest运算符唯一的限制就是在rest之后不能有命名参数。例如,这样使用就是引发语法错误。
// Syntax error: Can't have a named parameter after rest parameters
function sum(first, ...numbers, last) {
let result = first,
i = 0,
len = numbers.length;
while (i < len) {
result += numbers[i];
i++;
}
return result;
}
这里,last命名参数在numbers Rest之后就引发了语法错误。
在ECMAScript中,Rest参数被设计用来替代arguments。最初,ECMAScript4确实删除了arguments,增加了rest运算符来允许任意多的参数传递给函数。尽管ECMAScript4没有投入使用,但是这个思想保留了下来,在ECMAScript6中再次引入,但是arguments对象并没有被删除。
3、结构化参数
函数经常用来处理大量的可选的参数,通常是通过一个或多个可选的对象作为参数,例如
function setCookie(name, value, options) {
options = options || {};
var secure = options.secure,
path = options.path,
domain = options.domain,
expires = options.expires;
// ...
}
setCookie("type", "js", {
secure: true,
expires: 60000
});
在javascript中有很多类似于setcookie这样的函数。name和value参数是必须的但是其余的参数不是必须的。由于其他的数据没有优先级别,这样就可以定义一个可选的对象参数而不是定义更多的命名参数。这种方法是可行的,尽管这样会使得函数有一点难以理解。
使用 结构化参数,上面的函数可以写成以下的形式:
function setCookie(name, value, { secure, path, domain, expires }) {
// ...
}
setCookie("type", "js", {
secure: true,
expires: 60000
});
这个函数的行为与前一个函数相同。最大的不同在于第三个参数利用了结构化参数来获取需要的数据。这样可以很清晰的知道那些参数是需要的。并且结构化参数和普通的参数一样,如果调用函数的时候没有传递,他们的值为undefined。
使用这样参数形式定义函数的时候,当参数没有结构化传递就会引发一个错误。如果setCookie调用的时候只传递了两个参数,会导致一个运行错误。
// Error!
setCookie("type", "js");
这段代码会抛出一个错误因为缺少第三个参数(undefined)。理解为什么会导致错误,会帮助理解结构化参数只是结构化赋值的剪短方式。实际上javascript引擎做了下面的事情:
function setCookie(name, value, options) {
var { secure, path, domain, expires } = options;
// ...
}
由于右边的表达式等于null或者undefined,结构化赋值就会抛出一个错误。同理当第三个参数没有传递的时候也会抛出错误。
想使用这种参数定义形式,你可以为结构化参数提供一个默认参数:
function setCookie(name, value, { secure, path, domain, expires } = {}) {
// ...
}
This example now works exactly the same as the first example in this section. Providing the default value for the destructured parameter means that secure
, path
, domain
, and expires
will all be undefined
if the third argument to setCookie()
isn’t provided.
这个函数与本节中第一个例子相同。为结构化参数提供一个默认参数意味着,当setCookie的第三个参数没有传递的时候,secure,path,domain,expires都等于undefined。
注意:通常建议为结构化参数提供一个默认值以避免发生错误。
4、The Spread Operator
Closely related to rest parameters is the spread operator. Whereas rest parameters allow you to specify that multiple independent arguments should be combined into an array, the spread operator allows you to specify an array that should be split and have its items passed in as separate arguments to a function. Consider the Math.max()
method, which accepts any number of arguments and returns the one with the highest value. It’s basic usage is as follows:
Spread(传播)操作符与Rest参数非常类似。Rest操作符允许你指定多个独立的参数可以组合成一个数组。传播操作符允许你指定一个数组可以被分离和将它的每一个数组项目传递给函数。想一下Math.max() 方法,可以接受任意数量的参数,返回其中最大的一个。基础的用法如下:
let value1 = 25,
value2 = 50;
console.log(Math.max(value1, value2)); // 50
When you’re dealing with just two values, as in this example, Math.max()
is very easy to use. The two values are passed in and the higher value is returned. But what if you have been tracking values in an array, and now you want to find the highest value? The Math.max()
method doesn’t allow you to pass in an array, so in ECMAScript 5 and earlier, you’d be stuck either searching the array yourself or usingapply()
:
当你只是处理两个数值的时候,就像例子中那样,Math.max()使用起来非常方便。两个数值传递给函数并返回其中最大的一个。但是如果你需要比较数组中的数值,找到其中最大的元素该怎么办?Math.max()方法不允许你传递数组,所以在ECMAScript5和之前的版本中,你需要自己搜索数组或者使用apply():
let values = [25, 50, 75, 100]
console.log(Math.max.apply(Math, values)); // 100
While possible, using apply()
in this manner is a bit confusing - it actually seems to obfuscate the true meaning of the code with additional syntax.
这种利用apply的方式有一点难以理解-这样的多余的语法形式会导致这段代码难以理解。
The ECMAScript 6 spread operator makes this case very simple. Instead of calling apply()
, you can pass in the array and prefix it with the same ...
pattern that is used with rest parameters. The JavaScript engine then splits up the array into individual arguments and passes them in:
ECMAScript6通过传播操作符使得这种情况变得非常简单。不再使用apply(),你可以传递一个数组并且在数组前加上与rest操作符一样“...”形式。javascript引擎就会把这个分离成单独的参数并把他们传递给函数:
let values = [25, 50, 75, 100]
// equivalent to
// console.log(Math.max(25, 50, 75, 100));
console.log(Math.max(...values)); // 100
Now the call to Math.max()
looks a bit more conventional and avoids the complexity of specifying a this
-binding for a simple mathematical operation.
现在调用Math.max() 更加方便,避免了为简单的数学操作复杂的this绑定。
You can mix and match the spread operator with other arguments as well. Suppose you want the smallest number returned from Math.max()
to be 0 (just in case negative numbers sneak into the array). You can pass that argument separately and still use the spread operator for the other arguments:
你可以将传播操作符和其他的参数混合在一起使用。假设你希望从Math.max()函数返回最小值是0(以免负数混入到数组中。)你可以单独的传入这个参数,然后依然为其他的参数使用传播操作符:
let values = [-25, -50, -75, -100]
console.log(Math.max(...values, 0)); // 0
In this example, the last argument passed to Math.max()
is 0
, which comes after the other arguments are passed in using the spread operator.
在这个例子中,紧随使用了传播操作符的参数,最后一个传入Math.max()的参数是0。
The spread operator for argument passing makes using arrays for function arguments much easier. You’ll likely find it to be a suitable replacement for the apply()
method in most circumstances.
为参数使用传播操作符使得为函数传递数组更加方便。你会发现在大多数情况下,这个是apply()函数一种很好的替换方法。
5、name属性
Identifying functions can be challenging in JavaScript given the various ways a function can be defined. Additionally, the prevalence of anonymous function expressions makes debugging a bit more difficult, often resulting in stack traces that are hard to read and decipher. For these reasons, ECMAScript 6 adds the name
property to all functions.
在Javascript中,由于函数定义的多样性,识别函数是非常困难的。此外,匿名函数方式的流行让调试代码也变得非常困难。这样的结果是代码很难被追踪和理解。由于这些原因,ECMAScript6 为所有的函数增加了name属性
All functions in an ECMAScript 6 program will have an appropriate value for their name
property while all others will have an empty string. For example:
ECMAScript6 中的所有函数都会为name属性获得一个适当的值。例如:
function doSomething() {
// ...
}
var doAnotherThing = function() {
// ...
};
console.log(doSomething.name); // "doSomething"
console.log(doAnotherThing.name); // "doAnotherThing"
In this code, doSomething()
has a name
property equal to "doSomething"
because it’s a function declaration. The anonymous function expression doAnotherThing()
has a name
of "doAnotherThing"
due to the variable to which it is assigned.
在这段代码中,doSomething()的name属性值为"doSomething", 因为这是函数声明。匿名函数表达式doAnotherThing也有一个name属性值为doAnotherThing,因为这是变量赋值。
While appropriate names for function declarations and function expressions are easy to find, ECMAScript 6 goes further to ensure that all functions have appropriate names:
函数声明和函数表达式的name属性很容易就获取到,ECMAScript 6 为确保每个函数都能有一个合适的name做了更多的工作:
var doSomething = function doSomethingElse() {
// ...
};
var person = {
get firstName() {
return "Nicholas"
},
sayName: function() {
console.log(this.name);
}
}
console.log(doSomething.name); // "doSomethingElse"
console.log(person.sayName.name); // "sayName"
console.log(person.firstName.name); // "get firstName"
In this example, doSomething.name
is "doSomethingElse"
because the function expression itself has a name and that name takes priority over the variable to which the function was assigned. The name
property of person.sayName()
is "sayName"
, as the value was interpreted from the object literal. Similarly, person.firstName
is actually a getter function, so its name is "get firstName"
to indicate this difference (setter functions are prefixed with "set"
as well).
在这个例子中,doSomething.name
属性是 "doSomethingElse"因为函数声明本身有一个name属性,这个name属性比变量赋值的name属性的优先级高。
person.sayName()的name属性是“sayName”,等于对象字面量的值。相似的,person.firstName实际上是一个get函数,所以他的name属性是“get firstName”来表示不同(setter函数的前缀是“set”)。
There are a couple of other special cases for function names. Functions created using bind()
will have their name prefixed with "bound"
and functions created using the Function
constructor have a name of "anonymous"
:
函数的name属性有一些特例。函数创建使用了bind(),他们的函数name属性之前会增加 “bound”,构造函数的那么属性是“anonymous”
var doSomething = function() {
// ...
};
console.log(doSomething.bind().name); // "bound doSomething"
console.log((new Function()).name); // "anonymous"
The name
of a bound function will always be the name
of the function being bound prefixed with the "bound "
, so the bound version of doSomething()
is "bound doSomething"
.
绑定函数的name属性都会增加“bound”前缀,所以doSomething的bound版本的name属性值为“bound doSomething”。
6、块级别函数
In ECMAScript 3 and earlier, a function declaration occurring inside of a block (a block-level function) was technically a syntax error, but many browsers still supported it. Unfortunately, each browser that allowed the syntax behaved in a slightly different way, so it is considered a best practice to avoid function declarations inside of blocks (the best alternative is to use a function expression).
在ECMAScript3 和 之前的版本,在块级别声明函数会导致语法错误,但是很多浏览器还是支持这种写法。不幸的是,每个浏览器支持的语法都不太一样,所以最佳实践是避免在块级别声明函数(最好的替换方法是使用函数表达式)。
In an attempt to reign in this incompatible behavior, ECMAScript 5 strict mode introduced an error whenever a function declaration was used inside of a block. For example:
为了统一这种不兼容的行为,ECMAScript 5 strict mode中块级别声明函数会导致错误,例如:
"use strict";
if (true) {
// Throws a syntax error in ES5, not so in ES6
function doSomething() {
// ...
}
}
In ECMAScript 5, this code throws a syntax error. In ECMAScript 6, the doSomething()
function is considered a block-level declaration and can be accessed and called within the same block in which it was defined. For example:
在ECMAScript 5中,这个代码会抛出语法错误。在ECMAScript 6 中,doSomething函数会被认为是块级别的函数声明,在函数定义的块级别内部可以获得和调用。例如:
"use strict";
if (true) {
console.log(typeof doSomething); // "function"
function doSomething() {
// ...
}
doSomething();
}
console.log(typeof doSomething); // "undefined"
Block level functions are hoisted to the top of the block in which they are defined, so typeof doSomething
returns "function"
even though it appears before the function declaration in the code. Once the if
block is finished executing, doSomething()
no longer exists.
块级别的函数会被提升到它所定义的块级别的顶部。,所以 “ typeof doSomething
”返回“function”,尽管它出现在函数声明之前。块级别结束,函数doSomething不再存在。
Block level functions are a similar to let
function expressions in that the function definition is removed once execution flows out of the block in which it’s defined. The key difference is that block level functions are hoisted to the top of the containing block while let
function expressions are not hoisted. For example:
块级别函数类似于let函数表达式,一旦超出函数一定块的执行范围,函数立即被移除。最主要的区别在于,块级别的函数就将提升到块的顶部,而let表达式则不会提升,例如:
"use strict";
if (true) {
console.log(typeof doSomething); // throws error
let doSomething = function () {
// ...
}
doSomething();
}
console.log(typeof doSomething);
Here, code execution stops when typeof doSomething
is executed because the let
statement hasn’t been executed yet.
这样,当代码执行到typeof doSomething的时候,会抛出错误,因为此时,let声明还没有被执行到。
Whether you want to use block level functions or let
expressions depends on whether or not you want the hoisting behavior.
使用块级别函数还是let声明的函数取决于你是否想要函数定义被提升到块级别的顶部。
ECMAScript 6 also allows block-level functions in nonstrict mode, but the behavior is slightly different. Instead of hoisting these declarations to the top of the block, they are hoisted all the way to the containing function or global environment. For example:
ECMAScript 6在nonstrict mode 模式下也可以使用块级别的函数,但是函数行为有一些不同。不再是将这些声明提升到块级别的顶部,而是,这些函数会被提升到包含他们的函数的顶部或者global环境。例如:
// ECMAScript 6 behavior
if (true) {
console.log(typeof doSomething); // "function"
function doSomething() {
// ...
}
doSomething();
}
console.log(typeof doSomething); // "function"
In this example, doSomething()
is hoisted into the global scope so that it still exists outside of the if
block. ECMAScript 6 standardized this behavior to remove the incompatible browser behaviors that previously existed. ECMAScript 6 runtimes will all behave in the same way.
在这个例子中,doSomething被提升到了global范围,所以在块范围的外面,doSomething依然存在。ECMAScript 6 标准化了这种行为,删除了之前存在的不兼容浏览器的行为。ECMAScript6在执行期间都会按照这种方式工作。
7、箭头函数
One of the most interesting new parts of ECMAScript 6 are arrow functions. Arrow functions are, as the name suggests, functions defined with a new syntax that uses an “arrow” (=>
). However, arrow functions behave differently than traditional JavaScript functions in a number of important ways:
箭头函数是ECMAScript6最有意思的模块之一。箭头函数,就像是名字所暗示的那样,函数用新的语法箭头“=>”来定义。然而,箭头函数与普通函数在行为上有很大的区别:
- Lexical
this
binding - The value ofthis
inside of the function is determined by where the arrow function is defined not where it is used. - this 绑定 - 剪头函数内部this的值决定于箭头函数定义的地方而不是使用的地方。
- Not
new
able - Arrow functions cannot be used as constructors and will throw an error when used withnew
. - 不能被 new - 箭头函数不能被用作构造函数,当被new的时候就抛出错误。
- Can’t change
this
- The value ofthis
inside of the function can’t be changed, it remains the same value throughout the entire lifecycle of the function. - this 值不能改变 - 剪头函数内部this的值不能改变,在整个函数的生命周期中,this的值都是相同的。
- No
arguments
object - You can’t access arguments through thearguments
object, you must use named arguments or other ES6 features such as rest arguments - 没有arguments 对象 - 你不能通过arguments对象来获取参数,你必须使用命名参数或者ES6的其他功能例如 rest参数。
There are a few reasons why these differences exist. First and foremost, this
binding is a common source of error in JavaScript. It’s very easy to lose track of the this
value inside of a function, which can result in unintended consequences. Second, by limiting arrow functions to simply executing code with a single this
value, JavaScript engines can more easily optimize these operations (as opposed to regular functions, which might be used as a constructor or otherwise modified).
有几个原因导致了这些不同的存在。首先也是最重要的,this绑定是javascript中最普遍的引发错误的原因。在函数内部,很容易失去this的追踪,会导致非意料的后果。第二,在箭头函数内部限制this的值唯一,javascript引擎可以很容易优化这些操作(常规函数会被用作构造函数或者其他的修改)。
注意:Arrow functions also have a name
property that follows the same rule as other functions.
注意:箭头函数也有name属性,与其他的函数name属性规则一样。
7.1、语法
The syntax for arrow functions comes in many flavors depending upon what you are trying to accomplish. All variations begin with function arguments, followed by the arrow, followed by the body of the function. Both the arguments and the body can take different forms depending on usage. For example, the following arrow function takes a single argument and simply returns it:
剪头函数的语法的形式取决于你试图完成什么。最先的变量是函数的参数,接下来是箭头(=>),接下来是函数体。取决于使用方法的不同,参数和函数体也有几种不同的形式。例如:下面的箭头函数只有一个参数并且返回它:
var reflect = value => value;
// 等同于
var reflect = function(value) {
return value;
};
When there is only one argument for an arrow function, that one argument can be used directly without any further syntax. The arrow comes next and the expression to the right of the arrow is evaluated and returned. Even though there is no explicit return
statement, this arrow function will return the first argument that is passed in.
当箭头函数只有一个参数的时候,这个参数不需要其他的语法格式直接使用。接下来是箭头和箭头右边的表达式,直接返回参数。尽管没有直接的return声明,箭头函数会返回传进来的第一个参数。
If you are passing in more than one argument, then you must include parentheses around those arguments. For example:
如果你传递的参数多于一个,那么在这些参数的外面必须加上圆括号。例如:
var sum = (num1, num2) => num1 + num2;
// effectively equivalent to:
var sum = function(num1, num2) {
return num1 + num2;
};
The sum()
function simply adds two arguments together and returns the result. The only difference is that the arguments are enclosed in parentheses with a comma separating them (same as traditional functions).
sum函数将两个参数相加,并返回结果。唯一不同的地方就是参数被圆括号包围中间用逗号分隔。
If there are no arguments to the function, then you must include an empty set of parentheses:
如果没有参数传递,必须用传递一个空括号:
var getName = () => "Nicholas";
// effectively equivalent to:
var getName = function() {
return "Nicholas";
};
When you want to provide a more traditional function body, perhaps consisting of more than one expression, then you need to wrap the function body in braces and explicitly define a return value, such as:
当你希望函数体增加更多的传统功能,也许多于一个表达式,那么你需要将函数体用花括号包围并且明确返回的值。
var sum = (num1, num2) => {
return num1 + num2;
};
// effectively equivalent to:
var
sum
=
function
(
num1
,
num2
)
{
return
num1
+
num2
;
};
You can more or less treat the inside of the curly braces as the same as in a traditional function with the exception that arguments
is not available.
剪头函数中花括号里面的代码与传统函数中的表达式基本一样,只是arguments对象不可用。
If you want to create a function that does nothing, then you need to include curly braces:
如果你希望创建一个不做任何事情的函数,你也需要用花括号将函数体包围起来。
var doNothing = () => {};
// effectively equivalent to:
var doNothing = function() {};
Because curly braces are used to denote the function’s body, an arrow function that wants to return an object literal outside of a function body must wrap the literal in parentheses. For example:
因为花括号用来表示函数体,所以如果箭头函数返回一个对象字面量,那么需要用圆括号将对象字面量包围起来。
var getTempItem = id => ({ id: id, name: "Temp" });
// effectively equivalent to:
var getTempItem = function(id) {
return {
id: id,
name: "Temp"
};
};
Wrapping the object literal in parentheses signals that the braces are an object literal instead of the function body.
在函数体内部,用圆括号将对象字面量包裹起来。这里面的花括号就代表对象字面量而不是函数体。
7.2、立即执行函数 (IIFEs)
A popular use of functions in JavaScript is immediately-invoked function expressions (IIFEs). IIFEs allow you to define an anonymous function and call it immediately without saving a reference. This pattern comes in handy when you want to create a scope that is shielded from the rest of a program. For example:
javascript中函数比较常用的一种形式是立即执行函数表达式(IIFEs)。IIFEs允许你定义一个匿名函数并且立即调用他。当你希望创建一个块,并且与其他程序分隔开的时候,这种形式很方便,例如:
let person = (function(name) {
return {
getName() {
return name;
}
};
}("Nicholas"));
console.log(person.getName()); // "Nicholas"
In this code, the IIFE is used to create an object with a getName()
method. The method uses the name
argument as the return value, effectively making name
a private member of the returned object.
在这段代码中,IIFE被用来创建一个带有getName方法的对象。方法使用那么参数并且返回name的值。 使得name参数在被返回的对象中变成一个私有成员。
You can accomplish the same thing using arrow functions so long as you wrap the arrow function in parentheses:
你可以利用箭头函数完成同样的功能,只需要将箭头函数用圆括号包裹起来:
let person = ((name) => {
return {
getName() {
return name;
}
};
})("Nicholas");
console.log(person.getName()); // "Nicholas"
Note that the location of the parentheses in this example is different from the previous. The previous example using the function
keyword wraps parentheses around the entire expression, including passing the argument "Nicholas"
to the function. This example has the parentheses around just the arrow function and then passes the argument.
注意本例中圆括号的位置与之前例子的不同。之前是例子使用了function关键字,并且用圆括号包裹了整个表达式,包裹传递的参数“Nicholas”,本例中,圆括号只是包裹了箭头函数,然后传递参数。
7.3、this绑定
One of the most common areas of error in JavaScript is the binding of this
inside of functions. Since the value of this
can change inside of a single function depending on the context in which it’s called, it’s possible to mistakenly affect one object when you meant to affect another. Consider the following example:
javascript中经常引起错误的一个地方是函数内部的this绑定。因为同一个函数内部的this值可能会根据调用函数的上下文产生变化,当你想改变一个对象的时候,可能会错误的影响了另一个。考虑下面的例子:
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", function(event) {
this.doSomething(event.type); // error
}, false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
In this code, the object PageHandler
is designed to handle interactions on the page. The init()
method is called to set up the interactions and that method in turn assigns an event handler to call this.doSomething()
. However, this code doesn’t work as intended. The call to this.doSomething()
is broken because this
is a reference to the element object (in this case document
) that was the target of the event, instead of being bound to PageHandler
. If you tried to run this code, you will get an error when the event handler fires because this.doSomething()
doesn’t exist on the target document
object.
在这段代码中,PageHandler对象用来处理页面上的交互。init方法用来设置交互和最终这个方法调用this.doSomething并传递event事件。然后这段代码不会像预想的那样工作。调用this.doSomething会中断,由于this是对event的target元素的引用(本例中是document),而不是PageHandler。如果你试图执行这段代码,当event处理器被触发的时候会抛出一个错误,因为document对象上不存在this.doSomething方法。
You can bind the value of this
to PageHandler
explicitly using the bind()
method on the function:
你可以通过显示的使用bind方法将this的值绑定到PageHandler:
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", (function(event) {
this.doSomething(event.type); // no error
}).bind(this), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
Now the code works as expected, but may look a little bit strange. By calling bind(this)
, you’re actually creating a new function whose this
is bound to the current this
, which is PageHandler
. The code now works as you would expect even though you had to create an extra function to get the job done.
现在这段代码会像预想那样工作,但是看起来有一点奇怪。通过调用bind(this),你实际上是创建了一个新的函数,这个函数的this值绑定到当前的this也就是PageHandler。这个代码可以像预想那样工作尽管你不得不创建一个额外的函数来完成工作。
Arrow functions have implicit this
binding, which means that the value of this
inside of an arrow function is always the same as the value of this
in the scope in which the arrow function was defined. For example:
箭头函数this值隐式的被绑定了,意味着箭头函数内部的this值一直等于箭头函数定义的时候的this值,例如:
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click",
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
The event handler in this example is an arrow function that calls this.doSomething()
. The value of this
is the same as it is within init()
, so this version of the example works similarly to the one using bind()
. Even though the doSomething()
method doesn’t return a value, it is still the only statement executed necessary for the function body and so there is no need to include braces.
本例中的事件处理程序是箭头函数调用this.doSomething。this的值等于init的this值,所以这段代码中的示例和使用bind方法的相似。尽管doSomething方法没有返回值,它仍然是唯一的执行语句,所以不需要花括号。
Arrow functions are designed to be “throwaway” functions and so cannot be used to define new types. This is evident by the missing prototype
property that regular functions have. If you try to use the new
operator with an arrow function, you’ll get an error:
箭头函数被设计成“一次性”的函数,所以不能用来定义新的类型。理由是它缺少普通函数拥有的prototype属性。如果你试图new一个箭头函数,你会得到一个错误:
var MyType = () => {},
object = new MyType(); // error - you can't use arrow functions with 'ne
w'
Also, since the this
value is statically bound to the arrow function, you cannot change the value of this
using call()
, apply()
, or bind()
.
而且,对于箭头函数来说,this的值是固定的,你不能通过call(),apply(),bind()来改变this的值。
The concise syntax for arrow functions makes them ideal for use with array processing. For example, if you want to sort an array using a custom comparator, you typically write something like this:
箭头函数简单的语法功能使得它们适合使用数组处理。例如,如果你想使用一个比较器来对数组进行排序,你可能会编写如下的代码:
var result = values.sort(function(a, b) {
return a - b;
});
That’s a lot of syntax for a very simple procedure. Compare that to the more terse arrow function version:
一个简单的程序需要很对的语法。与箭头函数的版本相比较:
var result = values.sort((a, b) => a - b);
The array methods that accept callback functions such as sort()
, map()
, and reduce()
all can benefit from simpler syntax with arrow functions to change what would appear to be more complex processes into simpler code.
允许回调函数的数组方法例如:sort、map、reduce等都可以通过简单的箭头函数语法把原本复杂的程序变得更加简单。
Generally speaking, arrow functions are designed to be used in places where anonymous functions have traditionally been used. They are not really designed to be kept around for long periods of time, hence the inability to use arrow functions as constructors. Arrow functions are best used for callbacks that are passed into other functions, as seen in the examples in this section.
通常来讲,箭头函数一般用在匿名函数使用的地方。它们不是被设计成可以使用很长的一段时间,因此箭头函数不能用作构造函数。箭头函数最适合用作回调函数作为参数传入其他的函数中。
7.4、arguments 绑定
Even though arrow functions don’t have their own arguments
object, it’s possible for them to access the arguments
object from a containing function. That arguments
object is then available no matter where the arrow function is executed later on. For example:
尽管箭头函数自己没有arguments对象,它可以通过包含它的函数获取到arguments对象。不管箭头函数之后在哪里执行都能使用这个arguments对象,例如:
function createArrowFunctionReturningFirstArg() {
return () => arguments[0];
}
var arrowFunction = createArrowFunctionReturningFirstArg(5);
console.log(arrowFunction()); // 5
Inside of createArrowFunctionReturningFirstArg()
, arguments[0]
is referenced by the created arrow function. That reference contains the first argument passed to createArrowFunctionReturningFirstArg()
. When the arrow function is later executed, it returns 5
, which was the first argument passed in to createArrowFunctionReturningFirstArg()
. Even though the arrow function is no longer in the scope of the function that created it, arguments
remains accessible as a lexical binding.
createArrowFunctionReturningFirstArg函数内部,通过创建一个箭头函数引用arguments[0]。这个引用包含第一个传给createArrowFunctionReturningFirstArg函数的参数。之后箭头函数被执行,它返回5,是第一个传给createArrowFunctionReturningFirstArg的参数。尽管箭头函数不再它创建的范围内,通过绑定arguments依然可以获得到。
7.6、识别箭头函数
Despite the different syntax, arrow functions are still functions and are identified as such:
尽管有不同的语法,箭头函数依然是函数,通过以下的方法识别:
var comparator = (a, b) => a - b;
console.log(typeof comparator); // "function"
console.log(comparator instanceof Function); // true
Both typeof
and instanceof
behave the same with arrow functions as they do with other functions.
箭头函数的typeof和instanceof与其他函数的行为一致。
Also like other functions, you can still use call()
, apply()
, and bind()
, although the this
-binding of the function will not be affected. Here are some examples:
同其他函数一样,你也可以使用call、apply、bind,尽管函数this绑定不会受到影响。下面是一些例子:
var sum = (num1, num2) => num1 + num2;
console.log(sum.call(null, 1, 2)); // 3
console.log(sum.apply(null, [1, 2])); // 3
var boundSum = sum.bind(null, 1, 2);
console.log(boundSum()); // 3
In this example, the sum()
function is called using call()
and apply()
to pass arguments as you would with any function. The bind()
method is used to create boundSum()
, which has its two arguments bound to 1
and 2
so that they don’t need to be passed directly.
在这个例子中,与其他函数一样,sum函数通过call和apply来传递参数。bind函数用来创建boundsum函数,传递了两个参数1和2,这样它们就不需要直接传参了。
Arrow functions are appropriate to use anywhere you’re currently using an anonymous function expression, such as with callbacks.
箭头函数可以用于任何匿名函数表达式使用的地方,例如回调函数。
转载于:https://www.cnblogs.com/avivaWang/p/4305226.html
最后
以上就是年轻电源为你收集整理的ECMAScript6 Function函数的全部内容,希望文章能够帮你解决ECMAScript6 Function函数所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复