函数的四种调用方式

Posted chargeworld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数的四种调用方式相关的知识,希望对你有一定的参考价值。

 函数的四种调用方式

函数有下列调用模式

  1. 函数调用模式
  2. 方法调用模式
  3. 构造器模式
  4. 上下文模式

1. 函数调用 模式

要调用,就肯定要先定义,函数的定义方式:

  1. 声明式: function fuc() {}
  2. 表达式式: var func = function() {};
  3. Function: new Function( ‘参数’,…,’函数体’ );

单独独立调用的,就是函数调用模式,即 函数名( 参数 ),不能加任何其他的东西, 对象 o.fuc() 就不是了。

在函数调用模式中, this 表示全局对象 window

任何自调用函数都是函数模式。


2. 方法调用 模式 method

所谓方法调用,就是用对象的方法调用。方法是什么,方法本身就是函数,但是,方法不是单独独立的,而是要通过一个对象引导来调用。

就是说方法对象一定要有宿主对象。

即 对象.方法(参数)

this表示引导方法的对象,就是指宿主对象

对比-函数调用模式:

  1. 方法调用模式是不是独立的,需要宿主,而函数调用模式是独立的
  2. 方法调用模式方式:obj.fuc(); 函数调用模式方式: fuc();
  3. 方法调用模式中,this指宿主。而函数调用模式中 this 指 全局对象window

美团的一道面试题

  1.  
    var length = 10;
  2.  
    function fn() {
  3.  
    console.log( this.length ); // 10
  4.  
    }
  5.  
    var obj = {
  6.  
    length: 5,
  7.  
    method: function ( fn ) {
  8.  
    fn(); // 10 前面没有引导对象,是函数调用模式
  9.  
    arguments[ 0 ](); // 2
  10.  
    // arguments是一个伪数组对象, 这里调用相当于通过数组的索引来调用.
  11.  
    // 这里 this 就是 指的这个伪数组, 所以 this.length 为 2
  12.  
    }
  13.  
    };
  14.  
    obj.method( fn, 1 ); // 打印 10 和 2
  15.  
    //obj.method( fn, 1, 2, 3 ); // 打印 10 和 4
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

解析:

  1. fn() 前面没有引导对象,是函数调用模式, this是全局对象,输出 10

  2. arguments[ 0 ](),arguments是一个伪数组对象, 这里调用相当于通过数组的索引来调用.

    这里引导对象即宿主就是 arguments对象。

    所以,执行时,this 就是指 arguments,由于传了两个参数,所以 输出为 arguments.length 就是 2


3. 构造器模式(构造函数模式, 构造方法模式)

constructor

  1. 特点: 使用 new 关键字引导

  2. 执行步骤:var p = new Person();

    new 是一个运算符, 专门用来申请创建对象, 创建出来的对象传递给构造函数的 this。然后利用构造函数对其初始化。

    1.  
      function Person () {
    2.  
      // new了 进入构造函数时, p 对象的原型 就指向了 构造函数 Person,
    3.  
      // p.__proto__.constructor = function Person() {};
    4.  
      // 而 this 指的的是 p 对象
    5.  
      this.name = ‘jim‘,
    6.  
      this.age = 19;
    7.  
      this.gender = ‘male‘;
    8.  
      }
    9.  
      var p = new Person();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行完 new 进入构造函数时, p 对象的原型 就指向了 构造函数 Person

    而 构造时,this 指的的是 p 对象,是通过对象动态添加属性来构造的

    小贴士:如果调用构造函数的时候, 构造函数没有参数, 圆括号是可以省略的。

    1.  
      function Person() {
    2.  
      this.name = ‘jim‘;
    3.  
      }
    4.  
      var p = new Person; // 不传参,可以简写,不影响构造
    5.  
      console.log( p ); // p 含有 name属性
    • 1
    • 2
    • 3
    • 4
    • 5

    ↑ 不传参,可以简写,不影响构造

  3. 返回值

    1. 不写 return 语句, 那么 构造函数 默认返回 this

    2. 在构造函数 return 基本类型( return num, return 1223 ). 则忽略返回类型.

    3. 在构造函数 return 引用类型, 那么构造函数返回该引用类型数据, 而忽略 this

    1.  
      function Person () {
    2.  
      this.name = ‘Jepson‘;
    3.  
      return 123;
    4.  
      }
    5.  
      var p1 = new Person();
    6.  
      console.log( p1 );
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ↑ 忽略了 123,返回 this 对象, 指向构建的实例

    1.  
      function Person () {
    2.  
      this.name = ‘Jepson‘;
    3.  
      return { ‘peter‘: ‘nihao‘ };
    4.  
      }
    5.  
      var p1 = new Person();
    6.  
      console.log( p1 );
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ↑ 忽略了 this,返回 { ‘peter’: ‘nihao’ } 对象

构造函数结合性

  1. 如果构造函数没有参数, 可以省略 圆括号

    var p = new Person;

  2. 如果希望创建对象并直接调用其方法

    ( new Person () ).sayHello()

    -> 可以省略调整结核性的圆括号 new Person().sayHello()

    -> 如果想要省略构造函数的圆括号, 就必须添加结核性的圆括号 (new Person).sayHello()

面试题

一道面试题,大家可以自己尝试先做一下,再看下面的答案和解析

请问顺序执行下面代码,会怎样 alert

  1.  
    function Foo(){
  2.  
    getName = function(){ alert(1); };
  3.  
    return this;
  4.  
    }
  5.  
    Foo.getName = function(){ alert(2); };
  6.  
    Foo.prototype.getName = function(){ alert(3); };
  7.  
    var getName = function(){ alert(4); };
  8.  
    function getName(){ alert(5); }
  9.  
     
  10.  
    Foo.getName(); // alert ??
  11.  
    getName(); // alert ??
  12.  
    Foo().getName(); // alert ??
  13.  
    getName(); // alert ??
  14.  
    new Foo.getName(); // alert ??
  15.  
    new Foo().getName(); // alert ??
  16.  
    new new Foo().getName(); // alert ??
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

预解析,简化后的代码,以及答案

  1.  
    /* function getName(){ alert(5); } 执行到下面被覆盖了,直接删除 */
  2.  
    function Foo() {
  3.  
    getName = function () { alert(1); };
  4.  
    return this;
  5.  
    }
  6.  
    Foo.getName = function () { alert(2); };
  7.  
    Foo.prototype.getName = function () { alert(3); };
  8.  
    var getName = function () { alert(4); };
  9.  
     
  10.  
    Foo.getName(); // ------- 输出 2 -------
  11.  
    getName(); // ------- 输出 4 -------
  12.  
    Foo().getName(); // ------- 输出 1 -------
  13.  
    getName(); // ------- 输出 1 -------
  14.  
    new Foo.getName(); // ------- 输出 2 -------
  15.  
    new Foo().getName(); // ------- 输出 3 -------
  16.  
    var p = new new Foo().getName(); // ------- 输出 3 -------
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

全部解析过程 ↓

  1.  
    function Foo() {
  2.  
    getName = function () { alert(1); };
  3.  
    return this;
  4.  
    }
  5.  
    Foo.getName = function () { alert(2); };
  6.  
    Foo.prototype.getName = function () { alert(3); };
  7.  
    var getName = function () { alert(4); };
  8.  
     
  9.  
    Foo.getName(); // ------- 输出 2 -------
  10.  
    // 调用 Foo函数 作为 对象 动态添加的属性方法 getName
  11.  
    // Foo.getName = function () { alert(2); };
  12.  
     
  13.  
    getName(); // ------- 输出 4 -------
  14.  
    // 这里 Foo函数 还没有执行,getName还没有被覆盖
  15.  
    // 所以 这里还是 最上面的 getName = function () { alert(4); };
  16.  
     
  17.  
    Foo().getName(); // ------- 输出 1 -------
  18.  
    // Foo()执行,先覆盖全局的 getName 再返回 this,
  19.  
    // this 是 window, Foo().getName() 就是调用 window.getName
  20.  
    // 此时 全局的 getName已被覆盖成 function () { alert(1); };
  21.  
    // 所以 输出 1
  22.  
    /* 从这里开始 window.getName 已被覆盖 alert 1 */
  23.  
     
  24.  
    getName(); // -------- 输出 1 --------
  25.  
    // window.getName alert(1);
  26.  
     
  27.  
    new Foo.getName(); // ------- 输出 2 -------
  28.  
    // new 就是 找 构造函数(),由构造函数结合性,这里即使 Foo无参,也不能省略 (),所以不是 Foo().getName()
  29.  
    // 所以 Foo.getName 为一个整体,等价于 new (Foo.getName)();
  30.  
    // 而 Foo.getName 其实就是函数 function () { alert(2); } 的引用
  31.  
    // 那 new ( Foo.getName )(), 就是在以 Foo.getName 为构造函数 实例化对象。
  32.  
    // 就 类似于 new Person(); Person 是一个构造函数
  33.  
     
  34.  
    // 总结来看 new ( Foo.getName )(); 就是在以 function () { alert(2); } 为构造函数来构造对象
  35.  
    // 构造过程中 alert( 2 ),输出 2
  36.  
     
  37.  
    new Foo().getName(); // ------- 输出 3 -------
  38.  
    // new 就是 找 构造函数(),等价于 ( new Foo() ).getName();
  39.  
    // 执行 new Foo() => 以 Foo 为构造函数,实例化一个对象
  40.  
    // ( new Foo() ).getName; 访问这个实例化对象的 getName 属性
  41.  
    // 实例对象自己并没有 getName 属性,构造的时候也没有 添加,找不到,就到原型中找
  42.  
    // 发现 Foo.prototype.getName = function () { alert(3); };
  43.  
    // 原型中有,找到了,所以 ( new Foo() ).getName(); 执行,alert(3)
  44.  
     
  45.  
    var p = new new Foo().getName(); // ------- 输出 3 -------
  46.  
    // new 就是 找 构造函数(),等价于 new ( ( new Foo() ).getName )() 输出 3
  47.  
     
  48.  
    // 先看里面的 ( new Foo() ).getName
  49.  
    // new Foo() 以Foo为构造函数,实例化对象
  50.  
    // new Foo().getName 找 实例对象的 getName属性,自己没有,去原型中找,
  51.  
    // 发现 Foo.prototype.getName = function () { alert(3); }; 找到了
  52.  
     
  53.  
    // 所以里层 ( new Foo() ).getName 就是 以Foo为构造函数实例出的对象的 一个原型属性
  54.  
    // 属性值为一个函数 function () { alert(3); } 的引用
  55.  
     
  56.  
    // 所以外层 new ( (new Foo()).getName )()在以该函数 function () { alert(3); } 为构造函数,构造实例
  57.  
    // 构造过程中 执行了 alert(3), 输出 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

4. 上下文调用模式

就是 环境调用模式 => 在不同环境下的不同调用模式

简单说就是统一一种格式, 可以实现 函数模式与方法模式

-> 语法(区分)

  1. call 形式, 函数名.call( … )
  2. apply 形式, 函数名.apply( … )

这两种形式功能完全一样, 唯一不同的是参数的形式. 先学习 apply, 再来看 call 形式

apply方法的调用形式

存在上下文调用的目的就是为了实现方法借用,且不会污染对象。

  • 如果需要让函数以函数的形式调用, 可以使用

    foo.apply( null ); // 上下文为 window

  • 如果希望他是方法调用模式, 注意需要提供一个宿主对象

    foo.apply( obj ); // 上下文 为 传的 obj 对象

  1.  
    function foo () {
  2.  
    console.log( this );
  3.  
    }
  4.  
    var o = { name: ‘jim‘ };
  5.  
     
  6.  
    // 如果需要让函数以函数的形式调用, 可以使用
  7.  
    foo.apply( null ); // this => window // 或 foo.apply()
  8.  
     
  9.  
    // 如果希望他是方法调用模式, 注意需要提供一个宿主对象
  10.  
    foo.apply( o ) // this => o 对象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

带有参数的函数如何实现上下文调用?

  1.  
    function foo ( num1, num2 ) {
  2.  
    console.log( this );
  3.  
    return num1 + num2;
  4.  
    }
  5.  
     
  6.  
    // 函数调用模式
  7.  
    var res1 = foo( 123, 567 );
  8.  
     
  9.  
    // 方法调用
  10.  
    var o = { name: ‘jim‘ };
  11.  
    o.func = foo;
  12.  
    var res2 = o.func( 123, 567 );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

使用 apply 进行调用, 如果函数是带有参数的. apply 的第一个参数要么是 null 要么是对象

  1. 如果是 null 就是函数调用

  2. 如果是 对象就是 方法调用, 该对象就是宿主对象, 后面紧跟一个数组参数, 将函数所有的参数依次放在数组中.

  1.  
    例如: 函数模式 foo( 123, 567 );
  2.  
    apply foo.apply( null, [ 123, 567 ] ) 以 window 为上下文执行 apply
  3.  
     
  4.  
    如果有一个函数调用: func( ‘张三‘, 19, ‘男‘ ),
  5.  
    将其修改成 apply 模式: func.apply( null, [ ‘张三‘, 19, ‘男‘] )
  6.  
     
  7.  
    方法模式: o.func( 123, 567 )
  8.  
    apply var o = { name: ‘jim‘ };
  9.  
    foo.apply( o, [ 123, 567 ] ); 以 o 为上下文执行 apply
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

方法借用的案例

需求, 获得 div 与 p 标签, 并添加边框 border: 1px solid red

一般做法:

  1.  
    var p_list = document.getElementsByTagName(‘p‘);
  2.  
    var div_list = document.getElementsByTagName(‘div‘);
  3.  
     
  4.  
    var i = 0;
  5.  
    for( ; i < p_list.length; i++ ) {
  6.  
    p_list[ i ].style.border = "1px solid red";
  7.  
    }
  8.  
    for( i = 0; i < div_list.length; i++ ) {
  9.  
    div_list[ i ].style.border = "1px solid red";
  10.  
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  1. 利用方法借用优化,元素获取
  1.  
    var t = document.getElementsByTagName;
  2.  
    var p_list = t.apply( document, [ ‘p‘ ] ); // 方法借用
  3.  
    var div_list = t.apply( document, [ ‘div‘ ] ); // 方法借用
  • 1
  • 2
  • 3
  1. 接下来考虑下面的优化,两个for循环,只要将数组合并了,就可以只用一个 for 循环

    数组合并

    1.  
      var arr1 = [ 1, 2, 3 ];
    2.  
      var arr2 = [ 5, 6 ];
    3.  
      arr1.push.apply( arr1, arr2 ); // 方法调用,第一个给上对象
    4.  
      // 等价于 Array.prototype.push.apply( arr1, arr2 );
    • 1
    • 2
    • 3
    • 4

    所以同理,利用 apply 方法借用,将两个伪数组合并成同一个数组

    1.  
      var arr = []; // 伪数组没有 push 方法,所以这里要 声明一个 数组
    2.  
      arr.push.apply( arr, p_list ); // 将 p_list里的内容,一个个当成参数放了进来,相当于不用遍历了
    3.  
      arr.push.apply( arr, div_list ); // 同上,方法借用
    4.  
      console.log( arr );
    • 1
    • 2
    • 3
    • 4

将两者综合, 使用forEach,最终 6 行就解决了

  1.  
    var t = document.getElementsByTagName, arr = [];
  2.  
    arr.push.apply( arr, t.apply( document, [ ‘p‘ ] ) );
  3.  
    arr.push.apply( arr, t.apply( document, [ ‘div‘] ) );
  4.  
    arr.forEach( function( val, index, arr ) {
  5.  
    val.style.border = ‘1px solid red‘;
  6.  
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

call 调用

在使用 apply 调用的时候, 函数参数, 必须以数组的形式存在. 但是有些时候数组封装比较复杂

所以引入 call 调用, call 调用与 apply 完全相同, 唯一不同是 参数不需要使用数组

  1.  
    foo( 123, 567 );
  2.  
     
  3.  
    foo.apply( null, [ 123, 567 ] );
  4.  
     
  5.  
    foo.call( null, 123, 567 );
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 函数调用: 函数名.call( null, 参数1,参数2,参数3… );

  2. 方法调用: 函数名.call( obj, 参数1,参数2, 参数3… );

不传参时,apply 和 call 完全一样

借用构造方法实现继承

  1.  
    function Person ( name, age, gender ) {
  2.  
    this.name = name;
  3.  
    this.age = age;
  4.  
    this.gender = gender;
  5.  
    }
  6.  
     
  7.  
    function Student ( name, age, gender, course ) {
  8.  
    // 原型链结构不会变化,同时实现了继承的效果
  9.  
    Person.call( this, name, age, gender );// 借用Person构造方法
  10.  
    this.course = course;
  11.  
    }
  12.  
     
  13.  
    var p = new Student ( ‘jim‘, 19, ‘male‘, ‘前端‘ );
  14.  
    console.log( p );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

补充知识 1. 函数的 bind 方法 ( ES5 )

bind 就是 绑定

还是上面那个案例 获得 div 与 p 标签, 并添加边框 border: 1px solid red

  1.  
    var t = document.getElementsByTagName, arr = [];
  2.  
    arr.push.apply( arr, t.call( document, ‘p‘ ) );
  3.  
    arr.push.apply( arr, t.call( document, ‘div‘ ) );
  4.  
    arr.forEach( function( val, index, arr ) {
  5.  
    val.style.border = ‘1px solid red‘;
  6.  
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们 让 t 包含函数体(上面的方式),同时包含 对象,就可以更精简

  1.  
    var t = document.getElementsByTagName.bind( document ), arr = [];
  2.  
    arr.push.apply( arr, t(‘p‘) );
  3.  
    arr.push.apply( arr, t(‘div‘) );
  4.  
    arr.forEach( function( val, index, arr ) {
  5.  
    val.style.border = ‘1px solid red‘;
  6.  
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

bind : 就是让函数绑定对象的一种用法

函数本身就是可以调用, 但是其如果想要作为方法调用, 就必须传入宿主对象, 并且使用 call 或 apply 形式

但是 bind 使得我的函数可以与某一个对象绑定起来, 那么在调用函数的时候, 就好像是该对象在调用方法,就可以直接传参,而不需要传宿主对象。

语法: 函数.bind( 对象 )

返回一个函数 foo,那么调用 返回的函数 foo, 就好像 绑定的对象在调用 该方法一样

  1.  
    t.call( document, ‘p‘ );
  2.  
     
  3.  
    t( ‘p‘ ); 绑定后,就不用传宿主对象了,这里调用时上下文已经变成了 document
  4.  
     
  5.  
    bind 是 ES 5 给出的函数调用方法
  • 1
  • 2
  • 3
  • 4
  • 5

补充知识 2. Object.prototype 的成员

Object.prototype 的成员

  1. constructor
  2. hasOwnProperty 判断该属性是否为自己提供
  3. propertyIsEnumerable 判断属性是否可以枚举
  4. isPrototypeOf 判断是否为原型对象
  5. toString, toLocaleString, valueOf
  1.  
    function Person() {
  2.  
    this.name = ‘jim‘;
  3.  
    }
  4.  
    Person.prototype.age = 19;
  5.  
    var p = new Person();
  6.  
    console.log( p.hasOwnProperty( ‘name‘ ) ); // true; p 是否含有 name 属性,原型上不管
  7.  
    console.log( p.hasOwnProperty( ‘age‘ ) ); // false; p 是否含有 age 属性
  8.  
     
  9.  
    /* Person.prototype 是 p 的原型 */
  10.  
    console.log( p.isPrototypeOf( Person.prototype ) ); // false
  11.  
    console.log( Person.prototype.isPrototypeOf( p ) ); // true;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

用途:一般把一个对象拷贝到另一个对象时,可以进行判断,更加严谨,以防把原型中的属性也拷贝过去了…


补充知识 3. 包装类型

字符串 string 是基本类型, 理论上讲不应该包含方法

那么 charAt, substr, slice, …等等方法,理论上都不应该有,但确是有的

所以引入包装对象的概念,在 js 中为了更好的使用数据, 为三个基本类型提供了对应的对象类型

  1. Number
  2. String
  3. Boolean

在 开发中常常会使用基本数据类型, 但是基本数据类型没有方法, 因此 js 引擎会在需要的时候自动的将基本类型转换成对象类型, 就是包装对象

  • “abc”.charAt( 1 )

  • “abc” -> s = new String( “abc” )

  • s.charAt( 1 ) 返回结果以后 s 就被销毁

当基本类型.方法 的时候. 解释器首先将基本类型转换成对应的对象类型, 然后调用方法.

方法执行结束后, 这个对象就被立刻回收

在 apply 和 call 调用的时候, 也会有转换发生. 上下文调用的第一个参数必须是对象. 如果传递的是数字就会自动转换成对应的包装类型


补充知识 4. getter 和 setter 的语法糖 ( ES5 )

语法糖: 为了方便开发而给出的语法结构

本身实现:

  1.  
    var o = (function () {
  2.  
    var num = 123;
  3.  
    return {
  4.  
    get_num: function () {
  5.  
    return num;
  6.  
    },
  7.  
    set_num: function ( v ) {
  8.  
    num = v;
  9.  
    }
  10.  
    };
  11.  
    })();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1.  
    希望获得数据 以对象的形式
  2.  
    o.get_num(); => o.num 形式
  3.  
     
  4.  
    希望设置数据 以对象的形式
  5.  
    o.set_num( 456 ); => o.num = 456 形式
  • 1
  • 2
  • 3
  • 4
  • 5

所以 getter 和 setter 诞生 了

  1.  
    var o = (function () {
  2.  
    var num = 123;
  3.  
    return {
  4.  
    // get 名字 () { 逻辑体 }
  5.  
    get num () {
  6.  
    console.log( ‘执行 getter 读写器了‘ );
  7.  
    return num;
  8.  
    },
  9.  
     
  10.  
    // set 名字 ( v ) { 逻辑体 }
  11.  
    set num ( v ) {
  12.  
    console.log( ‘执行 setter 读写器了‘ );
  13.  
    num = v;
  14.  
    }
  15.  
    };
  16.  
    })();
  17.  
     
  18.  
    console.log( o.num ); // 执行 getter 读写器了 123
  19.  
    o.num = 33; // 执行 setter 读写器了
  20.  
    console.log( o.num ); // 执行 getter 读写器了 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

为什么不直接用 对象呢 var o = { num : 123 } ,也可以读写呀?

因为语法糖还可以 限制其赋值的范围,使用起来特别爽

  1.  
    var o = (function () {
  2.  
    var num = 13;
  3.  
    return {
  4.  
     
  5.  
    // get 名字 () { 逻辑体 }
  6.  
    get num () {
  7.  
    console.log( ‘执行 getter 读写器了‘ );
  8.  
    return num;
  9.  
    },
  10.  
     
  11.  
    // set 名字 ( v ) { 逻辑体 }
  12.  
    set num ( v ) {
  13.  
    console.log( ‘执行 setter 读写器了‘ );
  14.  
     
  15.  
    if ( v < 0 || v > 150 ) {
  16.  
    console.log( ‘赋值超出范围, 不成功 ‘ );
  17.  
    return;
  18.  
    }
  19.  
    num = v;
  20.  
    }
  21.  
    };
  22.  
    })();
  23.  
    o.num = -1; // 执行 setter 读写器了
  24.  
    // 读写器赋值超出范围, 不成功
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

补充知识 5. ES5 中引入的部分数组方法

  1. forEach
  2. map
  3. filter
  4. some
  5. every
  6. indexOf
  7. lastIndexOf

  1. forEach, 数组遍历调用,遍历arr,参数三个 1某项, 2索引, 3整个数组

    1.  
      var arr = [ ‘hello‘, ‘ js‘, { }, function () {} ];
    2.  
      // 遍历 数组
    3.  
      arr.forEach( function ( v, i, ary ) {
    4.  
      console.log( i + ‘=====‘ + v );
    5.  
      console.log( ary );
    6.  
      });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  2. map 映射

    语法: 数组.map( fn )

    返回一个数组, 数组的每一个元素就是 map 函数中的 fn 的返回值

    就是对每一项都进行操作,并返回

    1.  
      var arr = [ 1, 2, 3, 4 ];
    2.  
      // 数学中: x -> x * x
    3.  
      var a = arr.map(function ( v, i ) {
    4.  
      return v * v;
    5.  
      });
    6.  
      // a [1, 4, 9, 16]
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  3. filter 就是筛选, 函数执行结果是 false 就弃掉, true 就收着

    语法: 数组.filter( function ( v, i ) { return true/false })

    1.  
      var arr = [ 1, 2, 3, 4, 5, 6 ];
    2.  
      // 筛选奇数
    3.  
      var a = arr.filter( function ( v ) { return v % 2 === 1; });
    4.  
      // a [ 1, 3, 5 ]
    • 1
    • 2
    • 3
    • 4
  4. some 判断数组中至少有一个数据复合要求 就返回 true, 否则返回 false

    1.  
      var arr = [ ‘123‘, {}, function () {}, 123 ];
    2.  
      // 判断数组中至少有一个数字
    3.  
      var isTrue = arr.some( function ( v ) { return typeof v === ‘number‘; } ); // true;
    • 1
    • 2
    • 3
  5. every 必须满足所有元素都复合要求才会返回 true

    1.  
      var arr = [ 1, 2, 3, 4, 5, ‘6‘ ];
    2.  
      // 判断数组中每一个都是数字
    3.  
      var isTrue = arr.every( function ( v ) { return typeof v === ‘number‘; } ); } ); // false;
    • 1
    • 2
    • 3
  6. indexOf 在数组中查找元素, 如果含有该元素, 返回元素的需要( 索引 ), 否则返回 -1

    1.  
      var arr = [ 1, 2, 3, 4, 5 ];
    2.  
      var res = arr.indexOf( 4 ); // 要找 4
    3.  
      console.log( res ); // 3 找 4 在 索引为 3 找到
    4.  
       
    5.  
      var arr = [ 1, 2, 3, 4, 5, 4, 5, 6 ];
    6.  
      var res = arr.indexOf( 4, 4 ); // 要找 4, 从索引 4开始找
    7.  
      console.log( res ); // 找到了 索引为 5
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  7. lastIndexOf 从右往左找

    1.  
      var arr = [ 1, 2, 3, 4, 5, 4, 5, 6 ];
    2.  
      var res = arr.lastIndexOf( 4 );
    3.  
      console.log( res ); // 索引为 5, 从最后一项开始找,即从 length-1 项开始找
    4.  

以上是关于函数的四种调用方式的主要内容,如果未能解决你的问题,请参考以下文章

函数的四种调用方式

python函数调用的四种方式 --基础重点

js种函数调用的四种方式

js函数的四种调用方式以及对应的this指向

JS函数调用的四种方法

JS高级--原型链闭包作用域函数的四种调用方式