函数有下列调用模式
- 函数调用模式
- 方法调用模式
- 构造器模式 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, // this 是 window, 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 -------