第6章 函数
函数是一组语句的集合,它是一个独立运行的程序单元。
每个函数都有一个函数体,它是构成该函数的一组语句集合。
function sayHello(){
//这是函数体;从一个左花括号开始,到右花括号结束
}
6.1 返回值
函数调用是一种表达式。
在函数体中,return 关键字会立即结束函数并且返回一个特定值,这就是函数调用产生的值。
如果没有明确指定 return 语句,返回值会是 undefined。
6.2 引用调用
函数是一个对象,可以被传递和赋值。
在函数名后添加圆括号,JavaScript就会调用函数并执行函数体,最后返回结果。如果没有圆括号,就只是在引用函数。
可以把函数赋值给一个变量或对象的属性,或把函数添加到数组里。
function getGreeting(){
return "hello world";
}
//函数赋值给变量
const f = getGreeting;
//函数添加到对象的属性里
const o = {};
o.f = getGreeting;
o.f();
//函数添加到数组里
const arr = [1, 2, 3];
arr[1] = getGreeting;
arr[1]();
6.3 函数参数
参数是指那些在函数调用结束后就不再存在的变量。
参数只存在于函数内部,即使它们在函数外有相同的名字。
6.3.1 参数会让函数有所区别吗
不会,可以使用任意多个参数去调用任何一个函数。如果调用时没有传入参数,函数就会接收到一个为undefined的参数。
function f(x){
return `the ${x}`;
}
f(); //the undefined
6.3.2 解构参数
把一个对象解构到不同的变量中:
//传入的对象属性名要与参数属性名匹配,否则属性会接受一个undefined值
function getSentence({subject,verb,object}){
return `${subject} ${verb} ${object}`;
}
const o = {
subject: "I",
verb: "love",
object: "JavaScript"
};
getSentence(o); // I love JavaScript
解构一个数组:
function getSentence([subject
,verb
,object]){
return `${subject} ${verb} ${object}`
;
}
const arr = ["I"
, "love"
, "JavaScript"]
;
getSentence()
;
使用展开操作符( ... )来收集任何多出来的参数:
function addPrefix(prefix, ...words){
const prefixedWords = [];
for(let i=0; i<words.length; i++){
prefixedWords[i] = prefix + words[i];
}
return prefixedWords;
}
addPrefix("con","verse", "vex"); //
["converse", "convex"]
6.3.3 默认参数
ES6新特性。
如果没有给参数传值,它的值将会是undefined。默认值则可以给这些参数指定其他的默认值。
function f(a, b = "default", c= 3){
return `${a} - ${b} - ${c}`;
}
f(5, 6, 7); //5 - 6 - 7
f(5, 6); //5 - 6 - 3
f(5); //5 - default - 3
f(); //undefined - default -3
6.4 函数作为对象属性
当函数作为对象的属性时,通常称为方法。
const person = {
name: "liang",
say: function(){
return "Hi";
}
}
//或者使用ES6新的快捷语法
const person = {
name: "liang",
say(){return "Hi";}
}
6.5 this 关键字
在函数体中,有一个特殊的只读字段 this,通常与面向对象编程一起出现。
this 关键字通常关联那些作为对象属性的函数。
当方法被调用时,this 关键字的值就是被调用的对象:
const person = {
name: "liang",
speak(){
return `My name is ${this.name}`;
}
}
person.speak();
const say = person.speak;
say(); //My name is
如何绑定 this 是由方法如何被调用来决定的,而非函数定义所决定的。在这里,this 绑定到 person 上并不是因为 speak 是 person 的属性,而是因为它直接由 person 调用( person.speak )。say调用时,由于JavaScript并不知道原始函数是指 person 中定义的,所以this 将会绑上undefined。
const person = {
name: "liang",
greetBackwards: function(){
function getReverseName(){
let nameBackwards = '';
for(let i=this.name.length-1; i>=0; i--){
nameBackwards += this.name[i];
}
return nameBackwards;
}
return `${getReverseName()} si eman ym, olleH`;
},
};
person.greetBackwards(); //
" si eman ym, olleH"
当调用person.greetBackwards()时,JavaScript像预期那样给this绑定了值。然而,在greetBackwards中调用 getReverseName 函数时,this 却绑到其他地方。
把this 赋值给另一个变量来解决(也可以使用箭头函数解决):
const person = {
name: "liang",
greetBackwards: function(){
const self = this;
function getReverseName(){
let nameBackwards = '';
for(let i=
self.name.length-1; i>=0; i--){
nameBackwards +=
self.name[i];
}
return nameBackwards;
}
return `${getReverseName()} si eman ym, olleH`;
},
};
person.greetBackwards(); //
"gnail si eman ym, olleH"
6.6 函数表达式和匿名函数
//函数声明
function f(){
//...
}
//函数表达式
const f = function(){
//...
};
函数表达式创建了一个匿名函数并把它赋给一个变量。匿名函数可以作为其他函数或方法的参数,还可以用来创建对象的函数属性。
6.7 箭头符号
ES6新语法。
箭头符号可以减少敲击function 这个单词和花括号的次数。
如果函数只有一个参数,则可以省略花括号。
如果函数体是一个单独表达式,则可以省略花括号和返回语句。
const f1 = function(){ return "hello!";}
//等效于
const f1 = () => "hello!";
const f2 = function(name){ return `hello,${name}!`;}
//等效于
const f2 = name =>`hello,${name}!`;
const f3 = function(a,b){ return a + b;}
//等效于
const f3 = (a,b) => a+b;
箭头函数跟普通函数之间的主要区别是:this 是跟语句绑定起来的,就像其他变量一样。
通过箭头函数,可以在函数体中使用 this:
const person = {
name: "liang",
greetBackwards: function(){
const getReverseName = () => {
let nameBackwards = '';
for(let i=this.name.length-1; i >= 0; i--){
nameBackwards += this.name[i];
}
return nameBackwards;
};
return `${getReverseName()} si eman ym, olleH`;
}
};
person.greetBackwards(); //
"gnail si eman ym, olleH"
箭头函数不能当做对象构造器,同时,指定的参数变量在箭头函数中也不生效。
6.8 调用、请求和绑定
call() 方法
call() 方法使用指定的this 来调用函数。
const bruce = { name: "Bruce"};
const madeline = { name: "Madeline"};
function greet(){
return `Hello, I'm ${this.name}!`;
}
greet(); //
"Hello, I'm !"
greet.call(bruce); //
"Hello, I'm Bruce!"
greet.call(madeline); //
"Hello, I'm madeline!"
call() 方法第一个参数表示要传入this的的对象,剩下的参数则变成了要调用的函数的实参。
const bruce = { name: "Bruce"};
function update(birthYear, occupation){
this.birthYear = birthYear;
this.occupation = occupation;
}
update.call(bruce, 1949,"singer");
bruce; //
{name: "Bruce", birthYear: 1949, occupation: "singer"}
apply() 方法
除了处理函数参数的方式不同,apply 与 call 基本是一致的。
apply 以数组的方式获取参数:
//...接上例...
update.apply(bruce,[1933,"actor"]);
bruce; //{name: "Bruce", birthYear: 1933, occupation: "actor"}
bind() 方法
bind() 可以给一个函数永久地绑定 this 值。
//...接前例...
const updateBruce = update.bind(bruce);
updateBruce(1904,"actor");
bruce; //{name: "Bruce", birthYear: 1904, occupation:"actor"}
updateBruce.call(madeline,1274,"king");
bruce; //
{name: "Bruce", birthYear:1274, occupation:"king"}
风险:使用bind后,函数不能再有效的使用call、apply,bind也不能再次使用。
调用bind时也可以传参数。传入的参数不能再修改。