JavaScript有很强的面向对象编程的能力,面向对象编程是目前主流的编程范式。它将真实世界中各种复杂的关系,抽象为一个个对象,完成对真实世界的模拟。那么对象到底是什么?我们常说“new一个对象”,那么对象又是如何被“new”出来的?
对象是什么?
真实世界的任何物体都可以理解为一个对象,一本书、一辆车、一个人、一只狗、一个石头、一个数据库,甚至一个与远程数据库的连接也可以是对象。当实物抽象成对象,那么实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,正对对象进行编程。
从程序的角度来描述对象,对象是一个容器,它有自己的属性和方法。属性是对象的状态,方法是对象的行为,比如我们把一只狗抽象为对象,那么属性就是具体描述,比如狗有四条腿,有它自己的名字;方法就是狗具有的行为,比如可以吠叫,可以奔跑,可以进食。
如何生成对象?
对象是单个实物的抽象,通常需要一个模板,表示某一类实物的共同特征,然后根据这个模板生成对象。
在典型的面向对象编程语言中,比如C#或Java,都有“类”这个概念,所谓“类”,就是对象的模板,对象就是“类”的实例。但是JavaScript语言的对象体系,不是基于“类”的,而是基于“构造函数(constructor)”和“原型链(prototype)”。
JavaScript中的构造函数
JavaScript语言使用构造函数作为对象的模板。所谓构造函数,就是专门用来生成实例独享的函数。它是对象的模板,描述对象的基本结构。一个构造函数可以生成多个实例对象,这些实例对象都有相同的结构。
构造函数就是普通的函数,但是有自己的特征和用法。比如:
1 | function Vehicle() { |
上面,Vehicle
就是构造函数,通常构造函数名字的第一个字母需要大写,以方便和其他函数区分开来。
构造函数有两个特点:
- 函数内部使用了
this
关键字,代表了所要生成的对象实例。 - 生成对象的时候必须使用
new
命令。
首先来看第一条,构造函数是一个函数,不像其他语言中的类一样只能定义属性和行为,构造函数可以执行其他的语句。使用了this
关键字的话,代表这是加在实例对象上的属性或操作,没有this
关键字的话就理解为正常的函数里面的语句,比如我们在构造函数中加一条修改外部变量的语句,然后实例化:
1 | var a = 1; |
再来理解第二条,这就再次唤起了我们的好奇,为什么一定要用new
来实例化对象?new
的时候发生了什么?
先来看看new
命令的基本用法
new 命令基本用法
new
命令的作用,就是执行构造函数,返回一个对象实例。比如我们实例化上面的Vehicle
:
1 | var obj = new Vehicle(); |
我们通过new
命令,让构造函数Vehicle
生成了一个实例对象并保存在变量obj
中,现在obj
这个对象具有我们定义在构造函数中的属性name
以及行为getName
。
new
命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号,他们是等价的,但是为了表示调用函数和可读性,还是推荐带括号。例如:
1 | var v = new Vehicle(); // ok |
如果忘了使用new
命令会发生什么?
如果没有加上new
命令,那么构造函数就变成了调用普通函数,并不会生成实例独享,而且,this
这是代表全局,将会造成一些意想不到的结果。
应当尽量避免不使用new
命令直接调用构造函数,解决办法是,构造函数内部使用严格模式,即在第一行加上use strict
。这样,如果忘了new
命令直接调用构造函数就会报错。
why?
因为在严格模式中,函数内部的this
不能指向全局对象,默认等于undefined
,而JavaScript不允许对undefined
添加属性,就会导致报错。
另一个办法是在构造函数内部进行判断是否使用new
命令,如果发现没有使用,就直接返回一个实例对象:
1 | function Vehicle() { |
为什么可以new一个对象?
使用new
命令时,依次执行下面的步骤:
- 创建一个空对象,作为将要返回的对象的实例。
- 将这个空对象的原型,指向构造函数的
prototype
属性。 - 将这个空对象赋值给函数内部的
this
关键字。 - 开始执行构造函数内部的代码。
也就是说,构造函数内部的this
是一个空对象,所有针对this
的操作,都会发生在这个空对象上。
构造函数之所以叫“构造函数”,就是操作一个空对象,将其“构造”为需要的样子。
如果构造函数内部有return
语句,而且return
后面跟着一个对象,new
命令就会返回return
语句指定的对象,否则,就不管return
语句,返回this
对象。
1 | function Vehicle() { |
new
命令总是返回一个对象,要么是实例对象,要么是return
语句指定的对象。
最后了解下Object.create()
创建实例对象。
Object.create() 没有构造函数时创建对象
构造函数作为模板可以实例化为对象,但是,有时候没有构造函数,只能拿到一个现有对象,这时我们可以使用Object.create()
方法来以一个对象为模板创建一个新的对象。例如:
1 | var obj = { |
注意:在对象里面操作自己的属性的时候需要加this
指向自己,要不然会被对象外面的元素覆盖。