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(); // Nicholas person2.sayName(); // Greg
要创建person实例,就要使用new操作符调用Person构造函数,这样会产生如下操作:
1、在内存中创建一个新对象;
2、这个新对象内部的[[Prototype]]特性被赋值为构造函数的Prototype属性;
3、构造函数内部的this被赋值为这个新对象;
4、执行构造函数内部代码;
5、如果构造函数返回非空对象,则返回该对象;否则返回刚创建的新对象。
该例子中,person1和person2分别保存着Person的不同实例,这两个对象都有一个constructor属性指向Person:
console.log(person1.constructor == Person); // true console.log(person2.constructor == Person); // true
补充:
1、构造函数也是函数
构造函数与普通函数唯一的区别就是调用方式不同。除此之外,构造函数也是函数。并没有把某个函数转化为构造函数的语法。任何函数只要使用new运算符调用就是构造函数,不用new操作符调用就是普通函数,比如上面的Person:
// 作为构造函数 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"
2、构造函数的问题
构造函数的主要问题是其定义的方***在每个实例上都创建一遍。对前面的例子而言,person1和person2都有sayName方法,但却是不同的Function实例。因为都是做一件事,没有必要定义两个实例。想要解决这个问题,可以把函数定义转移到构造函数外部进行:
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(); // Nicholas person2.sayName(); // Greg
这样就解决了函数重复定义的问题,但又出现了新的问题。函数全部定义在外部扰乱了全局作用域,如果对象需要多个方法,那就要在全局作用域定义多个函数,这会导致自定义函数代码无法结合在一起,该问题可以通过原型模式解决。