细看javascript创建对象模式的诞生,具体的脉络为:不使用任何模式——工厂模式——构造函数模式——原型模式——组合使用构造函数模式——动态原型模式——寄生构造函数模式——稳妥构造函数模式。每一种模式都是为了解决一定的问题而诞生的,都有自己的缺陷和优势。下面我们来一一介绍:
一、不使用任何模式
javascript中创建对象的方式有以下两种:
//使用new Object构造函数 var person = new Object(); person.name = "Lillian"; person.age = 24; person.sayName = function(){ return person.name; } //使用对象字面量 var person = { name:"Lillian", age:24, sayName:function(){ return name; } };
二、使用工厂模式
当要同时创建多个对象时,使用这两种方式都会产生大量重复的代码,为了解决这个问题,工厂模式应运而生:
//工厂模式 function createPerson(name , age){ var o = new Object(); o.name = name; o.age = age; o.sayName = function(){ return this.name; } return o; } var person1 = createPerson("Lillian",24);
通过工厂模式,只需要一行代码,就能创建一个对象,但是工厂模式存在一个问题:无法检测实例的类型。
三、使用构造函数模式
//构造函数模式 function Person(name, age){ this.name = name; this.age = age; this.sayName = function(){ return this.name; } } var person1 = new Person("Lillian", 24); //检测实例类型 alert(person1 instanceof Person); //true alert(person1 instanceof Object); //true
使用构造函数模式,同样可以一行代码创建一个对象实例,并且可以检测实例类型,这也是构造函数模式胜过工厂模式的地方。
四、原型模式
使用构造函数模式的问题是,每一个实例都具有一些相同的属性和方法,创建实例时,这些属性和方法会重复创建;而使用原型模式,可以将这些属性和方法放在原型对象中,这样以这个对象为原型的实例,都共享原型的属性和方法,不需要单独创建。
//原型模式 function Person(){ } Person.prototype.name = "Lillian"; Person.prototype.age = 24; Person.prototype.sayName = function(){ alert(this.name); } // person1 和 person2 共享以上原型的属性和方法 var person1 = new Person(); person1.sayName(); var person2 = new Person(); person2.sayName();
这里创建的函数和它的原型对象,以及实例是怎么联系起来的呢?
无论什么时候,只要创建了一个新函数,函数就会默认带上一个叫做prototype的属性,这个属性指向函数的原型对象;而原型对象也会自动获得一个叫做constructor的属性,指向这个新函数(存在多个函数时,指向所有prototype属性指向该原型的函数);函数的实例当然也带上了prototype属性;这样它们就连接了起来。
在现实中是没有办法访问prototype属性的,我们可以通过isPrototypeOf函数来确定对象之间是否存在这种关系:
alert(Person.prototype.isPrototypeOf(person1)); //true
当代码搜索属性时,它会现在实例中找这个名字的属性,如果找到就返回,没有找到则在原型对象中搜索(因此,可以在实例中重新定义属性和方法来覆盖原型中的属性和方法)。那么,我们怎么知道,一个属性,它是在原型中定义的,还是在实例中定义的呢?判断给定属性是在实例中,有现成的函数:hasOwnProperty();判断给定属性是在原型中,没有现成的函数,这里用到hasOwnProperty函数和in操作符实现:
// hasOwnProperty():给定属性在实例中时,才会返回true // in 操作符:函数存在给定属性,无论是在实例还是原型中,都返回true //以下函数实现判断属性是否存在于原型中 function hasPrototypeProperty(object, name){ return (name in object) && !(object.hasOwnProperty(name)); }
使用对象字面量的方式,可以更加方便的定义原型,不过注意,需要手动指定原型的constructor属性。
function Person(){ } Person.prototype = { constructor:Person, name:"Lillian", age: 24, sayName:function(){ alert(this.name); } };
另外,原型具有动态性,对原型的修改,会立即反应在它constructor属性所指的对象上,但修改不包括重写整个原型。
原型模式相比构造函数模式,具有共享属性和方法的优点,但是这也带来了问题:当原型的某个对象修改了共享的属性时,所有的对象的属性也都相应修改了。
五、组合使用构造函数模式和原型模式
哐哐哐~~~使用构造函数和原型混合而成的模式,是目前ECMAScript中使用最广泛、认可度最高的一种自定义模式!!构造函数用于定义实例自己的属性,原型用于定义共享的方法,如下:
function Person(name , age){ this.name = name; this.age = age; } Person.prototype = { constructor:Person, sayName:function(){ alert(this.name); } }; var person1 = new Person("Lillian", 24); var person2 = new Person("Matthew", 23); person1.sayName(); person2.sayName();
六、动态原型模式
动态原型模式,与构造函数和原型结合的模式类似,只是它通过检查是否存在需要的函数,再来决定是否动态创建原型。
function Person(name , age){ this.name = name; this.age = age; if(typeof this.sayName != "function"){ Person.prototype.sayName = function(){ alert(this.name); } } }
七、寄生构造函数模式
这种模式不推荐使用,除了使用new操作符,把包装函数叫做构造函数之外,它其实和工厂模式是一模一样的(寄生构造函数这个名称,可以想象为:工厂模式寄生在构造函数里面):
function Person(name , age){ var o = new Object(); o.name = name; o.age = age; o.sayName = function(){ alert(this.name); } return o; } var person1 = new Person("Lillian", 24);
八、稳妥构造函数模式
稳妥构造函数定义了一个稳妥对象,所谓的稳妥对象,是指没有公共属性,而且其方法也不会引用this对象。稳妥对象适合在对安全要求性比较高的环境来使用(这些环境禁止使用this和new),或者防止数据被其他应用程序更改的场合。它和寄生构造函数模式非常类似,只是去掉了this;创建实例时,也不使用new操作符,这样,对属性的引用,只能通过定义的函数了。
function Person(name , age){ var o = new Object(); o.name = name; o.age = age; o.sayName = function(){ alert(name); } return o; } var person1 = Person("Lillian", 24); person1.sayName();
以上是ECMAScript中定义的7种创建对象的模式。另外,对象的属性,其实也并不简单,属性都具有4个描述其行为的特性:
// Configurable:是否可以通过delete删除属性而重新定义属性 // Enumerable:是否可以通过for-in循环返回属性 // Writable:是否可以修改属性的值 // Value:包含这个属性的数据值 // 使用Object.defineProperty()函数来修改属性的默认特性,如下配置name属性不可更改 var person = { }; Object.defineProperty(person, "name",{ writable:false, value:"Lillian" }); alert(person.name);//"Lillian" person.name = "Matthew"; alert(person.name);//"Lillian"