js中面向对象编程是基于 构造函数(consstructor)和原型链(prototype) 的。
构造函数作为对象的模板。所谓构造函数,就是提供一个生成对象的模板,并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构。
为了与普通函数区别,构造函数名字的第一个字母通常大写。函数体内使用this关键字,代表所要生成的对象实例。生成对象时,必须使用new命令来调用构造函数。
object instanceof constructor
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
new 命令
new命令的作用就是执行构造函数并返回一个对象实例。执行时 顺序为:
1、创建新的空对象,作为将要返回的对象实例。
2、将空对象的原型指向构造函数的prototype属性
3、将空对象赋值给构造函数内部的this关键字
4、开始执行构造函数内部的代码。
基本用法
new命令的作用,就是调用一个构造函数,并且返回一个对象实例。
function Keith() {
this.height = 180;
}
var boy = new Keith();
console.log(boy.height); //180
上面代码中通过new命令,让构造函数Keith生成一个对象实例,并赋值给全局变量boy。这个新生成的对象实例,从构造函数Keith中继承了height属性。也就说明了这个对象实例是没有height属性的。在new命令执行时,就代表了新生成的对象实例boy。this.height表示对象实例有一个height属性,它的值是180。
使用new命令时,根据需要,构造函数也可以接受参数。
function Person(name, height) {
this.name = name;
this.height = height;
}
var boy = new Person('Keith', 180);
console.log(boy.name); //'Keith'
console.log(boy.height); //180
var girl = new Person('Samsara', 160);
console.log(girl.name); //'Samsara'
console.log(girl.height); //160
用以上的一个例子,来对构造函数的特点和new基本原理进行一个梳理。
上面代码中,首先,我们创建了一个构造函数Person,传入了两个参数name和height。构造函数Person内部使用了this关键字来指向将要生成的对象实例。
然后,我们使用new命令来创建了两个对象实例boy和girl。
当我们使用new来调用构造函数时,new命令会创建一个空对象boy,作为将要返回的实例对象。接着,这个空对象的原型会指向构造函数Person的prototype属性。也就是boy.prototype===Person.prototype的。要注意的是空对象指向构造函数Person的prototype属性,而不是指向构造函数本身。然后,我们将这个空对象赋值给构造函数内部的this关键字。也就是说,让构造函数内部的this关键字指向一个对象实例。最后,开始执行构造函数内部代码。
因为对象实例boy和girl是没有name和height属性的,所以对象实例中的两个属性都是继承自构造函数Person中的。这也就说明了构造函数是生成对象的函数,是给对象提供模板的函数。
一个问题,如果我们忘记使用new命令来调用构造函数,直接调用构造函数了,会发生什么?
这种情况下,构造函数就变成了普通函数,并不会生成实例对象。而且由于后面会说到的原因,this这时代表全局对象,将造成一些意想不到的结果。
function Keith() {
this.height = 180;
}
var person = Keith();
console.log(person.height); //TypeError: person is undefined
console.log(window.height); //180
上面代码中,当在调用构造函数Keith时,忘记加上new命令。结果是this指向了全局作用域,height也就变成了全局变量。而变量person变成了undefined。
因此,应该非常小心,避免出现不使用new命令、直接调用构造函数的情况。
为了保证构造函数必须与new命令一起使用,一个解决办法是,在构造函数内部使用严格模式,即第一行加上use strict。
function Person(name, height) {
'use strict';
this.name = name;
this.height = height;
}
var boy = Person();
console.log(boy) //TypeError: name is undefined
上面代码的Person为构造函数,use strict命令保证了该函数在严格模式下运行。由于在严格模式中,函数内部的this不能指向全局对象。如果指向了全局,this默认等于undefined,导致不加new调用会报错(JavaScript不允许对undefined添加属性)。
另一个解决办法,是在构造函数内部判断是否使用new命令,如果发现没有使用,则直接返回一个实例对象。
function Person(name, height) {
if (!(this instanceof Person)) {
return new Person(name, height);
}
this.name = name;
this.height = height;
}
var boy = Person('Keith');
console.log(boy.name) //'Keith'
上面代码中的构造函数,不管加不加new命令,都会得到同样的结果。
如果构造函数内部有return语句,而且return后面跟着一个复杂数据类型(对象,数组等),new命令会返回return语句指定的对象;如果return语句后面跟着一个简单数据类型(字符串,布尔值,数字等),则会忽略return语句,返回this对象。
function Keith() {
this.height = 180;
return {
height: 200
};
}
var boy = new Keith();
console.log(boy.height); //200
function Keith() {
this.height = 100;
return 200;
}
var boy = new Keith();
console.log(boy.height); //100
另一方面,如果对普通函数(内部没有this关键字的函数)使用new命令,则会返回一个空对象。
function Keith() {
return 'this is a message';
}
var boy = new Keith();
console.log(boy); // Keith {}
上面代码中,对普通函数Keith使用new命令,会创建一个空对象。这是因为new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象或数组。本例中,return语句返回的是字符串,所以new命令就忽略了该语句。
构造函数的继承
1、构造函数绑定:使用call或apply方法,将父对象的构造函数绑定在子对象上。
2、prototype模式:使用prototype属性。
网友评论