学习目标
JavaScript 语言使用构造函数作为对象的模板。 所谓 ”构造函数”,就是一个普通的函数,只不过我们专门用它来生成对象(new 构造函数),这样使用的函数,就是构造函数;
它提供模板,描述对象的基本结构。 一个构造函数,可以生成多个对象,这些对象都有相同的结构。
function Person (name, age) {
this.name = name
this.age = age
this.sayName = function () {
console.log(this.name)
}
}
var p1 = new Person('Jack', 18)
p1.sayName() // => Jack
var p2 = new Person('Mike', 23)
p2.sayName() // => Mike
解析 构造函数代码 的执行
在上面的示例中,Person()
函数取代了 createPerson()
函数,但是实现效果是一样的。
这是为什么呢?
我们注意到,Person()
中的代码与 createPerson()
有以下几点不同之处:
this
return
语句Person
而要创建 Person
实例,则必须使用 new
操作符。
以这种方式调用构造函数会经历以下 5 个步骤:
prototype
属性。先记住,后面讲
this
关键字。function Person (name, age) {
// 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象
// 然后让内部的 this 指向新创建的对象
// 接下来所有针对 this 的操作实际上操作的就是刚创建的这个对象
this.name = name
this.age = age
this.sayName = function () {
console.log(this.name)
}
// 在函数的结尾处会将 this 返回,也就是这个新对象
}
构造函数和实例对象的关系
构造函数是根据具体的事物抽象出来的抽象模板
实例对象是根据抽象的构造函数模板得到的具体实例对象
实例对象由构造函数而来,一个构造函数可以生成很多具体的实例对象,而每个实例对象都是独一无二的;
每个对象都有一个 constructor
属性,该属性指向创建该实例的构造函数
反推出来,每一个对象都有其构造函数
console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true
因此,我们可以通过实例对象的 constructor
属性判断实例和构造函数之间的关系
注意:这种方式不严谨,推荐使用 instanceof
操作符,后面学原型会解释为什么
console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true
constructor 既可以判断也可以获取
instanceof 只能用于判断
以构造函数为模板,创建对象,对象的属性和方法都可以在构造函数内部定义;
function Cat(name, color) {
this.name = name;
this.color = color;
this.say = function () {
console.log('hello'+this.name,this.color);
};
}
var cat1 = new Cat('猫', '白色');
var cat2 = new Cat('猫', '黑色');
cat1.say();
cat2.say();
在该示例中,从表面上看好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象, name
和 say
都是一模一样的内容,
每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。
对于这种问题我们可以把需要共享的函数定义到构造函数外部:
function say(){
console.log('hello'+this.name,this.color);
}
function Cat(name, color) {
this.name = name;
this.color = color;
this.say = say;
}
var cat1 = new Cat('猫', '白色');
var cat2 = new Cat('猫', '黑色');
cat1.say();
cat2.say();
这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局变量(函数名)冲突的问题。
你肯定想到了可以把多个函数放到一个对象中用来避免全局变量(函数名)冲突的问题:
var s = {
sayhello:function (){
console.log('hello'+this.name,this.color);
},
saycolor:function(){
console.log('hello'+this.color);
}
}
function Cat(name, color) {
this.name = name;
this.color = color;
this.sayhello = s.sayhello;
this.saycolor = s.saycolor;
}
var cat1 = new Cat('猫', '白色');
var cat2 = new Cat('猫', '黑色');
cat1.sayhello();
cat2.saycolor();
至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。 但是代码看起来还是那么的格格不入,那有没有更好的方式呢?
小结