主要内容
分析函数的四种调用方式 找出函数中this的含义 理清构造函数对象的流程 学习使用上下文来调用函数
了解函数的调用过程有助于深入学习和分析JavaScript代码。 本文是JavaScript中级系列的第三篇文章,主要介绍了JavaScript中函数的四种使用方式。 在 JavaScript 中,函数是一等公民,函数是 JavaScript 中的一种数据类型,而不是像 C# 或其他描述性语言那样仅仅用作模块。 函数的调用模式有四种javascript函数调用函数返回值,分别是:函数调用模式、方法调用模式、构造函数模式、应用模式。 这里的所有调用方式中,主要区别在于关键字this的含义。 下面分别介绍这几种调用方式。
1.函数调用方法
// 声明一个函数,并调用 function func() { alert("Hello World"); } func();
或者:
// 使用函数的Lambda表达式定义函数,然后调用 var func = function() { alert("你好,程序员"); }; func();
这两段代码会在浏览器中弹出一个对话框,显示字符串中的文本,这就是函数调用。
可以发现函数调用非常简单,和平时学习的一样。 这里关键是,在函数调用方式下,函数中的this关键字指的是全局对象,也就是浏览器中的window对象。 例如:
var func = function() { alert(this); }; func();
此时会弹出对话框并打印出【对象窗口】。
2.方法调用方式
函数调用方式非常简单,是最基本的调用方式。 但同样是一个函数,它的形参被赋予一个对象的成员后,就不一样了。 将函数参数传递给对象的成员后,那么this就不再称为函数,而是一个trick。 例如:
// 定义一个函数 var func = function() { alert("我是一个函数么?"); }; // 将其赋值给一个对象 var o = {}; o.fn = func; // 注意这里不要加圆括号 // 调用 o.fn();
此时,o.fn 是一个技巧,而不是一个函数。 其实fn的方法体和func的方法体是一模一样的,只是这里有一个细微的差别。 看下面的代码:
// 接上面的代码 alert(o.fn === func);
打印结果为true,说明两个函数是同一个东西,只是改变函数的代码:
// 修改函数体 var func = function() { alert(this); }; var o = {}; o.fn = func; // 比较 alert(o.fn === func); // 调用 func(); o.fn();
这里运算的结果是两个函数是一样的,所以复制结果为true。 但由于两个函数的调用不同,func的调用打印的是[object Window],而o.fn的复制结果是[object Object]。
这是函数调用和方法调用之间的区别。 在函数调用中,this指的是全局对象window,但在方法中javascript函数调用函数返回值,this指的是当前对象,即o.fn中的this指的是对象o。
3.构造函数调用方式
它也是一个函数,在纯函数模式下,这意味着窗口; 在对象模式下,this 指的是当前对象。 除了这两种情况,JavaScript 中的函数也可以是构造函数。 使用函数作为构造函数的语法是在函数调用中添加 new 关键字。 如代码:
// 定义一个构造函数 var Person = function() { this.name = "程序员"; this.sayHello = function() { alert("你好,这里是" + this.name); }; }; // 调用构造器,创建对象 var p = new Person(); // 使用对象 p.sayHello();
上面的例子首先创建了一个构造函数Person,然后使用构造函数创建了一个对象p。 这里使用新的语法。 然后使用该对象调用 sayHello() 方法。 这种使用构造函数创建对象的情况比较简单。 从案例中可以看出,此时this指的是对象本身。 除了前面的简单使用之外,函数作为构造函数还有几个变化,即:
1. 对象需要使用的所有属性都必须以此为指导;
2、函数的return语句的含义被重写。 如果返回非对象,则返回 this。
构造函数中的 this
我们需要分析对象的创建过程才能理解this的含义。 下面的边代码:
var Person = function() { this.name = "程序员"; }; var p = new Person();
这里首先定义了函数Person,下面分析整个执行过程:
1.当程序到达这句话时,函数体不会被执行,因此JavaScript类库不知道这个函数的内容。
2、接下来,执行new关键字来创建一个对象。 解释器分配视频内存,获取对象的引用,并将新对象的引用传递给函数。
3. 执行函数后,立即将传递的对象引用传递给 this。 也就是说,在构造模式下,this就是刚刚new创建的对象。
4、然后给this添加成员,也就是给对象添加成员。
5. 在函数的最后,返回this,并将this传递给左边的变量。
分析构造函数的执行过程可以得到,构造函数中的this就是当前对象。
在构造函数中返回
构造函数中 return 的含义已更改。 首先,如果在构造函数中,如果返回一个对象,那么保持原来的含义。 如果返回非对象,例如数字、布尔值和字符串,则返回 this。 如果没有return语句,也会返回这个。 请参阅下面的代码:
// 返回一个对象的 return var ctr = function() { this.name = "赵晓虎"; return { name:"牛亮亮" }; }; // 创建对象 var p = new ctr(); // 访问name属性 alert(p.name);
执行代码,这里复制的结果是“牛亮亮”。 因为构造方法中返回了一个对象,所以保留了return的含义,返回的内容就是return后的对象,再看下面的代码:
// 定义返回非对象数据的构造器 var ctr = function() { this.name = "赵晓虎"; return "牛亮亮"; }; // 创建对象 var p = new ctr(); // 使用 alert(p); alert(p.name);
代码运行的结果是,先弹出窗口复制[object Object],然后复制“赵小虎”,因为这里返回的是字符串,属于基本类型,那么这里的return语句无效,并且返回这个对象,所以第一个复制的是[object Object],第二个不复制undefined。
四、应用通话模式
除了以上三种调用方式之外,函数作为对象还有apply方法和call方法可以使用。 这是第四种调用模式,我称之为apply模式。
首先介绍一下申请模式。 首先,apply模式可以作为函数使用,也可以作为方法使用。 可以说是一种灵活的使用方式。 先看句型:函数名.apply(对象,参数列表);
这里的句型比较晦涩,还是用一个案例来说明:
1、新建两个js文件,分别是“js1.js”和“js2.js”;
2.添加代码
// js1.js 文件中 var func1 = function() { this.name = "程序员"; }; func1.apply(null); alert(name); // js2.js 文件 var func2 = function() { this.name = "程序员"; }; var o = {}; func2.apply(o); alert(o.name);
3. 分别运行两段代码,可以发现第一个文件中的name属性已经被加载到全局对象窗口中了; 而第二个文件中的name属性是在传入的对象o中,即第一个相当于函数调用,第二个相当于方法调用。
这里的参数是方法本身的参数,但是需要以链表的形式存储,比如代码:
// 一个数组的例子 var arr1 = [1,2,3,[4,5],[6,7,8]]; // 将其展开 var arr2 = arr1.conact.apply([], arr1);
然后介绍一下通话模式。 call模式和apply模式最大的区别在于call中的参数不使用链表。 从下面的代码可以清楚地看出:
// 定义方法 var func = function(name, age, sex) { this.name = name; this.age = age; this.sex = sex; }; // 创建对象 var o = {}; // 给对象添加成员 // apply 模式 var p1 = func.apply(o, ["赵晓虎", 19, "男"]); // call 模式 var p2 = func.call(o, "赵晓虎", 19, "男");
上面的代码中,apply模式和call模式的结果是一样的。
事实上,使用apply模式和call模式,可以任意操作和控制this的含义,这在function js的设计模式中被广泛使用。 简单总结一下,js中函数调用有四种模式,分别是:函数模式、方法模式、构造函数模式和apply模式。 在该模式下,this 的含义是:在函数中,this 是全局对象 window ,在方法中 this 指当前对象,在构造函数中 this 是创建的对象,在 apply 模式下,this 可以自由指定。 如果apply模式中使用null,则为函数模式,如果使用对象,则为方法模式。