总结javascript面向对象编程7种对象模型

最近拿着《JavaScript高级程序设计 第3版》巩固了基础,这里做下总结笔记

##1. 工厂模式

function createPerson(name,age,job){
 5     var o = new Object();
 6     o.name = name;
 7     o.age  = age;
 8     o.job = job;
 9     o.sayName = function(){
10         console.log(this.name);
11     };
12     // 等价于  o.sayName = new Function("console.log(this.name);");
13     return o;
14 }
15 var p1 = createPerson('lvyahui1',12,'devloper');
16 var p2 = createPerson('lvyahui2',23,'desigen');
17 p1.sayName();
18 p2.sayName();
19 console.log(p1.sayName === p2.sayName); // false

这种方法存在很多问题,比如多个对象的方法是独立的,没用共享。不能判断对象的类型

2.构造函数模式

function Person (name,age,job){
 5     this.name = name;
 6     this.age = age;
 7     this.job = job;
 8     this.sayName = function(){
 9         console.log(this.name);
10     };
11 }
12 var pp1 = new Person('lvyahui1',12,'dev');
13 var pp2 = new Person('lvyahui2',12,'desien');
14 pp1.sayName();
15 pp2.sayName();
16 console.log(pp1.constructor === Person);    //  true
17 console.log(pp2.constructor === Person);    //  true
18 console.log(pp1 instanceof Object);         //  true
19 console.log(pp2 instanceof Person);         //  true

这中方式解决了对象类型判断的问题,但却没有解决方法共享的问题,方法依然在每个对象里创建了一遍 。要解决这样的问题,可以像下面这样

1 function Home (name,age,job){
2     this.name = name;
3     this.age = age;
4     this.job = job;
5     this.sayAge = sayAge;
6 }
7 function sayAge(){
8     console.log(this.age);
9 }

但这样有带来了新的问题,在全局作用域定义的function只能在对象环境运行,违背了全局的概念;而且,当要定义的方法非常多的石猴。就要定义n多的全局函数,毫无封装性可言。

另外,需要注意的是, 构造函数也是函数,它与普通函数的唯一区别,就在于调用方式的不同 。如下面这样,我门可以拿它当普通函数用,这样属性和方法将被绑定到浏览器全局的执行环境window上,或者绑定到别的对象上运行,将属性方法绑定到别的对象

 1 // 构造函数也是函数
 2 //  1
 3 var person = new Person('hahha',23,'ddd');
 4 //  2 做普通函数
 5 Person('window',11,'2323dd');
 6 //window.sayName();
 7 
 8 //  3 在另一个对象的作用域中使用
 9 var o = new Object();
10 Person.call(o,'lvyahui',23,'2323'),
11 o.sayName();

3.原型模式

js中每一个函数都有一个特殊的属性-prototype(原型),这个属性是一个指针,指向一个对象。这个对象包含所有以这个函数为构造函数创建的对象共享的属性和方法 ,首先看下面的代码

1 /*
 2  * 原型模式
 3  */
 4 function Dog(){}
 5 Dog.prototype.name = 'a mi';
 6 Dog.prototype.age = 1;
 7 Dog.prototype.sayName = function(){
 8     console.log(this.name);
 9 }
10 
11 var d1 = new Dog(),
12     d2 = new Dog();
13 d1.sayName();
14 d2.sayName();
15 console.log(d1.sayName === d2.sayName);         //  true
16 console.log(Dog.prototype.isPrototypeOf(d1));   //  true
17 console.log(Object.getPrototypeOf(d1)); //返回 [[prototype]]的值
18 console.log(Object.getPrototypeOf(d1) === Dog.prototype);

这里将属性和方法都添加到了函数的原型对象中。在第4行的代码中我定义了Dog构造方法,这让Dog的原型对象的 constructor 属性指向了Dog。5-7行代码又为Dog的原型对象添加了2个属性和一个方法。

第11行、12行代码创建了两个对象实例, 这两个实例中都只包含了一个特殊的属性[[Prototype]],这个属性指向了构造函数的prototype,构造函数的prototype属性指向了原型对象,原型对象的constructor属性有指会了Dog构造方法

原型很重要的一个概念是属性搜索(方法我认为也是属性),从实例对象开始一直往原型链上游搜索,如果找到了就停止搜索,所以如过我们在实例对象中添加原型中已有的属性或方法,会屏蔽掉原型链中的属性或方法。 看下面的代码

1 d1.name = 'sai mao';    // 屏蔽原型中的属性
2 console.log(d1.name);
3 // 要恢复原型中的属性,必须显示删除实例中的属性
4 delete d1.name;
5 console.log(d1.name);

可以看到,可以使用delete恢复原型中的属性,而下面的方法可以用来检测一个属性是在实例对象中,还是在原型链的原型对象中。

1 // 检测一个属性是在实例中,还是在原型中
 2 d1.name = 'hhhh';
 3 console.log(d1.hasOwnProperty('name')); // 只有在给定的属性存在于对象实例中才返回true
 4 delete d1.name;
 5 console.log(d1.hasOwnProperty('name'));
 6 
 7 //单独使用in操作符,只要能在对象中找到属性则返回true
 8 d1.name = 'dsfdsfsd';
 9 console.log('name' in d1);  // true
10 console.log('name' in d2);  // ture
11 
12 //同时使用hasOwnProperty 和 in操作符,就能确定这个属性到底是在原型中还是在实例中
13 function hasPrototypeProperty(object,name){
14     return !object.hasOwnProperty(name) && name in object;
15 }
16 console.log('d1 hasPrototypeProperty :'+hasPrototypeProperty(d1, 'name'));
17 console.log('d2 hasPrototypeProperty :'+hasPrototypeProperty(d2, 'name'));

例外有一种更简单的原型写法

 1 // 更简单的原型语法
 2 function Cat(){}
 3 Cat.prototype = {
 4     name    :   'mimi',
 5     age     :   12,
 6     job     :   'doubi',
 7     sayName :   function(){
 8         console.log(this.name);
 9     }
10 };
11 
12 var cat = new Cat();
13 console.log(cat instanceof Object);
14 console.log(cat instanceof Cat);
15 console.log(cat.constructor === Cat);//false
16 console.log(cat.constructor === Object);//true

这种方式其实是以字面量的方法 重新创建了一个对象,然后赋值给了原型指针 。它丢弃了原来的原型对象,所以 很显然的原型对象的constructor属性不再指向Cat,而是指向了Obejct ,有多种方法可以修复构造函数,比如在定义字面量对象的时候,就显示制定constructor属性为Cat,也可以使用下面的方法。

 1 // 重设Cat的constructor属性
 2 Cat.prototype.constructor = Cat;
 3 //  但这样constructor变成可枚举的了
 4 var cat_keys = Object.keys(Cat.prototype);
 5 console.log(cat_keys);//[ 'name', 'age', 'job', 'sayName', 'constructor' ]
 6 console.log(Object.keys(cat));//[]
 7 
 8 // 重设constructor的属性
 9 Object.defineProperty(Cat.prototype,'constructor',{
10     enumerable:false,
11     value:Cat
12 });
13 cat_keys = Object.keys(Cat.prototype);
14 console.log(cat_keys);//[ 'name', 'age', 'job', 'sayName' ]

原型模式也不是没有问题,比如不好向构造函数传递参数。它 最大的问题是对引用类型的原型属性的共享问题 ,看下面的代码

1 // 原型模式最大的问题在于对引用类型的属性共享问题
 2 
 3 function House(){}
 4 
 5 House.prototype = {
 6     constructor:House,
 7     friends:['lvyahui','d']
 8 };
 9 
10 var h1 = new House(),
11     h2 = new House();
12 h1.friends.push('li');
13 console.log(h1.friends);//[ 'lvyahui', 'd', 'li' ]
14 console.log(h2.friends);//[ 'lvyahui', 'd', 'li' ]

4、构造函数与原型对象方式组合的模式

组合模式可以说吸取了构造函数模式与原型模式的优点,既保证了每个对象实例都有自己独立的属性和方法,同时有可以实现共享的属性和方法。

 /**
 2  * 常用方式,组合使用构造函数模式和原型模式
 3  */
 4 
 5 function Movie(name,length){
 6     this.name= name;
 7     this.length = length;
 8     this.links = ['h1','h2'];
 9 }
10 Movie.prototype = {
11     constructor:Movie,
12     sayName : function (){
13         console.log(this.name);
14     }
15 };
16 var m1 = new Movie('diany1',14),
17     m2 = new Movie('diany2',23);
18 m1.links.push('h3');
19 
20 console.log(m1.links);
21 console.log(m2.links);
22 console.log(m1.links === m2.links);
23 console.log(m1.sayName === m2.sayName);

这种方式集各家之长,我想这种方式应该是用的比较多的了吧(本人还未毕业,对企业里实际情况不太了解,有知道的可以悄悄告诉我)

当然,还有一种更好的写法,就是所谓的动态原型模式

5、动态原型模式

1 function Miss(name,age){
 2     this.name = name;
 3     this.age = age;
 4 
 5     if(typeof this.sayName != 'function'){
 6         Miss.prototype.sayName = function(){
 7             console.log(this.name);
 8         }
 9     }
10 }
11 
12 var miss = new Miss('lvyahui',12);
13 miss.sayName();

这种方式的在保持了组合模式的优点的前提下,让代码看起了封装性更好,也更安全。

6、寄生构造模式

这中方式与工厂模式,就只有一点区别,通过new 构造函数的形式创建对象,像下面这样,注意它只带创建对象的时候与工厂模式有区别(16行 new)

 /**
 2  * 寄生构造模式
 3  */
 4 
 5 function createPerson2(name,age,job){
 6     var o = new Object();
 7     o.name = name;
 8     o.age  = age;
 9     o.job = job;
10     o.sayName = function(){
11         console.log(this.name);
12     };
13     // 等价于  o.sayName = new Function("console.log(this.name);");
14     return o;
15 }
16 var p1 = new createPerson2('lvyahui1',12,'devloper');

7、稳妥构造函数模式

这种模式基于稳妥对象的概念,这种对象是没有公共属性,它的方法中也不使用this的对象。大家都知道js中的this一直都是让人头疼的问题。

稳妥模式与寄生模式类似,区别在于

  • 不通过new操作符调用构造函数
  • 不在行创建对象的实例方法中使用this
1 /**
 2  * 稳妥构造模式
 3  */
 4 function Girl(name,age){
 5     var o = new Object();
 6     o.sayName = function(){
 7         console.log(name);
 8     };
 9     o.sayAge = function(){
10         console.log(age);
11     };
12     return o;
13 }
14 var gril = Girl('d',21);
15 console.log(gril.sayName());
16 console.log(gril.sayAge());
17 //    输出
18 //    d
19 //    undefined
20 //    21
21 //    undefined
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值