JS函数调用(4种方法)

1,137 阅读4分钟

函数有下列调用模式

  • 函数调用模式
  • 方法调用模式
  • 构造器模式 new
  • 上下文模式

一般形式的函数调用(函数调用模式)

在默认状态下,函数是不会被执行的。使用小括号()可以激活并执行函数。在小括号中可以包含零个或多个参数,参数之间通过逗号进行分隔。

function add(x, y){
    return x+y
}

var result = add(1,2);
console.log(result); // 3

作为对象的方法调用 (方法调用模式)

**当一个函数被设置为对象的属性值时**,称之为方法。使用**点语法**可以调用一个方法。

下面示例创建一个 obj 对象,它有一个 value 属性和一个 increment 属性。increment 方法接收一个可选参数,如果该参数不是数字,那么默认使用数字 1。

var obj = {    value: 0,    increment: function (inc) {        this.value += typeof inc === 'number' ? inc : 1;    }}obj.increment();console.log(obj.value); //1 
obj.increment(2);console.log(obj.value);  //2

使用 call 和 apply 动态调用 (上下文模式

**call 和 apply 是 Function 的原型方法**,它们能够将**特定函数当做一个方法绑定到指定对象上,并进行调用**。具体用法如下:
fun.call(thisobj, args...)
fun.apply(thisobj, [args])
  • fun是要调用的函数, 
  • thisobj 表示绑定对象,即 this 指代的对象;
  • args 表示要传递给被调用函数的参数。

call 方法可以接收多个参数列表,而 apply 只能接收一个数组或者伪类数组,数组元素将作为参数列表传递给被调用的函数。

例1:

function add(x, y){
    return x+y
}

console.log(f.call (null, 3, 4));  //返回7 代表绑定在windows上

例2:

var name = "小明";
var obj = {
    name: "小红",    getName: function(){
        return function(){ 
            return this.name;
        }
    }
}

let getName = obj.getName();
console.log(getName()); // 小明
console.log(getName.call(obj)) //小红

new 命令间接调用 (构造器模式

使用 new 命令可以实例化对象,这是它的主要功能,但是在创建对象的过程中会激活并运行函数。因此,使用 new 命令可以间接调用函数。

例:

function fun(x,y) {  //定义函数
    console.log("x =" + x + ", y =" + y);
}
let result = new fun(3,4); // x =3, y =4 
console.log(result) //会返回一个对象 fun{}

使用 new 命令调用函数时,返回的是对象,而不是 return 的返回值。如果不需要返回值,或者 return 的返回值是对象,则可以选用 new 间接调用函数。

例:

function fun(x,y) {  //定义函数
    console.log("x =" + x + ", y =" + y); 
    return {x,y}
}
let result = new fun(3,4);
console.log(result); //{x:3, y:4}

例2:

function fun(x,y) {  //定义函数
    console.log("x =" + x + ", y =" + y); 
    return x+y
}
let result = new fun(3,4);
console.log(result); //fun{}

面试题:

function Foo() {    getName = function () {        alert(1);    };    return this;}Foo.getName = function () {    alert(2);};Foo.prototype.getName = function () {    alert(3);};var getName = function () {    alert(4);};function getName() {    alert(5);}Foo.getName(); // alert 2  getName();  // alert 4   Foo().getName(); // alert 1    getName(); // alert 1    new Foo.getName(); // alert 2   new Foo().getName(); // alert 3    new new Foo().getName(); // alert 3

解析:

 function Foo() {     getName = function () {         alert(1);     };     return this; } Foo.getName = function () {     alert(2); }; Foo.prototype.getName = function () {     alert(3); }; var getName = function () {     alert(4); };  // 调用 Foo函数 作为 对象 动态添加的属性方法 getName    // Foo.getName = function () { alert(2); };      Foo.getName(); // ------- 输出 2 -------    // 这里 Foo函数 还没有执行,getName还没有被覆盖    // 所以 这里还是 最上面的 getName = function () { alert(4); };  getName();      // ------- 输出 4 -------        // Foo()执行,先覆盖全局的 getName 再返回 this,   // thiswindow, Foo().getName() 就是调用 window.getName    // 此时 全局的 getName已被覆盖成 function () { alert(1); };    // 所以 输出 1    /* 从这里开始 window.getName 已被覆盖 alert 1 */ Foo().getName();    // ------- 输出 1 -------     // window.getName alert(1);getName();  // -------- 输出 1 --------   // new 就是 找 构造函数(),由构造函数结合性,这里即使 Foo无参,也不能省略 (),所以不是 Foo().getName()    // 所以 Foo.getName 为一个整体,等价于 new (Foo.getName)();    // 而 Foo.getName 其实就是函数 function () { alert(2); } 的引用    // 那 new ( Foo.getName )(), 就是在以 Foo.getName 为构造函数 实例化对象。    // 就 类似于 new Person(); Person 是一个构造函数     // 总结来看 new ( Foo.getName )(); 就是在以 function () { alert(2); } 为构造函数来构造对象    // 构造过程中 alert( 2 ),输出 2new Foo.getName();     // ------- 输出 2 -------    // new 就是 找 构造函数(),等价于 ( new Foo() ).getName();    // 执行 new Foo() => 以 Foo 为构造函数,实例化一个对象    // ( new Foo() ).getName; 访问这个实例化对象的 getName 属性    // 实例对象自己并没有 getName 属性,构造的时候也没有 添加,找不到,就到原型中找    // 发现 Foo.prototype.getName = function () { alert(3); };    // 原型中有,找到了,所以 ( new Foo() ).getName(); 执行,alert(3)    new Foo().getName();    // ------- 输出 3 -------    // new 就是 找 构造函数(),等价于 new ( ( new Foo() ).getName )() 输出 3     // 先看里面的 ( new Foo() ).getName    // new Foo()Foo为构造函数,实例化对象    // new Foo().getName 找 实例对象的 getName属性,自己没有,去原型中找,    // 发现 Foo.prototype.getName = function () { alert(3); }; 找到了     // 所以里层 ( new Foo() ).getName 就是 以Foo为构造函数实例出的对象的 一个原型属性    // 属性值为一个函数 function () { alert(3); } 的引用     // 所以外层 new ( (new Foo()).getName )()在以该函数 function () { alert(3); } 为构造函数,构造实例    // 构造过程中 执行了 alert(3), 输出 3var p = new new Foo().getName();     // ------- 输出 3 -------