6.1函数简介

函数是一组语句的集合,它是一个独立运行的程序单元,本质上,它是一段子程序。函数是JavaScript的核心。
每个函数都有一个函数体,它是构成该函数的一组语句集合。

  1. function sayHello(){
  2. //这是函数体
  3. console.log("Hello World11111");
  4. console.log("Hello World22222");
  5. console.log("Hello World33333");
  6. }
  7. //调用
  8. sayHello();

6.2函数的传参

给函数调用传值的主要途径称为函数参数,函数参数可以给函数内部传值,参数是在函数调用后就不存在的变量。

  1. //a和b称为形参
  2. function avg(a, b) {
  3. return (a + b) / 2;
  4. }
  5. //实际参数
  6. avg(5,10);

6.3返回值

返回值,在函数体中,return 关键字会立即结束函数,并且返回一个特定值。

  1. function getGreeting(){
  2. return "Hello World";
  3. }
  4. let str = getGreeting();
  5. console.log(str);

6.4参数列表

在其他编程语言中,一个函数的签名会包含它的参数列表。
比如,在C语言中,函数f()(没有参数)和函数f(x)(包含一个参数)是两个不同的函数。
但是JavaScript则对此不作区分,当有一个名为f的函数时,调用时可以给它0个、1个,甚至是10个参数,
不管调用时有多少参数,都始终调用了同一个函数。

  1. function f(x){
  2. return `in f:x=${x}`;
  3. }
  4. console.log(f());
  5. console.log(f(1));

6.5引用调用

在JavaScript中,函数是一个对象,像其他对象一样,可以被传递和赋值。所以理解函数调用和引用之间的差别很重要。当在函数名后面添加小括号是,JavaScript就知道要调用它,然后执行函数体,并返回结果。如果没有小括号,仅仅是在引用这个函数,并没有调用它。

  1. function getGreeting() {
  2. return "Hello World";
  3. }
  4. getGreeting(); //调用函数
  5. getGreeting //引用函数
  6. const o = {};
  7. o.f = getGreeting;
  8. o.f();
  9. console.log(o.f());
  10. //把函数添加到数组里
  11. const arr = [11, 12, 13];
  12. arr[1] = getGreeting;
  13. arr[1]();
  14. console.log(arr[1]());

6.6解构参数

在解构赋值中,参数属性名必须是字符串标识符,而且如果传入的对象不存在与参数属性名匹配的属性,该属性将会接受一个undefined值。

  1. //把一个对象解构到不通的变量中
  2. function getSentence1({
  3. subject,
  4. verb,
  5. object
  6. }) {
  7. return `${subject} ${verb} ${object}`;
  8. }
  9. const o = {
  10. subject: "I",
  11. verb: "love",
  12. object: "JavaScript"
  13. };
  14. console.log(getSentence1(o));
  15. //解构一个数组
  16. const arr = ["I", "love", "JavaScript"];
  17. function getSentence2([
  18. subject,
  19. verb,
  20. object
  21. ]) {
  22. return `${subject} ${verb} ${object}`;
  23. }
  24. console.log(getSentence2(arr));

6.6展开操作符(…)

注意,如果在函数定义中使用展开操作符,它必须是最后一个参数。

  1. //展开操作符...匹配任何多出来的参数
  2. function addPrefix(prefix, ...words) {
  3. const prefixedWords = [];
  4. for (let i = 0; i < words.length; i++) {
  5. prefixedWords[i] = prefix + words[i];
  6. }
  7. return prefixedWords;
  8. }
  9. console.log(addPrefix("con", "verse", "vex"));
  10. //如果在函数定义中使用展开操作符,它必须是最后一个参数。

6.7默认参数

ES6中的一个新特性是可以指定参数的默认值。通常,如果没有给参数传值,它的值将会是undefined。默认值则可以给这些参数指定其他的默认值。

  1. function f(a, b = "default", c = 3) {
  2. return `${a}-${b}-${c}`;
  3. }
  4. console.log(f(5,6,7)); //5-6-7
  5. console.log(f(5,6)); //5-6-3
  6. console.log(f(5)); //5-default-3
  7. console.log(f()); //undefined-default-3

6.8值传递和引用传递

  1. //基本类型的值传递
  2. function f(x){
  3. console.log("方法里的值:"+x);
  4. x = 5;
  5. console.log("方法里修改后值:"+x);
  6. }
  7. let x = 3;
  8. console.log("原始值:"+3);
  9. f(x);
  10. //对象类型的,引用传递
  11. function f(o) {
  12. o.message = "f方法改变了值";
  13. }
  14. let o = {
  15. message: "原始值"
  16. }
  17. console.log("o.message" + o.message);
  18. f(o);

6.9函数和方法

当函数作为一个对象的属性时,通常被称为方法

  1. const o = {
  2. name: 'Wallace', //属性
  3. bark: function() { //方法
  4. return 'Woof';
  5. }
  6. }
  7. //ES6为了方法引入了一个新的快捷语法。
  8. const o1 = {
  9. name: 'Wallace', //属性
  10. bark() { //方法
  11. return 'Woof';
  12. }
  13. }

6.10方法中的this关键字

this关键字通常与面向对象编程一起出现。第9章会详解更多用法。
this 关键字通常关联那些作为对象属性的函数。当方法被调用时,this 关键字的值就是被调用的对象
如何绑定this是由方法如何被调用所决定的,而非函数定义所决定

  1. //ES6为了方法引入了一个新的快捷语法。
  2. const o = {
  3. name: 'Wallace', //属性
  4. speak() { //方法
  5. return `My name is ${this.name}`;
  6. }
  7. }
  8. //当调用o.speak()的时候,this 关键字跟o进行了绑定
  9. //this就是o 谁调用就是谁
  10. console.log(o.speak());//My name is Wallace
  11. //this是由方法如何被调用所决定的,而非函数定义所决定
  12. const speak = o.speak;
  13. console.log(speak === o.speak);//true
  14. console.log(speak());//My name is

函数嵌套的this细节

  1. const o = {
  2. name: 'Julie',
  3. greetBackwards: function() {
  4. //name 反序列名字
  5. function getReverseName() {
  6. let nameBackwards = '';
  7. for (let i = this.name.length - 1; i >= 0; i--) {
  8. nameBackwards += this.name[i];
  9. }
  10. return nameBackwards;
  11. }
  12. return `反过来的名称是:${getReverseName()} `;
  13. }
  14. }
  15. console.log(o.greetBackwards());
  16. //this会被绑定一个到全局对象或者undefined
  17. //一个常用的解决方案给this赋另一个变量: const self =this;

6.11箭头符号

ES6引入了一个新的语法叫作箭头符号。本质是一种语法糖
箭头函数允许使用以下3种方式来简化语法:
可以省略function这个单词。
如果函数只有一个参数,则可以省略小括号。
如果函数体是一个单独表达式,则可以省略大括号和返回语句。

  1. const f = function() {
  2. console.log("匿名函数");
  3. }
  4. //g名字优先级高
  5. const g = function ff() {
  6. }
  7. //箭头函数大多是匿名函数
  8. const f1 = function() {
  9. return "Hello!";
  10. }
  11. //简写
  12. const f1 = () => "Hello";
  13. const f2 = function(name) {
  14. return `Hello,${name}`;
  15. }
  16. //简写
  17. const f2 = name => `Hello,${name}`;
  18. const f3 = function(a, b) {
  19. return a + b;
  20. }
  21. //简写
  22. const f3 = (a, b) => a + b;

箭头函数跟普通函数之间的主要区别是:this。
普通函数的this是,指向调用的那个对象,谁调用就是谁
箭头函数不会创建自己的this,箭头函数的this,始终指向父级上下文。

  1. const o = {
  2. name: 'Julie',
  3. greetBackwards: function() {
  4. //name 反序列名字
  5. const getReverseName=()=> {
  6. let nameBackwards = '';
  7. for (let i = this.name.length - 1; i >= 0; i--) {
  8. nameBackwards += this.name[i];
  9. }
  10. return nameBackwards;
  11. }
  12. return `反过来的名称是:${getReverseName()} `;
  13. }
  14. }
  15. console.log(o.greetBackwards());

6.12调用call,请求apply和绑定bind

指定this的绑定对象
call方法在所有函数上都可用,它允许使用指定的this来调用函数。

  1. const bruce = {
  2. name: "Bruce"
  3. };
  4. const madeline = {
  5. name: "Madeline"
  6. };
  7. function greet() {
  8. return `Hello,I am ${this.name}`;
  9. }
  10. console.log(greet());; // this没有绑定任何值
  11. console.log(greet.call(bruce)); //this 绑定了 bruce
  12. console.log(greet.call(madeline)); //this 绑定了 madeline

可以看到call方法允许在调用函数时给this绑定一个对象,就好像有一个对象在做这件事。
call方法的第一个参数是想要的绑定的值,剩下的参数则变成了要调用的函数的参数

  1. const bruce = {
  2. name: "Bruce"
  3. };
  4. const madeline = {
  5. name: "Madeline"
  6. };
  7. function update(birthYear, occupation) {
  8. this.birthYear = birthYear;
  9. this.occupation = occupation;
  10. }
  11. update.call(bruce, 1949, 'singer');
  12. //bruce:{name:Bruce,birthYear:1949,occupation:singer}
  13. console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);
  14. update.call(madeline, 1942, 'actress');
  15. //madeline:{name:Madeline,birthYear:1942,occupation:actress}
  16. console.log(`madeline:{name:${madeline.name},birthYear:${madeline.birthYear},occupation:${madeline.occupation}}`);

除了处理函数参数的方式不同,apply 与call基本是一致的。call 会像一般的函数一样直接获取参数。apply则以数组的方式获取参数
apply 比较适合用于将数组作为函数参数的场景。

  1. update.apply(bruce, [1949, 'singer']);
  2. //bruce:{name:Bruce,birthYear:1949,occupation:singer}
  3. console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);
  4. update.apply(madeline, [1942, 'actress']);
  5. //madeline:{name:Madeline,birthYear:1942,occupation:actress}
  6. console.log(`madeline:{name:${madeline.name},birthYear:${madeline.birthYear},occupation:${madeline.occupation}}`);

apply可以改变传递给函数参数的形式
如果参数本来就存在一个数组中,那就用 apply,如果参数比较散乱相互之间没什么关联,就用 call

  1. //apply可以改变传递给函数参数的形式
  2. const arr = [2, 3, -5, 15, 7];
  3. console.log(Math.min(2, 3, 5, -5, 15, 7));
  4. console.log(Math.min.apply(null, arr));
  5. // ES6中的展开运算符((...),可以实现与apply同样的功能。
  6. const newBruce = [1940, "martial artist"];
  7. update.call(bruce, ...newBruce);
  8. console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);
  9. console.log(Math.min(...arr));

bind方法可以给一个函数永久地邦定this值。
bind方法返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行。

  1. const updateBruce = update.bind(bruce);
  2. updateBruce(1904, "actor");
  3. console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);
  4. updateBruce.call(madeline, 1274, "king");
  5. console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);

调用bind方法时也可以传参数,这跟创建一个有固定参数的新函数效果一样。
比如,希望update函数总是把bruce 的出生年份设为1949,但却允许修改他的职业,可以这样做

  1. const updateBruce1949 = update.bind(bruce, 1949);
  2. updateBruce1949("singer,songwriter");
  3. console.log(`bruce:{name:${bruce.name},birthYear:${bruce.birthYear},occupation:${bruce.occupation}}`);

思考为啥要改变this绑定值?

  1. const obj1 = {
  2. name: "rxy",
  3. speak() {
  4. console.log("姓名:" + this.name);
  5. }
  6. }
  7. obj1.speak();
  8. const obj2 = {
  9. name: "tom",
  10. }
  11. obj1.speak.call(obj2);
  1. function Vehicle() {
  2. this.type = "汽车";
  3. }
  4. function Car(make, model) {
  5. //arguments,它是js中函数内置的一个对象,而执行函数方法的实参中值都存储在arguments中
  6. //通过下标/索引来获取每个参数的值
  7. Vehicle.apply(this, arguments);
  8. this.make = make;
  9. this.model = model;
  10. }
  11. var car = new Car("Tesla","ModelS");
  12. alert(car.type); // 汽车
  13. alert(car.make); // Tesla
  14. alert(car.model); // ModelS