02 function类型对象
loyalvi Lv7

02 🎈function类型对象

在 JS 中, function可以说是最核心的内容了。它本身是一种对象,另外,它还可以创建对象,而且可以对对象进行操作,所以这块内容可以说是 JS 中最复杂的内容 。
JS 中的 function 有三大作用 :

  • 作为对象来管理其中的属性
  • 作为方法处理具体业务
  • 创建对象
    本文首先介绍怎样创建function,然后分别介绍它的三种用法,最后介绍function的三种子类型

1 💎创建function

在 JS 中创建 function 的常用方法有两种:

1.1 函数声明

函数声明的结构如下

1
2
3
function 函数名(参数){
函数体
}

函数由4个部分组成

  • function关键词,固定不变
  • 函数名
  • 圆括号内存放函数参数
  • 花括号包含的函数体

1.2 函数表达式

函数表达式的结构与函数声明相比,是将函数声明中的函数名去掉,然后将创建的结果赋值给一个变量

1
2
3
var 变量 = function (参数){
函数体
}

函数表达式和函数声明出来的函数基本没有区别。

2个方式的关系

在 JS 中所有的数据只有两种存在形式,要么是对象的属性,要么是变量。
函数也不例外,无论是对象的属性还是变量都是名值对的结构,因此函数也应该是这种名值对的结构,由函数表达式可以很容易看明白这一点。
其实,通过函数声明方式创建函数时, JS 在背后自动帮用户做了这件事情,它首先创建了函数对象,然后又创建了和函数名同名的变量,并将创建出来的函数赋值给了这个变量 。

1
2
3
4
5
6
function(a){ 
console.log(a);
}
var f = F;
F = null;
f ('hello function');//输出 hello function

!Drawing 2024-04-13 23.28.52.excalidraw

2 💎用作对象

JS 中的函数本身也是对象,是对象就可以有自己的属性。函数对象的属性一般是使用点操作符来操作的,可以通过点给对象的属性进行赋值,如果属性不存在则直接创建,如果存在,则可以修改其内容 。
函数对象的属性也可以是直接量、 object 对象和 function 对象三种类型中的任意一种 。
如果是 function对象类型的属性,还可以通过点操作符来调用它执行相应逻辑。我们来看下面的例子 。

1
2
3
4
5
6
function func () {}; 
func.val = "go";
func.logVal = function () {
console.log(this.val);
}
func.logVal(); //go

05 点运算符与 this 关键字 该使用方式其实就是把 function 当做构造函数,可以参考 es6 新功能 class
function 对象也可以当作普通的object 对象来使用。

3 💎处理业务

在 JS 中,使用函数function对象用来处理业务是最常见的用法。JS 中真正对对象的操作大部分都是通过函数对象来执行的 。函数的创建方式前面已经介绍过,其中用来处理业务的是函数体。函数体主要包括变量、操作符和语句三大部分内容。

3.1 变量

js变量

3.2 语句

语句是用来执行具体功能的,可以分为单条语句和语句块 。 单条语句以分号结束,语句块用花括号包含,语句块可以包含多条语句 。
语句主要由变量(或属性)、操作符和关键字组成。有的语句使用操作符完成,还有的语句需要使用相应的关键字。下面将介绍使用关键字完成的语句。
js语句

3.3 变量作用域

这里所说的变量作用域主要是指使用 var 定义的变量的作用域,这种变量的作用域是 function 级的,这一点在前面讲 var 语句的时候已经提过了。 JS 中的 function 是可以嵌套使用的,嵌套的 function 中的变量的作用域又是怎样的呢?请先看个例子。

1
2
3
4
5
6
7
8
9
var v = O; 
function fl(){
var v = 1;
function f2(){
console.log(v);
}
f2();
}
fl(); //1

这个例子中,定义了全局变量 v ,在函数 f1 中定义了局部变量 v ,f2定义在 f1 函数中,当调用函数 f1 时,会在其内部调用函数 f2,f2中用到了变量 v ,这时 v 会使用 f1 函数中定义的 v 。
在调用嵌套函数时,引擎会根据嵌套的层次自动创建一个参数作用域链,然后将各层次函数所定义的变量从外到内依次存放到作用域链中。例如,在上述示例中,执行函数 f2时,会首先将全局对象(浏览器中指页面本身,也就是 Window 对象)放在最下层,然后放 f1 最后放 f2。

3.4 闭包

js闭包

💎4 创建对象

JS 中的 function 除了前面介绍的两种用法之外,还有一种非常重要的用法,那就是创建 object 实例对象。03 object类型对象详细介绍了 object 对象,本文主要介绍如何使用 function 来创建 object 对象以及创建时的一些细节问题。

4.1 创建方式

使用 function 对象创建 object 类型对象的方法非常简单,直接在 function 对象前面使用 new 关键字就可以了,例如下面的例子 。

1
2
3
4
5
function F() {
this.v = 1;
}
var obj = new F(); //创建F类型对象obj
console.log(obj.v); //1

这个例子中,首先定义了一个 function 类型的对象 F ,然后使用 F 创建了 object 类型的对象 obj ,最后在控制台打印出 obj 对象的 v 属性的值 。
使用 function (例如 F )创建 object 类型的对象(例如 obj ),只需要在 function 对象(F)前加 new 关键字就可以了。也就是说,对于一个 function 类型的对象,如果调用时前面没有 new 关键字,那么调用方法处理业务,如果前面有 new 关键字,那么用来创建对象。当然,创建对象时函数体也会被执行。
其实,经常使用的 Array,Date 等对象也都是 function 类型,可以使用 new 关键字来创建相应的 object 类型的对象实例。
为了区分主要用于处理业务的 function 和主要用于创建对象的 function ,一般会将主要用于创建对象的 function 的首字母大写而将主要用于处理业务的 function 的首字母小写 。 但这只是人为区分,实际使用时并没有什么影响。

4.2 创建过程

使用 function 创建 object 类型对象的过程可以简单地分为以下两步(可以这么理解,实际创建过程要复杂一些)。

  1. 创建 function 对应类型的空 object 类型对象 ;
  2. 将 function 的函数体作为新创建的 object 类型对象的方法来执行(主要目的是初始化 object 对象)
    例如下面的例子 。
1
2
3
4
5
6
function Car(color, displacement) { 
this.color= color ;
this.displacement = displacement;
}
var car = new Car("black","2.4T");
console.log(car.color,car.displacement); //black, 2.4T

这个例子中,首先创建了 function 类型的 Car ,然后使用它新建 object 类型的 car 实例对象。在新建 car 对象时首先会新建 Car 类型的空对象 car,然后再将 Car 函数作为新建对象的方法来调用,从而初始化新建的 car 实例对象,相当于下面的过程。

1
2
3
4
5
6
7
8
function Car(){} 
var car= new Car();
car.init = function(color, displacement) {
this.color= color ;
this.displacement = displacement;
}
car.init("black","2.4T");
console.log(car.color,car.displacement); //black, 2.4T

上述示例将原来 Car 对象中的函数体的内容放到新建的 car 的 init 方法中,在使用 Car 创建完 car 实例对象后,再调用 init 方法初始化,这种方式和前面例子中将初始化内容放到 Car 的函数体内的效果是完全相同的 。
需要特别注意的是,创建过程的第二步,也就是说,在使用 function 对象新建 object 对象时依然会执行 function 的函数体。通过下面的例子可以更加直观地看到这一点。

1
2
3
4
5
6
7
var name="和坤";
function Sikuquanshu() {
name="纪晓岚"
}
console.log(name);//和神
var skqs = new Sikuquanshu();
console.log(name);//纪晓岚

这个例子中存在一个全局变量 name ,原值为“和坤”,在函数 Sikuquanshu 内部将其改为“纪晓岚”,在上述代码中并没有直接执行此函数,但在使用它创建 skqs 对象时其函数体得到了执行,这从创建 skqs 对象前后打印出的内容就能看出来,全局变量 name 被修改了。在使用 function 对象创建实例对象时一定要注意这一点 。
理解了对象创建的过程就可以理解为什么在构造函数(例如 Car )中使用 this 可以将属性添加到新创建的对象上 。 因为这时的函数体就相当于新创建的对象的一个方法,方法中的 this 指的就是新创建的对象自身,给 this 赋值就是给新创建的对象赋值,因此在 function 对象中使用 this 就可以给新创建出来的对象添加属性,就像本书第一个例子中的 Car 方法的 this.color = color;语句,这条语句会给新创建的 car 对象添加 color 属性并将 color 参数的值赋给它。对于这一点,在后面讲到 object 的属性时还会做进一步介绍。
05 点运算符与 this 关键字
js对象在内存中怎样保存

4.3 💎prototype属性与继承

js中prototype属性与继承

5 三种子类型

前面介绍过 ES 中的 function 共有三种用法 :作为对象使用、处理业务以及创建 object 类型的实例对象 。 跟这三种用法相对应的有三种子类型,分别是对象的属性、变量(包括参数)和创建出来的 object 类型实例对象的属性。这三种子类型是相互独立的,而且也很容易区分 。

5.1 function 作为对象来使用

这种情况下, function 对象的子类型就是对象自己的属性,这时通过点操作符“.”(或者方括号操作符)使用,例如下面的例子。

1
2
3
4
function book(){} 
book.price = 161.0;
book.getPrice = function(){return this.price;}
console.log (book.getPrice()) ; //161

在这种情况下, function 是作为 object 类型的对象来使用的。上面的例子中首先定义了 function 类型的 book 对象,然后给它添加了 price 属性和 getPrice 方法,这时就可以直接使用点操作符来对其进行操作了。

5.2 function 用于处理业务

这种情况下, function 的子类型就是自己定义的局部变量(包括参数),这时的变量是在方法被调用时通过变量作用域链来管理的。变量作用域链的相关内容前面已经介绍过,这里就不再重述了。

5.3 function 用于创建对象

这种情况下,对应的子类型是使用 function 创建的实例对象的属性,主要包括在 function 中通过 this 添加的属性,以及创建完成之后实例对象自己添加的属性。另外,还可以调用 function 的 prototype 属性对象所包含的属性,例如前面用过的 Car 的例子。

1
2
3
4
5
6
function Car(color, displacement) { 
this.color= color ;
this.displacement = displacement;
}
var car = new Car("black","2.4T");
console.log(car.color,car.displacement); //black, 2.4T

这个例子中创建的 car 对象就包含有 color 和 displacement 两个属性,而且还可以调用 Car. prototype 的 logMessage 方法。当然,创建完之后还可以使用点操作符给创建的 car 对象添加或者修改属性,也可以使用 delete 删除其中的属性,例如下面的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Car(color, displacement) { 
this.color= color ;
this.displacement = displacement;
}
Car.prototype.logMessage = function(){
console.log(this.color,this.displacement);
}
var car = new Car("black","2.4T");
car.logColor = function(){
console.log(this.color);
}
car.logColor() //black
car.color = "red"
car.logColor() //red
delete car.color
car.logMessage() //undefined 2.4T

这个例子中,在创建完 car 对象后又给它添加了 logColor 方法,可以打印出 car 的 color 属性 。 添加完 logColor 方法后直接调用就可以打印出 car 原来的 color 属性值( black ) 。 然后, 将其修改为 red ,再打印就打印出了 red 。 最后,使用 delete 删除 car 的 color 属性,这时再调用 logColor 方法就会打印出 undefined 。

5.4 三种子类型的关系

function 的三种子类型是相互独立的,它们只能在自己所对应的环境中使用而不能相互调用,例如下面的例子 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Bird() {
var name = "kitty ";
this.type = "pigeon"
this.getName = function() {
return this.name;
}
}
Bird.color = "white";
Bird.getType = function() {
return this.type;
}
Bird.prototype.getColor = function() {
return this.color;
}
var bird = new Bird();
console.log(bird.getColor());//undefined
console.log(bird.getName());//undefined
console.log(Bird.getType());//undefined

这个例子中的最后三条语句都会打印出 undefined ,下面分析其中的原因。

  • Bird 作为对象时包含 color 和 getType 两个属性
  • 作为处理业务的函数时包含一个名为 name 的局部变量
  • 创建的实例对象 bird 具有 type 和 getName 两个属性,而且还可以调用 Bird.prototype 中的 getColor 属性, getColor 也可以看作 bird 的属性
    每种用法中所定义的方法只能调用相应用法所对应的属性,而不能交叉调用, getName 、 getColor 和 getType 三个方法都获取不到对应的值,所以它们都会输出 undefined 。
用法 子类型
对象 Bird color,getType
处理业务 Bird 方法 name
创建实例对象 bird type,getName,(getColor)
另外, getName 和 getColor 是 bird 的属性方法, getType 是 Bird 的属性方法,如果用 Bird 对象调用 getName 或 getColor 方法或者使用 bird 对象调用 getType 方法都会抛出找不到方法的错误 。
除了三种子类型不可以相互调用之外,还有一种情况也非常容易被误解,那就是对象的属性并没有继承的关系,例如下面的例子 。
1
2
3
4
5
6
7
8
function obj() {} 
obj.v = 1;
obj.func = {
logV: function() {
console.log(this.v);
}
}
obj.func.logV();

这个例子中的 obj 是作为对象使用的, obj 有一个属性 v 和一个对象属性 func, func 对象中又有一个 logV 方法, logV 方法用于打印对象的 v 属性。这里需要特别注意, logV 方法打印的是 func 对象的 v 属性,但是 func对象并没有 v 属性,所以最后会打印出 undefined 。
在这个例子中,虽然 obj 对象中包含 v 属性,但是由于属性不可以继承,所以 obj 的 func 属性对象中的方法不可以使用 obj 中的属性 v 。这一点各位读者一定要记住,并且不要和 prototype 的继承以及变量作用域链相混淆。
JS中公有属性私有属性和静态属性

由 Hexo 驱动 & 主题 Keep
访客数 访问量