一、什么是构造函数?
构造函数 ,是一种特殊的方法。主要用来为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中,对于JavaScript的内置对象Number()、String()、Boolean()、Object()、Array()、Function()、Date()、RegExp()、Error()等都是构造函数;
构造函数的特点:
- 构造函数的首字母大写,用来区分于普通函数;
- 内部使用的this对象,来指向即将要生成的实例对象;
- 使用New来生成实例对象;
举个栗子:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayHello = function(){
console.log(this.name+":Hello!");
};
}
var person1 = new Person("lilei", 26, "Teacher"); //实例对象 person1
var person2 = new Person("xiaom", 27, "Doctor"); //实例对象 person2
构造函数与其他函数的唯一区别,就在于调用它们的方式不同。构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数
二、构造函数与对象
上一节我们说到在JavaScript中,几乎所有的事物都是对象:对象只是带有属性和方法的特殊数据类型
但是基本类型值不是对象,因而从逻辑上讲它们不应该有方法,实际上我们创建 String 类型时后台自动做了一些处理:
- 创建 String 类型的一个实例;
- 在实例上调用指定的方法;
- 销毁这个实例。
var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;
基本类型值不是对象,只有引用类型是对象,但是 我们可以让String成为引用类型:
var s1 = new String("some text");
当我们手动new一个字符串s1的时候,s1既是String又是引用类型,所以他是一个对象;
引用类型与基本包装类型的主要区别就是对象的生存期。使用 new 操作符创建的引用类型的实例, 在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一 行代码的执行瞬间,然后立即被销毁;
三、创建实例对象
要创建 Person 的新实例,必须使用 new 操作符,调用构造函数创建对象经过了以下几个过程:
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
- 执行构造函数中的代码(为这个新对象添加属性);
- 返回新对象;
在前面例子中,person1 和 person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数) 属性,该属性指向 Person:
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
四、判断对象类型
在JS中判断一个变量的类型经常会用 typeof 运算符,但是在使用 typeof 运算符来判断引用类型时,无论引用的是什么类型的对象,它都返回"object"。所以在判断对象的类型时我们可以使用 instanceof 运算符:
console.log(person1 instanceof Object); //true
五、 构造函数缺点
使用构造函数时每个方法都要在每个实例上重新创建一遍:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayHello = function(){
console.log(this.name+":Hello!");
};
//this.sayHello = new Function('console.log(this.name+":Hello!")')
}
new Function与声明函数在逻辑上是一样的,所以每次实例化Person对象时,sayHello 方法也是一个新实例,所以:
console.log(person1.sayName == person2.sayName); //false
注意:person1.sayName 与person1.sayName()不一样,person1.sayName()是指函数返回值,person1.sayName是指函数本身;
创建两个完成同样任务的 Function 实例的确没有必要,因此,我们可以通过把函数定义转移到构造函数外部来解决这个问题。例如:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayHello = sayHello;
}
function sayHello(){
console.log(this.name+":Hello!");
}
这样将 sayName 属性设置成指向全局函数 sayName() 的指针。sayName()只被实例化一次; 但是如果对象需要定义很多方法,那么就要定义很多个全局函数。而且在全局作用域中定义的函数实际上只被某个对象调用,这样不符合全局函数的理念。我们这个自定义的引用类型也丝毫没有封装性可言。好在这些问题可以通过使用原型模式来解决。所以下一节《 prototype(原型)》
文章参考:
《JavaScript 高级程序设计》中文译本 第三版