为什么80%的码农都做不了架构师?>>>
前天就在看面象对象设计这一章了,可是总是迷迷糊糊,今天又看了一遍,又看了云课堂里面的关于js的视频,才是真的对创建对象有了一点理解;现在对这一节做一个总结。创建对象的方法在这本书中一共提到了工厂模式、构造函数模式、原型模式,组合使用构造函数模式和原型模式、动态原型模式,寄生构造函数模式、稳妥构造函数模式7种,其中以构造函数和原型模式为主,原型模式就是重中之重了吧,同时,我觉得也以原生模式最为烧脑壳子,我当时就是因为它一度不下看下去这本书,不过,当你静下心来,慢慢想的时候,事情就变得简单了,不多说,作总结。
工厂模式
- 优点:实现函数封装,可以解决大量重复相似实例的问题,减少代码;
- 缺点:无法解决对象识别的问题;
如:
function box(name,age){var o=new Object();o.name =name;o.age=age;o.sayName = function(){alert(this.name);}return o;
}
var box1=box("zhangsan",21);
var box2=box("zhangsan",21);alert(box1 intanceof Object);//true;
alert(box2 intanceof Object);//true;
上面代码返回值一样。所以没有解决对象识别的问题;
构造函数模式
构造函数在运行时会自动出现在执行环境中,也可以创建自定义的构造函数,从而定义自己定义的对象的属性和方法;
构造函数的特点:
- 没有显示的创建对象,
- 直接将属性和方法赋给了this对象;
- 没有return语句;
- 构造函数函数名首字母大写;
- 必须使用new操作符来创建新实例;
如:
function Box(name,age){this.name =name;//this代表的是当前作用域下的对象,在这里指Box;this.age=age;this.sayName = function(){alert(this.name);};
}
var box1=new Box("zhan
var box2=new Box("zhangsan",21);
创建自定义的构造函数意味着我们可以将它的实例标示为一种特定的类型,这也是构造函数优胜于工厂模式的地方;但是构造函数模式也有问题,就是每个方法都要在每个实例上创建一遍,也就是不同实例的同名函数是不等的,虽然值是一样的,但是引用地址不一样:如
function Box(name,age){this.name =name;//this代表的是当前作用域下的对象,在这里指Box;this.age=age;this.sayName = function(){alert(this.name);};
}
//function sayName(){alert(this.name);这是全局的sayName,this表示window;
}
var box1=new Box("zhangsan",21);
var box2=new Box("zhangsan",21);
alert(box1.sayName==box2.sayName)//false;
为了解决这个问题,我们可以把方法写到全局环境中,从而实现引用地址的一致性,但是这样的话,函数就没有实现封装了,哎!那么现在就该放大招,用原型吧。
原型模式
我们创建的每个函数都有一个 prototype 属性,它是一个指向一个对象的指针,而这个对象包含了对象实例共享的属性和方法,也称为通过构造函数创建的实例对象的原型对象,当然好处就是共享了;如:
function Box(){};//函数中不对属性进行定义;利用prototype属性对属性进行定义;Box.prototype.name ="zhangsan";//this代表的是当前作用域下的对象,在这里指Box;Box.prototype.age=21;Box.prototype.sayName = function(){alert(this.name);};var box1=new Box("zhangsan",21);
box1.sayName();//"zahngsan"
var box2=new Box("zhan
box2.sayName();//"zahngsan"
alert(box1.sayName==box2.sayName);//true;
alert(Box.prototype.isPrototypeOf(box1));//true;这个用来测试实例是否指向了构造函数的原型对象;
alert(Object.getPrototypeOf(box1)==Box.prototype);//true;这个方法用来返回对象的原型;
原型中的几个方法:
- hasOwnProperty():检测对象实例中是否含有该属性;如果有,返回true;可以用来判断属性是在实例还是在原型对象中;
- in操作符:检测是否含有可以访问的属性,包括实例属性和原型属性;
- hasPrototypeProperty(():来检测属性是否是在原型对象中;
- isPrototypeOf(): 测试实例是否指向了原型对象;
原型对象的缺点:
- 省略了传参,使得所有初始化值一样;
- 共享,使实例不具有独立性;
为了解决这个问题。采用组合“构造函数和原型”的方法,如:
构造函数和原型模式
function Box(name,age){this.name =name;//this代表的是当前作用域下的对象,在这里指Box;this.age=age;this.family=["哥哥","姐姐"];
}
Box.prototype ={constructor:Box,sayName:function(){alert(t}
}
var box1=new Box("zhangsan",21);
var box2=new Box("zhangsan",21);box1.family.push("弟弟");
alert(box1.family);//"哥哥","姐姐","弟弟"
alert(box2.family);//"哥哥","姐姐"
alert(box1.sayName==box2.sayName);//true;
alert(box1.family==box2.family);//false
可以看出这样可以实现实例的独立性;构造函数模式可以用来定义实例属性,而原型模式可以用来定义方法和共享的属性。结果,每个实例都有自己的一份实例属性的副本,但同时又共享这对方法的引用,最大限度地节约了内存。这是目前使用最广泛、认同度最高的一种创建自定义类型的方法。这是用来定义引用类型的一种默认模式。
由于原型和构造函数分离,不便于引用,这就有了动态原型模式实现构造函数和原型的封装;
function Box(name,age){this.name =name;//this代表的是当前作用域下的对象,在这里指Box;this.age=age;this.family=["哥哥","姐姐"];if(typeof this.sayName!="function"{Box.sayName=function(){alert(this.name);};}}
var box1=new Box("zhangsan",21);
box1.sayName();
这种方法特别使用,但要注意这种方法中不能使用字面量重写原型;
总的来说,原型模式和构造函数本就是武林高手,当它们组合在一起时,便天下无敌了。