一:简述
当初学编程一看到什么什么模式就比较头晕,不过本文我们通过简单的示例代码来说一下 js 对象这个话题 ,来看下如何理解这个原型模式。
二:理解对象
1. 简单对象
js 对象中没有 java、C# 等类的概念。但是在 js 中 一切皆对象嘛 我们可以这么写一个实例
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9 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
- 21
- 22
是不是很像一个 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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9 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
- 27
- 28
工厂就是把东西加工、包装成一个对象。 其实我们大多数的写法也就是这么个模式 。但是这样我们怎么知道这个对象的类型呢 ?随着 js 发展又出来一个新模式
4. 构造函数模式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9 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
- 24
- 25
看着是不是跟对象字面量很像。这个模式跟工厂模式对比来看
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');
构造函数模式也有自己的问题。每个方法都要在每个实例上重新创建一遍。
构造函数的另一种定义:
- 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)")
- }
这样就可以看出 person1.sayName 不等于 person2.sayName 了 因为不同实例的同名函数是不相等的
但是我们可以这么写:
- function Person(name,age,job) {
- this.name = name;
- this.age = age;
- this.job = job;
- this.sayName = sayName
- }
- function sayName() {
- alert(this.name);
- }
但是这样虽然是函数指向相同了 但是方法多了 就没有封装性可言了 。
5. 原型模式
我们每个函数都有一个 prototype(原型)属性。它是构造函数中(就是 new 的函数)自动创建的对象实例的原型对象 (就是一 new 就有了)
好处:可以让所有对象实例共享它包含的属性和方法(可以直接添)如下
- //声明一个空函数(首字母大写)
- 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
因为新对象的属性和方法都是共享的,所以 person1 和 person2 都是访问的同一个 sayName 函数
a. 理解原型对象:
上面说到只要创建一个函数就会自动创建一个 prototype 属性,这个属性指向的就是函数的原型对象
Person.prototype.constructor 就是 Person
View Code
- //声明一个函数(首字母大写)
- 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);
chrome 中打印输出结构:
虽然 person1 不包含任何属性和方法 ,但是我们可以使用 person1.sayName() 这就是通过查找对象属性来实现的
person1 就是实例 Person 就是原型 当实例中的属性修改后
person1.name='1234' 不影响实例的属性 person2 没修改 name 还是 tony
- delete person1.name;
这个可以删除 person1 的实例属性 从而访问到原型中的值
(1)我们可以通过 hasOwnProperty 方法来判断本身是否有这个属性(就是判断自己有没有该属性 返回 true、false)
- 1 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
(2)通过 in 操作符来判断(单独使用和在 for 循环中)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8 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
- 27
- 28
可以看到 in 可以访问到原型中去查找也就返回了都是 true 而 hasOwnProperty 只在本身查找
(3)我只想看是否在原型中的属性 使用 hasPrototypeProperty (在原型中不在实例中的属性)
(4)像 in 的用法在 ie 等浏览器中是有 bug 的 所以 es5 提供了 Object.keys() 方法来获取所有枚举属性
- //声明一个函数(首字母大写)
- 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
上面原型写法完全可以用对象字面量的形势简写(注意字面量写法中的红字)
- //声明一个函数(首字母大写)
- 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);
- //}
原型模式最大的问题是共享的本性导致的 。里面包含引用类型比如数组 也会跟着共享
所以原型模式一般不会单独出现
6. 组合使用构造函数模式和原型模式
构造函数模式用来定义实例属性
原型模式用来定义方法和共享的属性
这种混合模式是目前 es 中使用最广泛和认同度最高的一种自定义类型的方法 这也是引用类型的一种默认方式
- //声明一个函数(首字母大写)
- 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
来源: http://www.cnblogs.com/DemoLee/p/6605550.html