第一种:创建0bject对象实例
创建自定义对象的最简单方法就是创建一个Object的实例,再为他添加属性和方法。
1 var people = new Object(); 2 people.name = "yewenxiang"; 3 people.age = 24; 4 people.sayName = function(){ 5 console.log(this.name); 6 }
还可以使用对象字面量语法创建一个对象,等价于上面的代码
1 var people = { 2 name:"yewenxiang", 3 age:24, 4 sayName:function(){ 5 console.log(this.name); 6 } 7 }
这个方法是new Object() 来创建一个people对象的实例,有一个很大的问题:
- 使用同一个接口创建了很多对象,会产生大量重复的代码。
第二种:工厂模式
为了解决上面问题发明了用函数来创建一个对象的方法:
1 function People(name,age){ 2 var o = new Object(); 3 o.name = name; 4 o.age = age; 5 o.sayName = function(){ 6 console.log(this.name); 7 } 8 return o; 9 } 10 var people1 = People("yewenxiang",24); 11 console.log(people1.name); 12 people1.sayName();
函数People()能接收两个参数,可以无限次的调用这个函数,每次都会返回一个对象,对象的基本属性为传入函数的参数,解决了创建多个相似对象导致代码臃肿的问题。但是不能识别创建的对象类型,创建的对象的构造函数全部是Object。
-
console.log(people1 instanceof Object); //true
第三种:构造函数模式
构造函数模式可以用来创建自定义类型的对象,原生的构造函数类型有Object Array String等等。
1 function People(name,age){ 2 this.name = name; 3 this.age = age; 4 this.sayName = function(){ 5 console.log(this.name); 6 } 7 } 8 var people1 = new People("yewenxiang",24); 9 var people2 = new People("xiangwang",23); 10 console.log(people1.name); 11 console.log(people1 instanceof People); //true 12 console.log(people1 instanceof Object); //true
这个例子中创建的对象实例既是Object对象的实例,也是自定义构造函数People 的实例。
构造函数缺点:
- 每个方法都要在实例上面重新创建一遍。也就是实例化对象的sayName属性中存的函数地址不同,虽然他们调用后的结果可能一样。
上面的构造函数可以这么定义
1 function People(name,age){ 2 this.name = name; 3 this.age = age; 4 this.sayName = new Function(){ 5 console.log(this.name); 6 } 7 }
看第四行代码。每次实例化一个对象,sayName属性都指向了一个不同的函数引用,也就是指向了由Function构造函数创建的不同实例。
1 function People(name,age){ 2 this.name = name; 3 this.age = age; 4 this.sayName = function(){ 5 console.log(this.name); 6 } 7 } 8 var people1 = new People("yewenxiang",24); 9 var people2 = new People("yewenxiang",23); 10 console.log(people1.sayName == people2.sayName); //false 11 console.log(people1.sayName() == people2.sayName()); //true
第四种:原型模式
我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向了原型对象。这个原型对象的作用是由自定义的构造函数所有实例化的对象共享的属性和方法.
1 function People(){ 2 } 3 People.prototype.name = "yewenxiang"; 4 People.prototype.age = "23"; 5 People.prototype.sayName = function(){ 6 console.log(this.name); 7 } 8 var people1 = new People(); 9 var people2 = new People(); 10 console.log(people1.sayName == people2.sayName); // true
- people1和people实例对象上没有sayName这个方法,就去原型上找,两个实例找的都是原型上存放的同一个sayName方法的引用,也就是地址,所以他们是相等的,不管是调用后的值,还是地址。
简单的原型写法:
用一个包含所有属性和方法的对象字面量来重写整个原型。
1 function People(){ 2 } 3 People.prototype = { 4 name:"yewenxiang", 5 age:24, 6 sayName:function(){ 7 console.log(this.name); 8 } 9 };
但是有个问题,因为是用对象字面量来重写了People.prototype对象,People.prototype.constructor 指向了Object这个构造函数,不再指向People构造函数,如下图虚线为变化前,实线为变化后。
1 function People(){ 2 } 3 People.prototype = { 4 name:"yewenxiang", 5 age:24, 6 sayName:function(){ 7 console.log(this.name); 8 } 9 } 10 var people1 = new People(); 11 console.log(people1.prototype); 12 console.log(People.prototype.constructor); //输出Object构造函数 13 console.log(people1 instanceof People); //true 14 console.log(people1 instanceof Object); //true
虽然可以使用instanceof 来检测对象的类型,但是无法使用people1.constructor 来确定对象了,因为所有实例都会使用People.prototype原型上的constructor属性,而这个属性指向Object构造函数。
instanceof 检测的原理:
- A instanceof B
- 这里A 和 B 是两个对象,沿着A的__proto__这条线找,同时沿着B的prototype这条线找,如果两条线能找到同一个对象,那么返回true,如果终点还未重合,则返回false
- 上面的 People.propotype 和people.__proto__的指向没有变都是指向 People.propotype这个原型,所以返回了true。
可以给People.prototype = {constructor:People}来解决这个问题,但是重设了constructor属性的值,导致他变成了可枚举的状态,原生的是不可枚举的,可以使用Object.defineProperty来定义这个属性
1 function People(){ 2 } 3 People.prototype = { 4 name:"yewenxiang", 5 age:24, 6 sayName:function(){ 7 console.log(this.name); 8 } 9 } 10 Object.defineProperty(People.prototype,"constructor",{ 11 enumerable:false, // 不可枚举 12 value:People 13 }); 14 var people1 = new People(); 15 console.log(people1.prototype); 16 console.log(people1.constructor); //输出People构造函数 17 console.log(people1 instanceof People); //true 18 console.log(people1 instanceof Object); //true
原型模式的问题:
- 省略了为构造函数传递初始化参数,结果所有的实例在默认情况下取得相同的属性值。
- 原型中所有的属性被很多实例共享,这种共享对函数很合适,对于引用类型值的属性来说会产生问题,如数组类型,其中一个实例化对象改变了这个属性的值,其他实例化对象上全部改变了。
第五种:组合使用构造函数模式和原型模式
使用的技巧:
- 构造函数上定义实例各自私有的属性。
- 原型上定义方法和共享的属性。
1 function People(name,age){ 2 this.name = name; 3 this.age = age; 4 } 5 People.prototype.sayName = function(){ 6 console.log(this.name); 7 } 8 var people1 = new People("yewenxiang",24);