6.1函数简介
函数是一组语句的集合,它是一个独立运行的程序单元,本质上,它是一段子程序。函数是JavaScript的核心。
每个函数都有一个函数体,它是构成该函数的一组语句集合。
function sayHello(){//这是函数体console.log("Hello World11111");console.log("Hello World22222");console.log("Hello World33333");}//调用sayHello();
6.2函数的传参
给函数调用传值的主要途径称为函数参数,函数参数可以给函数内部传值,参数是在函数调用后就不存在的变量。
//a和b称为形参function avg(a, b) {return (a + b) / 2;}//实际参数avg(5,10);
6.3返回值
返回值,在函数体中,return 关键字会立即结束函数,并且返回一个特定值。
function getGreeting(){return "Hello World";}let str = getGreeting();console.log(str);
6.4参数列表
在其他编程语言中,一个函数的签名会包含它的参数列表。
比如,在C语言中,函数f()(没有参数)和函数f(x)(包含一个参数)是两个不同的函数。
但是JavaScript则对此不作区分,当有一个名为f的函数时,调用时可以给它0个、1个,甚至是10个参数,
不管调用时有多少参数,都始终调用了同一个函数。
function f(x){return `in f:x=${x}`;}console.log(f());console.log(f(1));
6.5引用调用
在JavaScript中,函数是一个对象,像其他对象一样,可以被传递和赋值。所以理解函数调用和引用之间的差别很重要。当在函数名后面添加小括号是,JavaScript就知道要调用它,然后执行函数体,并返回结果。如果没有小括号,仅仅是在引用这个函数,并没有调用它。
function getGreeting() {return "Hello World";}getGreeting(); //调用函数getGreeting //引用函数const o = {};o.f = getGreeting;o.f();console.log(o.f());//把函数添加到数组里const arr = [11, 12, 13];arr[1] = getGreeting;arr[1]();console.log(arr[1]());
6.6解构参数
在解构赋值中,参数属性名必须是字符串标识符,而且如果传入的对象不存在与参数属性名匹配的属性,该属性将会接受一个undefined值。
//把一个对象解构到不通的变量中function getSentence1({subject,verb,object}) {return `${subject} ${verb} ${object}`;}const o = {subject: "I",verb: "love",object: "JavaScript"};console.log(getSentence1(o));//解构一个数组const arr = ["I", "love", "JavaScript"];function getSentence2([subject,verb,object]) {return `${subject} ${verb} ${object}`;}console.log(getSentence2(arr));
6.6展开操作符(…)
注意,如果在函数定义中使用展开操作符,它必须是最后一个参数。
//展开操作符...匹配任何多出来的参数function addPrefix(prefix, ...words) {const prefixedWords = [];for (let i = 0; i < words.length; i++) {prefixedWords[i] = prefix + words[i];}return prefixedWords;}console.log(addPrefix("con", "verse", "vex"));//如果在函数定义中使用展开操作符,它必须是最后一个参数。
6.7默认参数
ES6中的一个新特性是可以指定参数的默认值。通常,如果没有给参数传值,它的值将会是undefined。默认值则可以给这些参数指定其他的默认值。
function f(a, b = "default", c = 3) {return `${a}-${b}-${c}`;}console.log(f(5,6,7)); //5-6-7console.log(f(5,6)); //5-6-3console.log(f(5)); //5-default-3console.log(f()); //undefined-default-3
6.8值传递和引用传递
//基本类型的值传递function f(x){console.log("方法里的值:"+x);x = 5;console.log("方法里修改后值:"+x);}let x = 3;console.log("原始值:"+3);f(x);//对象类型的,引用传递function f(o) {o.message = "f方法改变了值";}let o = {message: "原始值"}console.log("o.message" + o.message);f(o);
6.9函数和方法
当函数作为一个对象的属性时,通常被称为方法
const o = {name: 'Wallace', //属性bark: function() { //方法return 'Woof';}}//ES6为了方法引入了一个新的快捷语法。const o1 = {name: 'Wallace', //属性bark() { //方法return 'Woof';}}
6.10方法中的this关键字
this关键字通常与面向对象编程一起出现。第9章会详解更多用法。
this 关键字通常关联那些作为对象属性的函数。当方法被调用时,this 关键字的值就是被调用的对象
如何绑定this是由方法如何被调用所决定的,而非函数定义所决定
//ES6为了方法引入了一个新的快捷语法。const o = {name: 'Wallace', //属性speak() { //方法return `My name is ${this.name}`;}}//当调用o.speak()的时候,this 关键字跟o进行了绑定//this就是o 谁调用就是谁console.log(o.speak());//My name is Wallace//this是由方法如何被调用所决定的,而非函数定义所决定const speak = o.speak;console.log(speak === o.speak);//trueconsole.log(speak());//My name is
函数嵌套的this细节
const o = {name: 'Julie',greetBackwards: function() {//name 反序列名字function getReverseName() {let nameBackwards = '';for (let i = this.name.length - 1; i >= 0; i--) {nameBackwards += this.name[i];}return nameBackwards;}return `反过来的名称是:${getReverseName()} `;}}console.log(o.greetBackwards());//this会被绑定一个到全局对象或者undefined//一个常用的解决方案给this赋另一个变量: const self =this;
6.11箭头符号
ES6引入了一个新的语法叫作箭头符号。本质是一种语法糖
箭头函数允许使用以下3种方式来简化语法:
可以省略function这个单词。
如果函数只有一个参数,则可以省略小括号。
如果函数体是一个单独表达式,则可以省略大括号和返回语句。
const f = function() {console.log("匿名函数");}//g名字优先级高const g = function ff() {}//箭头函数大多是匿名函数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是,指向调用的那个对象,谁调用就是谁
箭头函数不会创建自己的this,箭头函数的this,始终指向父级上下文。
const o = {name: 'Julie',greetBackwards: function() {//name 反序列名字const getReverseName=()=> {let nameBackwards = '';for (let i = this.name.length - 1; i >= 0; i--) {nameBackwards += this.name[i];}return nameBackwards;}return `反过来的名称是:${getReverseName()} `;}}console.log(o.greetBackwards());
6.12调用call,请求apply和绑定bind
指定this的绑定对象
call方法在所有函数上都可用,它允许使用指定的this来调用函数。
const bruce = {name: "Bruce"};const madeline = {name: "Madeline"};function greet() {return `Hello,I am ${this.name}`;}console.log(greet());; // this没有绑定任何值console.log(greet.call(bruce)); //this 绑定了 bruceconsole.log(greet.call(madeline)); //this 绑定了 madeline
可以看到call方法允许在调用函数时给this绑定一个对象,就好像有一个对象在做这件事。
call方法的第一个参数是想要的绑定的值,剩下的参数则变成了要调用的函数的参数
const bruce = {name: "Bruce"};const madeline = {name: "Madeline"};function update(birthYear, occupation) {this.birthYear = birthYear;this.occupation = occupation;}update.call(bruce, 1949, 'singer');//bruce:{name:Bruce,birthYear:1949,occupation:singer}console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);update.call(madeline, 1942, 'actress');//madeline:{name:Madeline,birthYear:1942,occupation:actress}console.log(`madeline:{name:${madeline.name},birthYear:${madeline.birthYear},occupation:${madeline.occupation}}`);
除了处理函数参数的方式不同,apply 与call基本是一致的。call 会像一般的函数一样直接获取参数。apply则以数组的方式获取参数
apply 比较适合用于将数组作为函数参数的场景。
update.apply(bruce, [1949, 'singer']);//bruce:{name:Bruce,birthYear:1949,occupation:singer}console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);update.apply(madeline, [1942, 'actress']);//madeline:{name:Madeline,birthYear:1942,occupation:actress}console.log(`madeline:{name:${madeline.name},birthYear:${madeline.birthYear},occupation:${madeline.occupation}}`);
apply可以改变传递给函数参数的形式
如果参数本来就存在一个数组中,那就用 apply,如果参数比较散乱相互之间没什么关联,就用 call
//apply可以改变传递给函数参数的形式const arr = [2, 3, -5, 15, 7];console.log(Math.min(2, 3, 5, -5, 15, 7));console.log(Math.min.apply(null, arr));// ES6中的展开运算符((...),可以实现与apply同样的功能。const newBruce = [1940, "martial artist"];update.call(bruce, ...newBruce);console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);console.log(Math.min(...arr));
bind方法可以给一个函数永久地邦定this值。
bind方法返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行。
const updateBruce = update.bind(bruce);updateBruce(1904, "actor");console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);updateBruce.call(madeline, 1274, "king");console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);
调用bind方法时也可以传参数,这跟创建一个有固定参数的新函数效果一样。
比如,希望update函数总是把bruce 的出生年份设为1949,但却允许修改他的职业,可以这样做
const updateBruce1949 = update.bind(bruce, 1949);updateBruce1949("singer,songwriter");console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);
思考为啥要改变this绑定值?
const obj1 = {name: "rxy",speak() {console.log("姓名:" + this.name);}}obj1.speak();const obj2 = {name: "tom",}obj1.speak.call(obj2);
function Vehicle() {this.type = "汽车";}function Car(make, model) {//arguments,它是js中函数内置的一个对象,而执行函数方法的实参中值都存储在arguments中//通过下标/索引来获取每个参数的值Vehicle.apply(this, arguments);this.make = make;this.model = model;}var car = new Car("Tesla","ModelS");alert(car.type); // 汽车alert(car.make); // Teslaalert(car.model); // ModelS
