构造函数模式创建对象
创建对象的过程,以及对象标识
function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.sayName = function() {console.log(this.name);};}let person1 = new Person("Nicholas", 29, "Software Engineer");let person2 = new Person("Greg", 27, "Doctor");person1.sayName(); // Nicholasperson2.sayName(); // Greg
按照约定 ,构造函数首字母要大写。这是从面向对象编程语言那里借鉴的,有助于在 ECMAScript 中区分构
造函数和普通函数。毕竟,构造函数就是一个能创建对象的函数。
通过new 创建一个对象,会执行一下操作:
- 在内存中创建一个新对象。
- 这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性。
- 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。
- 执行构造函数内部的代码(给新对象添加属性)。
- 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
上述例子 person1 和 person2 分别保存着 Person 的不同实例。这两个对象都有一个
constructor 属性指向 Person,如下:
console.log(person1.constructor == Person); // trueconsole.log(person2.constructor == Person); // true
constructor 本来是用于标识对象类型的。不过,一般认为 instanceof 操作符是确定对象类型
更可靠的方式。前面例子中的每个对象都是 Object 的实例,同时也是 Person 的实例,如下面调用
instanceof 操作符的结果所示:
console.log(person1 instanceof Object); // trueconsole.log(person1 instanceof Person); // trueconsole.log(person2 instanceof Object); // trueconsole.log(person2 instanceof Person); // true
在实例化时,如果不想传参数,那么构造函数后面的括号可加可不加。只要有 new 操作符,就可以
调用相应的构造函数:
function Person() {this.name = "Jake";this.sayName = function() {console.log(this.name);};}let person1 = new Person();let person2 = new Person; //不加括号person1.sayName(); // Jakeperson2.sayName(); // Jake
构造函数也是函数
构造函数与普通函数唯一的区别就是调用方式不同。除此之外,构造函数也是函数。并没有把某个
函数定义为构造函数的特殊语法。任何函数只要使用 new 操作符调用就是构造函数,而不使用 new 操
作符调用的函数就是普通函数
// 作为构造函数let person = new Person("Nicholas", 29, "Software Engineer");person.sayName(); // "Nicholas"// 作为函数调用Person("Greg", 27, "Doctor"); // 添加到 window 对象window.sayName(); // "Greg"// 在另一个对象的作用域中调用let o = new Object();Person.call(o, "Kristen", 25, "Nurse");o.sayName(); // "Kristen"
构造函数的问题
构造函数虽然有用,但也不是没有问题。构造函数的主要问题在于,其定义的方法会在每个实例上
都创建一遍。因此对前面的例子而言,person1 和 person2 都有名为 sayName()的方法,但这两个方
法不是同一个 Function 实例。我们知道,ECMAScript 中的函数是对象,因此每次定义函数时,都会
初始化一个对象,如下展示,两个实例的sayName并不相等
console.log(person1.sayName == person2.sayName); // false
因为都是做一样的事,所以没必要定义两个不同的 Function 实例。况且,this 对象可以把函数
与对象的绑定推迟到运行时。
要解决这个问题,可以把函数定义转移到构造函数外部:
function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.sayName = sayName;}function sayName() {console.log(this.name);}let person1 = new Person("Nicholas", 29, "Software Engineer");let person2 = new Person("Greg", 27, "Doctor");person1.sayName(); // Nicholasperson2.sayName(); // Greg//两个实例的sayName相等console.log(person1.sayName == person2.sayName)//true
这样定义,就可以解决相同逻辑的函数重复定义的问题了。但这样也有很明显的问题。对象的方法定义在了外部,不能很好的和对象定义聚合在一快。另外就是,这里只有一个方法,如过对象有多个方法,就会扰乱全局作用域,造成混乱。这个问题可以通过原型模式来解决。
