JavaScript创建对象总结(上)
在JavaScript中对象被定义为无序属性的集合,其属性值可以是基本值、对象或者函数。
在这一系列文章我们将介绍
- 工厂模式
- 构造函数模式
- 原型模式
- 组合使用构造函数模式和原型模式
- 动态原型模式
- 寄生构造函数模式
- 稳妥构造函数模式
我们知道创建对象可以使用 Object构造函数 或者 字面量的形式 创建单个对象,但是这样会产生大量重复的代码。
// Object构造函数
var obj = new Object();
// 字面量
var obj = {};
// 例如我们要记录小明跟小红的姓名跟年龄
var xiaoming = {
name: "xiaoming",
age: 11
};
var xiaohong = {
name: "xiaohong",
age: 10
};
很明显上面例子代码重复,要是记录的信息多一点,代码重复率则越高,为了解决这个问题,就有了工厂模式
function createPerson(name, age) {
var person = {
name: name,
age: age,
getName: function() {
console.log(this.name);
}
};
return person;
}
var xiaoming = createPerson("xiaoming", 11);
xiaoming.getName() // xiaoming
但是工厂模式无法解决对象识别的问题,其实是无法辨认实例是由哪个构造函数创建
console.log(xiaoming instanceof createPerson); // false
console.log(xiaoming instanceof Object); // true
为了解决这个问题就有了构造函数模式
// 按照惯例,构造函数以大写字母开头,以区别普通函数。
function Person(name) {
this.name = name;
this.getName = function() {
console.log(this.name);
};
}
var xiaoming = new Person("xiaoming");
xiaoming.getName(); // xiaoming
// 成功解决工厂模式问题
console.log(xiaoming instanceof Person); // true
之所以成为构造函数是因为 new 操作符,要创建构造函数实例就必须使用 new 操作符,并且经历以下4个步骤
- 创建一个新对象
- 将构造函数的作用于赋给新对象(因此this指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回这个新对象
既然说到了构造函数,那自然要说说它的返回值。这其中分三种情况。
①: 没有指明具体的返回值,则返回该构造函数的实例化对象
function Person(name) {
this.name = name;
this.getName = function() {
console.log(this.name);
};
}
var xiaoming = new Person("xiaoming");
console.log(xiaoming);
②:若指明返回值,但不是引用类型 的话,与情况①结果相同,返回该构造函数的实例化对象。
function Person(name) {
this.name = name;
this.getName = function() {
console.log(this.name);
};
return 12;
}
var xiaoming = new Person("xiaoming");
console.log(xiaoming);
③:若指明返回值且是引用类型,则返回该引用类型
function Person(name) {
this.name = name;
this.getName = function() {
console.log(this.name);
};
return {
age: 12
};
}
var xiaoming = new Person("xiaoming");
console.log(xiaoming);
接下来,我们来看下构造模式与工厂模式的不同
- 构造函数没有很明显的创建对象
- 构造函数志军讲属性跟方法赋给了this
- 没有return语句
感觉构造函数很厉害?其实它与普通函数没有什么区别,只有用new操作符,它才是构造函数
// 函数调用方式
function Person(name) {
this.name = name;
this.getName = function() {
console.log(this.name);
};
}
// 构造函数调用
var xiaoming = new Person("xiaoming");
xiaoming.getName(); // xiaoming
// 普通函数调用
Person("xiaohong");
window.getName(); // xiaohong
// 此时this指向全局window,若不懂可阅读我的手记《JavaScript迷之this总结》
// 在另一个对象中调用
var obj = {};
Person.call(obj, "xiaodong");
obj.getName(); // xiaodong
但是构造函数虽然好,但也会带来一定的问题:构造函数中的方法在每一个实例都会被重新创建。
function Person(name) {
this.name = name;
this.getName = function() {
console.log(this.name);
};
}
var xiaoming = new Person("xiaoming");
var xiaohong = new Person("xiaohong");
console.log(xiaoming.getName == xiaohong.getName); // false
在上面例子中创建两个完成相同功能的实例方法完全没有必要,所以你可能会这么解决。
function Person(name) {
this.name = name;
this.getName = getName;
}
function getName() {
console.log(this.name);
}
var xiaoming = new Person("xiaoming");
var xiaohong = new Person("xiaohong");
console.log(xiaoming.getName == xiaohong.getName); // true
上面例子将getName()函数定义为全局函数,使其成为window的属性,而在Person构造函数内部的getName属性设置成全局函数getName(),这样getName属性的属性值就是一个指向全局getName()函数的指针,因此来解决构造函数的问题。
但是(不好的预感)你看下全局函数getName()函数只能被某个对象调用,这明显就是名不副实,而且上面只有一个方法还好,但是你要添加多少个方法,就要创建多少个全局函数。
这个时候就要原型模式出场了。
由于原型模式内容较多,并且较复杂,我们在下篇中讲解。
共同学习,写下你的评论
评论加载中...
作者其他优质文章