虽然Object构造函数或对象字面量,或者Object.create()均可以用来建立单个对象,但这些方式有个明显的缺点:使用同一个接口建立不少对象,会产生大量的重复代码。java
这种将建立对象的封装成为函数,调用函数来建立对象。用函数来封装以特定接口建立对象的细节.windows
function createPerson(age,name){ var o = {}; o.age = age; o.name = name; o.sayName = function(){ console.log(this.name); } return o; // 不能肯定对象的类型。 } var p = createPerson('zhangsan',25); p.sayName(); // zhangsan
在上面咱们建立的是Person类型的对象p,可是却不能判断他的类型是Person。
工厂模式虽然解决了建立多个类似对象的问题,但却没有解决对象识别的问题。app
建立自定义的构造函数,从而定义自定义对象类型的属性和方法。这样就能够将实例对象指定为特定对象了。注意,该模式直接去用this定义属性和方法,并无显示的建立对象,且没有返回值。
改写前面的例子:函数
function Person(age,name){ this.name = name; this.age = age; this.sayName = function(){ console.log(this.name) } } var p = new Person();
var p = new Person();
执行含义是:
1.建立一个新对象
2.将构造函数的做用域赋给新对象(所以 this 就指向了这个新对象);
3.执行构造函数中的代码(为这个新对象添加属性);
4.返回新对象。
注意: 若将构造函数当成普通函数使用,this指向windows.
使用够函数的弊端就是,方法也会在每一个实例上在建立一次,浪费内存。this
为了不这空间的浪费,咱们能够把函数转移到函数外部。prototype
function sayName(){ console.log(this.name) } function Person(age,name){ this.name = name; this.age = age; this.sayName = sayName; } var p = new Person();
在全局做用域中定义的函数实际上只能被某个对象调用,这让全局做用域有点名存实亡。而更让人没法接受的是:若是对象须要定义不少方法,那么就要定义不少个全局函数,因而咱们这个自定义的引用类型就丝毫没有封装性可言了。code
这种模式的基本思想是建立一个函数,该函数的做用仅仅是封装建立对象的代码,而后再返回新建立的对象;但从表面上看,这个函数又很像是典型的构造函数。
寄生构造函数模式与构造函数模式有相同的问题,每一个方法都要在每一个实例上从新建立一遍,建立多个完成相同任务的方法彻底没有必要,浪费内存空间并且使用该模式返回的对象与构造函数之间没有关系。所以,使用instanceof运算符和prototype属性都没有意义。
适用于对象的包装,相似于java的装饰器模式。 返回一个包装对象。
基本样式:对象
function Person(name, age, job){ var o = new Object(); // 类型是Object。 和Person无关 o.name = name; o.age = age; o.sayName = function(){ alert(this.name); }; return o; } var friend = new Person("zhangsan",25); friend.sayName(); //"zhangsan"
应用:让建立的Array对象拥有新的方法(在不改变原有代码的基础上)接口
function SpecialArray() { var value = new Array(); value.push.apply(value,arguments); value.toPipedString = function(){ // 对Array对象的一层包装 return this.join("|"); } return value; }
稳妥构造函数遵循与寄生构造函数相似的模式,但有两点不一样:一是新建立对象的实例方法不引用 this;二是不使用 new 操做符调用构造函数。
特色: 没有公共属性,并且其方法也不引用this的对象,instanceof失效。和寄生构造函数的不一样在于不使用new来构造函数,同时实例方法不引用this。ip
function Person(name,age,job){ //建立要返回的对象 var o = new Object(); //能够在这里定义私有变量和函数 //添加方法 o.sayName = function(){ console.log(name); // 注意这里访问的是形参的值, 并非对象身上的name属性 }; //返回对象 return o; } //在稳妥模式建立的对象中,除了使用sayName()方法以外,没有其余方法访问name的值 var p = Person("zhangsan",25); p.name = 'lisi' // 即便能够为这个对象修改了属性name, 单sayName访问是形参name p.sayName();//"zhangsan"
使用原型对象的好处是可让全部对象实例共享它所包含的属性和方法。换句话说,没必要在构造函数中定义对象实例的信息,而是能够将这些信息直接添加到原型对象中。
function Person(){}; Person.prototype = { // constructor:Person, // 这样添加是可枚举的 name: "zhangsan", age: 25, sayName : function(){ console.log(this.name); } }; // 默认状况下,原生的constructor属性是不可枚举的, 因此最好设置为不可枚举的 Object.defineProperty(Person.prototype,'constructor',{ enumerable: false, value: Person }); var person1 = new Person(); person1.sayName();//"bai" console.log(person1.constructor === Person);//true console.log(person1.constructor === Object);//false
上述p和p1对象的属性和方法都是共享的。原型模式问题在于引用类型值属性会被全部的实例对象共享并修改。
建立自定义类型的最多见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每一个实例都会有本身的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数,是最适用的一种模式。
function Person(name,age,job){ this.name = name; this.age = age; this.friends = ["zhangsan","lisi"]; } Person.prototype = { constructor: Person, sayName : function(){ console.log(this.name); } } var person1 = new Person("wangwu",29); person1.friends.push("zhaoliu"); console.log(person1.friends);// ["zhangsan","lisi","zhaoliu"]; console.log(person2.friends);// ["zhangsan","lisi"]; console.log(person1.friends === person2.friends);//false console.log(person1.sayName === person2.sayName);//true
动态原型模式将组合模式中分开使用的构造函数和原型对象都封装到了构造函数中,而后经过检查方法是否被建立,来决定是否初始化原型对象。
function Person(name,age,job){ // 属性 this.name = name; this.age = age; //方法 检查sayName 是否建立 if(typeof this.sayName != "function"){ Person.prototype.sayName = function(){ console.log(this.name); }; } } var friend = new Person("zhangsan",25); friend.sayName();//'zhangsan'