0%

js 对象的构造函数以及new命令

JavaScript有很强的面向对象编程的能力,面向对象编程是目前主流的编程范式。它将真实世界中各种复杂的关系,抽象为一个个对象,完成对真实世界的模拟。那么对象到底是什么?我们常说“new一个对象”,那么对象又是如何被“new”出来的?

对象是什么?

真实世界的任何物体都可以理解为一个对象,一本书、一辆车、一个人、一只狗、一个石头、一个数据库,甚至一个与远程数据库的连接也可以是对象。当实物抽象成对象,那么实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,正对对象进行编程。

从程序的角度来描述对象,对象是一个容器,它有自己的属性和方法。属性是对象的状态,方法是对象的行为,比如我们把一只狗抽象为对象,那么属性就是具体描述,比如狗有四条腿,有它自己的名字;方法就是狗具有的行为,比如可以吠叫,可以奔跑,可以进食。

如何生成对象?

对象是单个实物的抽象,通常需要一个模板,表示某一类实物的共同特征,然后根据这个模板生成对象。

在典型的面向对象编程语言中,比如C#或Java,都有“类”这个概念,所谓“类”,就是对象的模板,对象就是“类”的实例。但是JavaScript语言的对象体系,不是基于“类”的,而是基于“构造函数(constructor)”和“原型链(prototype)”。

JavaScript中的构造函数

JavaScript语言使用构造函数作为对象的模板。所谓构造函数,就是专门用来生成实例独享的函数。它是对象的模板,描述对象的基本结构。一个构造函数可以生成多个实例对象,这些实例对象都有相同的结构。

构造函数就是普通的函数,但是有自己的特征和用法。比如:

1
2
3
4
5
6
function Vehicle() {
this.name = 'tony';
this.getNmae = function(){
return this.name;
}
}

上面,Vehicle就是构造函数,通常构造函数名字的第一个字母需要大写,以方便和其他函数区分开来。

构造函数有两个特点:

  1. 函数内部使用了this关键字,代表了所要生成的对象实例。
  2. 生成对象的时候必须使用new命令。

首先来看第一条,构造函数是一个函数,不像其他语言中的类一样只能定义属性和行为,构造函数可以执行其他的语句。使用了this关键字的话,代表这是加在实例对象上的属性或操作,没有this关键字的话就理解为正常的函数里面的语句,比如我们在构造函数中加一条修改外部变量的语句,然后实例化:

1
2
3
4
5
6
7
8
9
10
var a = 1;
function Vehicle() {
this.name = 'tony';
a = 3;
this.getNmae = function(){
return this.name;
}
}
var obj = new Vehicle();
console.log(a); // a=3

再来理解第二条,这就再次唤起了我们的好奇,为什么一定要用new来实例化对象?new的时候发生了什么?

先来看看new命令的基本用法

new 命令基本用法

new命令的作用,就是执行构造函数,返回一个对象实例。比如我们实例化上面的Vehicle

1
var obj = new Vehicle();

我们通过new命令,让构造函数Vehicle生成了一个实例对象并保存在变量obj中,现在obj这个对象具有我们定义在构造函数中的属性name以及行为getName

new命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号,他们是等价的,但是为了表示调用函数和可读性,还是推荐带括号。例如:

1
2
var v = new Vehicle(); // ok
var v = new Vehicle; // ok

如果忘了使用new命令会发生什么?

如果没有加上new命令,那么构造函数就变成了调用普通函数,并不会生成实例独享,而且,this这是代表全局,将会造成一些意想不到的结果。

应当尽量避免不使用new命令直接调用构造函数,解决办法是,构造函数内部使用严格模式,即在第一行加上use strict。这样,如果忘了new命令直接调用构造函数就会报错。

why?

因为在严格模式中,函数内部的this不能指向全局对象,默认等于undefined,而JavaScript不允许对undefined添加属性,就会导致报错。

另一个办法是在构造函数内部进行判断是否使用new命令,如果发现没有使用,就直接返回一个实例对象:

1
2
3
4
5
6
7
8
9
10
function Vehicle() {
if (!(this instanceof Vehicle)) {
return new Vehicle();
}
this.name = 'tony';
this.getNmae = function(){
return this.name;
}
}
var obj = Vehicle(); // ok

为什么可以new一个对象?

使用new命令时,依次执行下面的步骤:

  1. 创建一个空对象,作为将要返回的对象的实例。
  2. 将这个空对象的原型,指向构造函数的prototype属性。
  3. 将这个空对象赋值给函数内部的this关键字。
  4. 开始执行构造函数内部的代码。

也就是说,构造函数内部的this是一个空对象,所有针对this的操作,都会发生在这个空对象上。

构造函数之所以叫“构造函数”,就是操作一个空对象,将其“构造”为需要的样子。

如果构造函数内部有return语句,而且return后面跟着一个对象,new命令就会返回return语句指定的对象,否则,就不管return语句,返回this对象。

1
2
3
4
5
6
7
8
9
function Vehicle() {
this.name = 'tony';
this.getNmae = function(){
return this.name;
}
return [];
}
var obj = new Vehicle();
console.log(obj); // obj=[]

new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象。

最后了解下Object.create()创建实例对象。

Object.create() 没有构造函数时创建对象

构造函数作为模板可以实例化为对象,但是,有时候没有构造函数,只能拿到一个现有对象,这时我们可以使用Object.create()方法来以一个对象为模板创建一个新的对象。例如:

1
2
3
4
5
6
7
8
9
10
11
var obj = {
name: 'tony',
getName: function() {
return this.name;
}
};

console.log(obj.getName()); // tony
obj.name = 'stack';
var obj1 = Object.create(obj);
console.log(obj1.getName()); // stack

注意:在对象里面操作自己的属性的时候需要加this指向自己,要不然会被对象外面的元素覆盖。

码字辛苦,打赏个咖啡☕️可好?💘