JavaScript原型模式-理解对象
一:简述
当初学编程一看到什么什么模式就比较头晕,不过本文我们通过简单的示例代码来说一下js 对象这个话题 ,来看下如何理解这个原型模式。
二:理解对象
1.简单对象
js对象中没有java、C#等类的概念。但是在js中 一切皆对象嘛 我们可以这么写一个实例
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <title></title> 6 </head> 7 <body> 8 9 <script> 10 //创建一个对象 11 var person = new Object(); 12 //声明属性 13 person.name = 'hello'; 14 person.age = 29; 15 person.job = 'Software Engineer'; 16 //声明方法 17 person.sayName = function () { 18 alert(this.name); 19 } 20 </script> 21 </body> 22 </html>
是不是很像一个class。但这是早期js创建对象的写法,后来出现了对象字面量的写法 简化了上面的写法
2.对象字面量
我们来改进上面的写法
1 var person = { 2 //属性 3 name: 'hello', 4 age: 29, 5 job: 'Software Engineer', 6 //声明方法 7 sayName: function () { 8 alert(this.name); 9 } 10 }
是不是简化了很多,但是上面两种创建的对象如果创建多了会产生大量重复代码 ,所以也就出现了以下第三种模式(又是模式....)
3.工厂模式
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title></title> 6 </head> 7 <body> 8 9 <script> 10 //用函数来包装 11 function createPerson() { 12 var person = new Object(); 13 //声明属性 14 person.name = 'hello'; 15 person.age = 29; 16 person.job = 'Software Engineer'; 17 //声明方法 18 person.sayName = function () { 19 alert(this.name); 20 } 21 return person; 22 } 23 //调用 24 var person1 = createPerson('tom', 21, 'baidu'); 25 var person2 = createPerson('tony', 31, 'tencent'); 26 </script> 27 </body> 28 </html>
工厂就是把东西加工、包装成一个对象。 其实我们大多数的写法也就是这么个模式 。但是这样我们怎么知道这个对象的类型呢 ?随着js发展又出来一个新模式
4.构造函数模式
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title></title> 6 </head> 7 <body> 8 9 <script> 10 11 12 function Person(name,age,job) { 13 this.name = name; 14 this.age = age; 15 this.job = job; 16 this.sayName = function () { 17 alert(this.name); 18 } 19 } 20 21 var pserson1 = new Person('tom', 21, 'baidu'); 22 var pserson2= new Person('tony', 31, 'tencent'); 23 </script> 24 </body> 25 </html>
看着是不是跟对象字面量很像。这个模式跟工厂模式对比来看
a.没有显示的创建对象
b.直接将属性和方法赋值给了this对象
c.没有return
d.执行构造函数中的代码(在Person新对象中添加属性和方法)
console.info(person1.constructor == Person);//true console.info(person1.constructor == Person);//true
对象的constructor属性 是用来标识对象类型的 ,可以将他的实例标识为一种特定的类型
说说这个特殊的函数--构造函数 ,它跟函数的唯一区别就是调用的方式不同(有啥不同啊。。)
只要是能通过new操作符来调用的函数就可以作为构造函数。(没new的就是普通函数呗O(∩_∩)O)
//我是构造函数 var pserson1 = new Person('tom', 21, 'baidu'); //我是普通函数 person('tony', 31, 'tencent');
构造函数模式也有自己的问题。每个方法都要在每个实例上重新创建一遍。
构造函数的另一种定义:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <script> function Person(name,age,job) { this.name = name; this.age = age; this.job = job; //第一种写法 this.sayName = function () { alert(this.name); } //第二种写法 this.sayName = new Function("alert(this.name)") } </script> </body> </html>
这样就可以看出person1.sayName 不等于person2.sayName了 因为不同实例的同名函数是不相等的
但是我们可以这么写:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <script> function Person(name,age,job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName } function sayName() { alert(this.name); } </script> </body> </html>
但是这样虽然是函数指向相同了 但是方法多了 就没有封装性可言了 。
5.原型模式
我们每个函数都有一个prototype(原型)属性。它是构造函数中(就是new的函数)自动创建的对象实例的原型对象 (就是一new就有了)
好处:可以让所有对象实例共享它包含的属性和方法(可以直接添)如下
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <script> //声明一个空函数(首字母大写) function Person() { }
//添加属性和方法 Person.prototype.name = "tony"; Person.prototype.age=29; Person.prototype.job = 'baidu'; Person.prototype.sayName = function () { alert(this.name); } var person1 = new Person(); person1.sayName();//baidu var person2 = new Person(); person2.sayName();//baidu alert(person1.sayName == person2.sayName);//true </script> </body> </html>
因为新对象的属性和方法都是共享的,所以person1和person2都是访问的同一个sayName函数
a.理解原型对象:
上面说到只要创建一个函数就会自动创建一个prototype属性,这个属性指向的就是函数的原型对象
Person.prototype.constructor就是Person
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <script> //声明一个函数(首字母大写) function Person() { } Person.prototype.name = "tony"; Person.prototype.age=29; Person.prototype.job = 'baidu'; Person.prototype.sayName = function () { alert(this.name); } var person1 = new Person(); console.info(person1); </script> </body> </html>
chrome中打印输出结构:
虽然person1不包含任何属性和方法 ,但是我们可以使用person1.sayName() 这就是通过查找对象属性来实现的
person1就是实例 Person就是原型 当实例中的属性修改后
person1.name='1234' 不影响实例的属性 person2没修改 name还是tony
delete person1.name;
这个可以删除person1的实例属性 从而访问到原型中的值
(1)我们可以通过hasOwnProperty方法来判断本身是否有这个属性(就是判断自己有没有该属性 返回true、false)
1 <script> 2 //声明一个函数(首字母大写) 3 function Person() { 4 5 } 6 Person.prototype.name = "tony"; 7 Person.prototype.age=29; 8 Person.prototype.job = 'baidu'; 9 Person.prototype.sayName = function () { 10 alert(this.name); 11 } 12 var person1 = new Person(); 13 alert(person1.hasOwnProperty('name'));//false 14 person1.name = '123';//我自己重新赋值 有了该属性 15 alert(person1.hasOwnProperty('name'));//true 16 console.info(person1); 17 </script>
(2)通过 in操作符来判断(单独使用和在for循环中)
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title></title> 6 </head> 7 <body> 8 <script> 9 //声明一个函数(首字母大写) 10 function Person() { 11 12 } 13 Person.prototype.name = "tony"; 14 Person.prototype.age=29; 15 Person.prototype.job = 'baidu'; 16 Person.prototype.sayName = function () { 17 alert(this.name); 18 } 19 var person1 = new Person(); 20 alert(person1.hasOwnProperty('name'));//false 21 alert('name' in person1) //true 22 person1.name = '123';//我自己重新赋值 有了该属性 23 alert(person1.hasOwnProperty('name'));//true 24 alert('name' in person1) //true 25 26 </script> 27 </body> 28 </html>
可以看到in可以访问到原型中去查找也就返回了都是true 而hasOwnProperty只在本身查找
(3)我只想看是否在原型中的属性 使用hasPrototypeProperty (在原型中不在实例中的属性)
(4)像in的用法在ie等浏览器中是有bug的 所以es5提供了 Object.keys() 方法来获取所有枚举属性
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <script> //声明一个函数(首字母大写) function Person() { } Person.prototype.name = "tony"; Person.prototype.age=29; Person.prototype.job = 'baidu'; Person.prototype.sayName = function () { alert(this.name); } var keys = Object.keys(Person.prototype); alert(keys);//name,age,job,sayName var person1 = new Person(); person1.name = '123';//我自己重新赋值 有了该属性 var keys2 = Object.keys(person1.prototype); alert(keys2);//name </script> </body> </html>
上面原型写法完全可以用对象字面量的形势简写(注意字面量写法中的红字)
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <script> //声明一个函数(首字母大写) function Person() { } Person.prototype = {
constructor:Person,//修正构造函数重新指向自己 name: "tony", age: 29, job: 'baidu', sayName: function () { alert(this.name); } } //Person.prototype.name = "tony"; //Person.prototype.age=29; //Person.prototype.job = 'baidu'; //Person.prototype.sayName = function () { // alert(this.name); //} </script> </body> </html>
原型模式最大的问题是共享的本性导致的 。里面包含引用类型比如数组 也会跟着共享
所以原型模式一般不会单独出现
6.组合使用构造函数模式和原型模式
构造函数模式用来定义实例属性
原型模式用来定义方法和共享的属性
这种混合模式是目前es中使用最广泛和认同度最高的一种自定义类型的方法 这也是引用类型的一种默认方式
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <script> //声明一个函数(首字母大写) function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ["lili","tonk"]; } Person.prototype = { constructor: Person, sayName: function () { alert(this.name); } } var person1 = createPerson('tom', 21, 'baidu'); var person2 = createPerson('tony', 31, 'tencent'); person1.friends.push('van'); console.info(person1.friends);//lili、tonk、van console.info(person2.friends);//lili、tonk console.info(person1.friends == person2.friends);//false console.info(person1.sayName == person2.sayName);//true </script> </body> </html>