1、工厂模式
function CreatePerson(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.showName = function(){ alert(this.name); } return o; } var person1 = CreatePerson('王五','25','Coder');
可以按照上述方式批量生成包含所有必要信息的CreatePerson对象 并且可以反复调用
缺点是 无法解决对象识别的问题
2、构造函数模式
// 构造函数模式 function CreatePerson(name,age,job){ this.name = name; this.age = age; this.job = job; this.showName = function(){ alert(this.name); } } var person1 = new CreatePerson('王五','25','Coder'); var person2 = new CreatePerson('王五','25','Coder');
此时 有如下几个优点
- 没有工厂模式下的显式的创建对象
- 直接将属性和方法赋给了this对象
- 并且没有return语句等缺点
缺点:
- 必须用new操作符来创建一个新的实例
- 方法不公用
pesron1和pesron2是两个不同的实例 指向不同地址 所以不相等
console.log( person1 == person2); //false
但是两个对象都拥有constructor属性 并且该属性指向CreatePerson
1 console.log( person1.constructor == CreatePerson); //ture 2 console.log( person1.constructor == person2.constructor); //true
本来constructor属性是用来标识对象类型的,但是现在有更多的标识对象类型的方法 instanceof
1 console.log( person1 instanceof Object ); //true 2 console.log( person1 instanceof CreatePerson ); //true
方法不公用
1 console.log( person1.showName == person2.showName); //false
3、原型模式
每一个函数都有prototype原型属性 这个属性是一个指针 指向一个对象 可以用这个原型来实现方法的公用
1 // 原型模式 2 function CreatePerson(name,age,job){ 3 this.name = name; 4 this.age = age; 5 this.job = job; 6 } 7 8 CreatePerson.prototype.showName = function(){ 9 alert(this.name); 10 } 11 12 var person1 = new CreatePerson('王五','25','Coder'); 13 var person2 = new CreatePerson('王五','25','Coder');
此时的方法公用
1 console.log(person1.showName == person2.showName); //true
另外可以通过isPrototypeOf()的方法来确定对象之间是否存在联系
console.log(CreatePerson.prototype.isPrototypeOf(person1)); //true
Es5中新增了一个方法 Object.getPrototypeOf() 用来返回函数原型的值
console.log(Object.getPrototypeOf(person1) == CreatePerson.prototype); //true
注意: 当原型和函数中都有同样的方法时 首先函数先查找函数中有没有此方法 若没有再去查找原型上有无对应方法
function Test(){ this.showName = function(){ console.log(2); } } Test.prototype.showName = function(){ console.log(3); } var test = new Test(); test.showName(); //2
当给实例添加一个属性时,实例的属性会屏蔽原型对象中的保存的同名属性 并不会进行任何修改
function Test(){ } Test.prototype = { name : '张三' } var test1 = new Test(); var test2 = new Test(); test2.name = '李四'; console.log(test1.name); //张三 console.log(test2.name); //李四
可以通过delete操作符完全删除实例属性 不能直接删除实例
function Test(){ } Test.prototype = { name : '张三' } var test1 = new Test(); var test2 = new Test(); test2.name = '李四'; delete test2.name; console.log(test1.name); //张三 console.log(test2.name); //张三
可以使用hasOwnProperty()来判断某个属性是否存在于实例中还是原型中 实例中返回true
function Site(){ this.name = "CodePlayer"; this.url = "xxx"; } Site.prototype.aa = function(){ console.log(1111); } var test1 = new Site(); console.log(test1.hasOwnProperty("name")); //true 实例中有name和url console.log(test1.hasOwnProperty("url")); //true console.log(test1.hasOwnProperty("aa")); //false 原型上的aa
假如仅仅是想判断某个属性是否存在此对象中 可以用in操作符
function Site(){ this.name = "CodePlayer"; this.url = "xxx"; } Site.prototype.aa = function(){ console.log(1111); } var test1 = new Site(); console.log("name" in test1); //true console.log("url" in test1); //true console.log("aa" in test1); //true console.log("xx" in test1); //false
以下面代码举例:
新建一个名为Person的构造函数
function Person(name,sex){ this.name = name; this.sex = sex; } Person.prototype.showName = function(){ console.log(this.name); } Person.prototype.showSex = function(){ console.log(this.sex); } // 新建一个Person构造函数的实例 var person1 = new Person('张三','男'); person1.showName(); //张三 person1.showSex(); //男
再新建一个名为Worker的构造函数
通过call()或者apply()来实现继承父级的属性
常见的错误继承方法
function Worker(name,sex,job){ //构造函数伪装 将Worker构造函数中this传到Person中 //继承父级的属性 call来改变作用域 也可以使用apply // //第一种call 必须要将第二个参数全部列举出来 Person.call(this,name,sex); //第二种apply 参数为数组 // Person.apply(this,[name,sex]); this.job = job; } // 原型链 继承父级的原型方法 // 第一种 浅复制 指向同一地址 对Worker的方法会影响到父级Person 不推荐使用 Worker.prototype = Person.prototype; Worker.prototype.showJob = function(){ console.log(this.job); } var worker1 = new Worker('李四','女','UI设计师'); worker1.showName(); //李四 worker1.showSex(); //女 worker1.showJob(); //UI设计师
此时
// 问题出在父级也拥有子级的showJob方法 console.log(Person.prototype); //Object { //showJob: () //showName: () //showSex: () //__proto__: Object //}
这是由于浅复制
Worker.prototype = Person.prototype;
父级和子级指向同一对象的地址 对子级的修改同样会影响到父级 为了避免这样的情况得new一个新对象来开辟新的地址来存放对象
// 第二种 寄生构造函数模式 Worker.prototype = new Person(); // 或者 通过循环的方式 for( var i in Person.prototype){ Worker.prototype[i] = Person.prototype[i]; }
此时
console.log(Person.prototype);
没有showJob的方法
instanceof ...的实例
console.log(Person instanceof Object) //true console.log(person1 instanceof Object) //true console.log(person1 instanceof Person) //true console.log(person1 instanceof Worker) //false console.log(worker1 instanceof Person) //true