对象:在js中,ECMA-262把对象定义为是无序属性的集合,其属性包括基本值,对象或函数。可看作散列表,表内是一组没有特定顺序的值,对象的每个属性都映射到一个值。每个对象都是基于一个引用类型创建的,这个引用类型可以是原生类型,也可以是开发人员自定义的类型。
在js中创建对象有四种方式:工厂模式、构造函数模式、原型模式、组合使用构造函数模式和原型模式
前三种方式都有一定的弊端,组合模式是结合前两种模式的优点的一种相对较优模式。此处依次讲解。
1、工厂模式:用函数封装以特定接口创建对象的过程。在函数内创建对象,为对象添加属性和方法,然后返回对象。
function creat(name,age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayAge = function() {
console.log(this.name);
};
return obj;
}
creat("qpq",21);
缺点:工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别问题,即无法知道对象的类型。不过接下来这种方法——构造函数模式解决了这一缺点。
2、构造函数模式:创建特定类型的对象,从而定义自定义类型的属性和方法。
function Creat(name,age) {
this.name = name;
this.age = age;
this.sayAge = function() {
console.log(this.name);
}
}
var one = new Creat("qpq",21);
var two = new Creat("qpq2",22);
(1)要创建构造函数的实例必须使用new,这种方式会经历下列四个步骤:
①创建一个新对象
②将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
③执行构造函数中的代码(为新对象添加属性)
④返回新对象
(2)要判断对象实例的类型:
instanceof:
console.log(one instanceof Creat);
console.log(one instanceof Object);
one既是构造函数的实例同时也算是Object的实例,因为所有对象都继承自Object.
(3)构造函数和其他函数唯一的区别是:调用它们的方式不同。
(4)使用构造函数的主要问题:每个方法都要在每个实例上重新创建一遍,且多个实例创建的方法指向自身,并非共享,会导致不同的作用域和标识符解析。但是创建多个完成同样任务的function并没有必要,且有this对象在,跟本没有必要执行对象前将函数绑定到特定对象上面。原型模式解决了这个问题。
3、原型模式:我们创建的每一个函数都有一个prototype(原型)属性,它是一个指针,指向一个对象。它的用途是包含特定类型的所有实例共享的属性和方法。即prototype就是通过调用构造函数创建的实例的原型对象。使用原型可以让所有对象实例共享它包含的属性和方法。
function Creat() {}
Creat.prototype.name = "qpq";
Creat.prototype.age = 21;
Creat.prototype.sayAge = function() {
console.log(this.age);
};
var one = Creat();
虽然原型模式实现了资源共享问题,但它依然存在一些一些不足:
(1)它省略了为构造函数初始化参数一环节,导致所有实例在默认情况下都取得相同的属性和方法。
(2)最大的问题是:对于包含引用类型值的属性,由于原型共享的本质和引用特性会导致所有实例对象中引用同化,即改变其中一个示例中的引用到导致另外实例中的引用值也发生改变。不过接下来讲解的方法将解决上述所有的问题。
4、组合使用构造函数和原型模式:构造函数用于定义实例化属性,而原型属性用于定义方法和共享属性。
function Creat(name,age) {
this.name = name;
this.age = age;
this.arr = [1,2,3];
}
Creat.prototype = {
constructor: Creat,
sayAge: function() {
console.log(this.age);
}
}
var one = new Creat("qpq",21);
one.arr.push(4);
var two = new Creat("qpq",21);
console.log(one);
//Creat {name: "qpq", age: 21, arr: Array(4)}
console.log(two);
//Creat {name: "qpq", age: 21, arr: Array(3)}
或者写成下面这种形式:
function Creat(name,age) {
this.name = name;
this.age = age;
this.arr = [1,2,3];
}
Creat.prototype = {
sayAge: function() {
console.log(this.age);
}
}
Object.defineProperty(Creat.prototype,"constructor",{
enumerable: false,
value: Creat})
//在defineProperty中设置Creat.prototype中的constructor属性,可以将用户自己设置的constructor中的enumerable:true变为enumerable:false不可枚举.
var one = new Creat("qpq",21);
one.arr.push(4);
var two = new Creat("qpq",21);
console.log(one);
//Creat {name: "qpq", age: 21, arr: Array(4)}
console.log(two);
//Creat {name: "qpq", age: 21, arr: Array(3)}
优点:
(1)解决了前三种方法存在的问题,且结合了它们的优点。
(2)每个实例都有属于自身的实例属性,同时又共享者对方法的引用,最大限度的节省了内存。
(3)支持向构造函数传递参数,可实现自定义实例属性值。