一、工厂模式->构造函数模式:解决对象类型识别问题
//工厂模式
function createPerson(name,age){
var o=new Object();
o.name=name;
o.age=age;
o.sayName=function(){
alert(this.name);
}
return o;
}
var p=createPerson("Alice",20);
console.log(p);
//构造函数模式
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
alert(this.name);
}
}
var p=new Person("Cindy",20);
console.log(p);
从两者输出的实例对象开看,原型链是不一样的。
工厂模式原型链:实例对象->Object构造函数的原型对象,p.constructor输出Object。
构造函数模式原型链:实例对象->Person构造函数的原型对象->Object构造函数的原型对象,p.constructor输出Person。
这点差异使得构造函数模式可以用来进行对象识别,就是说可以直到这个对象是什么类型对象。可以使用constructor和instanceof进行类型判断。
console.log(p.constructor==Person) //true
console.log(p instanceof Person) //true
二、构造函数模式->原型模式:解决创建多对象时代码复用问题
1、对象属性搜索机制:沿着原型链进行查找,找到就中止查找。首先搜索对象实例本身是否有该属性,没有则沿着原型链(__proto__指向的对象)去查找原型对象中是否有该属性,直到找到原型链的最后一个原型对象。
2、对象、构造函数与原型对象的关系:一个构造函数对应一个函数原型对象,构造函数创建出来的n个对象对应同一个构造函数原型对象(就是构造函数的原型对象)。
基于以上两点,要想实现n个对象共享属性或方法,就把这些属性和方法定义在原型对象上,可以接受不少内存空间。而且原型具有动态性,修改原型中的属性值,可以在所有对象得到反映。
但是单独使用原型模式弊端会很明显,函数定义在原型上回很合适,但是如果引用类型放在原型上,就会导致所有对象都共享这个引用类型。
三、构造函数模式和原型模式相结合:处理共有与独有的关系,是最优的模式
构造函数定义对象独有的属性,原型上定义对象共有的属性。
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayName=function(){
console.log(this.name);
}
var p1=new Person("Cindy",20);
var p2=new Person("Alice",25);
p1.sayName() //Cindy
p2.sayName() //Alice
四、ES6 class语法——构造函数模式和原型模式的语法糖
class Person1{
constructor(name,age){
this.name=name
this.age=age
}
country="China";
sayName=function(){
alert(this.name);
}
sayContry(){
alert(this.country);
}
}
var p=new Person1("Aclie",22)
对象p输出的结果是:
Person1 {country: "China", name: "Alice", age:20, sayName: ƒ}
age: 20
country: "China"
name: "Alice"
sayName: ƒ ()
[[Prototype]]: Object
constructor: class Person1
sayContry: ƒ sayContry()
[[Prototype]]: Object
constructor: ƒ Object()
...
1、构造器函数constructor()不是必须的,但是写个构造函数有助于通过传参,定义对象的独有属性。构造器中定义的属性时对象实例的属性。
2、class中通过表达式定义的属性,无论是基本类型(country)、引用类型还是函数(sayName),都是对象实例的属性。
3、class中使用函数声明定义的函数,才是构造函数原型上的方法。
五、动态原型模式——构造函数模式和原型模式封装在构造函数中
我觉得有了class语法糖之后,就不需要用这种方式强硬封装了。
function Person(name,age){
this.name=name;
this.age=age;
if(typeof this.sayName!=="function"){
Person.prototype.sayName=function(){
console.log(this.name);
}
}
}
六、构造函数模式延伸的两种特殊模式——特殊用途(扩展篇)
(一)寄生构造函数模式
构造函数不返回值,就是常规的构造函数模式。
构造函数返回值,就是寄生构造函数模式。函数调用上下文赋值给这个返回的对象,即this指向是这个新对象。
function Person(name,age){
var o=new Object();
o.name=name;
o.age=age;
o.sayName=function(){
alert(this.name);
}
return o;
}
var p=new Person("Alice",20);
console.log(p);
看到Person这个函数和工厂模式是一样的,但是调用时用new关键字调用,这种方式下,原型链也是,实例对象->Object构造函数的原型对象,因此无法用来识别对象类型,也没什么共享优势,那么它的用途是什么呢?
JavaScript有很多内置对象,比如Array,如果想给内置对象添加某些方法方便使用,就可以借助寄生构造器模式实现。
function SpecialArray(){
var arr=new Array();
arr.push(...arguments);
//添加一个特殊方法
arr.toPipedString=function(){
return this.join("|");
}
return arr;
}
console.log(colors.toPipedString()); //输出 red|blue|green
console.log(colors.length) //输出 3
colors.slice(0,2) //输出 ["red", "blue"]
可以看到,使用寄生构造器模式改造的Array对象,不仅有新增的方法,还可以使用Array对象原来的属性和方法,简直不要太nice。
(二)稳妥构造函数模式
这个模式适用于那种很特殊的this和new关键字都不给使用的环境。乍一看和工厂模式的代码几乎没有出入,除了不使用this关键字。和寄生构造器模式一样,无法用来识别对象类型,也没什么共享优势,最大的优势就是安全。
function createPerson(name,age){
var o=new Object();
o.name=name;
o.age=age;
o.sayName=function(){
alert(name);
//注意这里的差异,这个name在外层cretePerson的变量对象上,是可以沿着作用域链访问到的
}
return o;
}
var p=createPerson("Alice",20);
console.log(p);
p.sayName() //输出"Alice"
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!