咱们经常使用的两种编程模式javascript
POP--面向过程编程(Process-oriented programming)java
面向过程编程是以功能为中心来进行思考和组织的一种编程方法,它强调的是系统的数据被加工和处理的过程,在程序设计中主要以函数或者过程为程序的基本组织 方式,系统功能是由一组相关的过程和函数序列构成。面向过程强调的是功能(加工),数据仅仅做为输入和输出存在。这种过程化的思想是一种很朴素和广泛的思 想和方法,人类不少活动都是这种组织模式,好比工厂生产,企业服务等。面向过程以数据的加工处理过程为主线,忽略了过程的所属、边界和环境,混淆了服务功 能和自我功能(好比人能够砍树,这就是一种服务功能,有输入也有输出;它能够提供给外部,而行走,则是自我功能,没有输入也没有输出),外部环境和内部组 织,以及环境数据和原料性数据之间的区别。从思惟上来说,面向过程更强调细节,忽视了总体性和边界性,但这与现实世界有很大的出入,由于现实世界中,这种过程都不是孤立存在的,而是从属于某个对象,所以,面向过程虽然反映了现实世界的而一个方面(功能),但没法更加形象的模拟或者表示现实世界。好比以下这种写法:编程
function A(){ } function B(){ A(); }
感受互相之间独立存在的安全
OOP--面向对象编程(Object Oriented Programming)app
世界是由一个个对象组成的,所以面向对象的思惟方式更加接近现实世界,面向对象编程的组织方式也更加贴近现实世界。面向对象以对象为中心,将对象的内部组织与外部环境区分开来,将表征对象的内部属性数据与外部隔离开来,其行为与属性构成一个总体,而系统功能则表现为一系列对象之间的相互做用的序列,能更加 形象的模拟或表达现实世界。在编程组织中,对象的属性与方法再也不像面向过程那样分开存放,而是视为一个总体(程序的最终实现其实仍是分离的,但这仅仅是物 理实现上的,不影响将对象的这两个部分视为一个总体),所以具备更好的封装性和安全性(表征内部的属性数据须要经过对象的提供的方法来访问)。面向对象强 调的是总体性,所以面向对象与面向过程在不少方面是能够互补的。同时因为对象继承和多态技术的引入,使得面向对象具备更强、更简洁的对现实世界的表达能 力。从而加强了编程的组织性,重用性和灵活性。好比以下这种写法:ide
var obj={ default:{}, config:{}, init:function(){ this.A(); this.B(); }, A:function(){ this.config.name=’A’; }, B:function(){ this.config.name=’B’; } }
这种看起来就有点OO的感受了,把属性和方法封装在一个对象里面。面向对象的开发模式是逐渐流行起来,且被开发者们普遍推广的模式。函数
Javascript是一门基于对象的语言,但它不是一种真正的面向对象编程(OOP)语言,对象的属性都是以键值对的形式存在的,就是平时咱们所说的数据字典。把对象的属性和方法封装在一个对象里面,通常经过四种方式:原始对象,构造函数,原型模式,构造函数和原型混合模式。我我的认为面向对象的写法让系统更具备可维护性,可扩展性,可重用性,还有可配置性,功能模块也让人感受一目了然。性能
下面来说一下关于javascript对应的OOP的封装,继承,多态三大特性学习
(1)对象字面量或者实例化对象模式this
var Obj = { Id: '', Width: '', Height: '', init: function() { }, eventsBind: function() { }, renderView: function() { } }
或者
Var obj=new Object();
Obj.id=’’;….
Obj. renderView….
这种封装方式比较简单, 最经常使用的一种模式,简洁明了,适用于简单的封装.这两种写法,推荐字面量的方式。
(2) 构造函数模式
function MyPlugin(name, pwd) { this.name = name; this.pwd = pwd; this.Init = function() { }; this.renderView = function() { }; this.bindEvent = function() { } }
这种方式和C#的构造函数方式相似,每次实例化,全部的元素和方法都被从新建立,从新分配内存,互不影响 ,缺点在于实例化时公有的方法指向不一样的地址,形成没必要要的浪费,性能欠佳 ,方法应该共享才对,下面的混合模式会讲到。
(3)原型prototype
var MyPlugin = function(){ } MyPlugin.prototype = { obj: { name: ’aaaaa’ }, Init: function() { }, renderView: function() { }, bindEvent: function() { } }
这种方式的特色是全部的在原型链上的方法都是共享的,而且指向同一个地址。这里须要注意,若是原型链上面一个属性对应的是object对象会有一个问题,就是在一个实例对象里面给这个对象的属性赋值会影响另外一个实例对象.
var a=new MyPlugin();
var b= new MyPlugin();
a.obj.name=’bbbbbbbb’
这样在b中的obj对象的name值会被改变。缘由是这里的obj是引用类型,a.obj和b.obj指向的是同一个地址,若是是值类型则不会存在这样的问题
(4)构造函数和原型混合模式
这种模式的意义在于实例化的时候保持属性的相互独立,只共享方法.在作封装的时候推荐使用这种方式.刚才咱们所说的构造函数的缺点和原型模式的缺点在这里获得改善。
var MyPlugin = function(name) { this.name = name; } MyPlugin.prototype = { Show: function(){ Console.log(this.name); } }
(5) 其余写法
var MyPlugin = function(config) { var fn1 = function() { } var fn2 = function() { } return { test1: fn1, test2: fn2 } }
或者
var MyPlugin = function(config) { var obj = new Object(); obj.fn1 = function() { } obj.fn2 = function() { } return obj; }
刚才咱们提到构造函数模式实例化以后对象的方法地址指向不同,而原型模式,地址指向一致的说法。
咱们来看一下:
var funcDemo=function (name) { } funcDemo.prototype.init=function(){ } var a=new funcDemo('aaa'); var b=new funcDemo('bbb'); console.log(a.init===b.init);
输出结果是true.
再来看看构造函数:
var funcDemo=function (name) { this. Init=function(){ } } var a=new funcDemo('aaa'); var b=new funcDemo('bbb'); console.log(a.init===b.init);
输出结果是false.
1.call ,apply
var funcA = function() { this.show = function() { console.log('funcA'); } } var funcB = function() { funcA.call(this); } var b = new funcB(); b.show();
在这里 funcA.call(this);这句至关因而在funB的内部里面执行了
this.show=function(){
console.log('funcA');
}
而当前的做用域在funcB内部,this指向的是funB的实例化对象,也就是把show方法赋值给了funcB的实例化对象
有一点须要注意,若是是直接执行funcB(),那么当前做用域就是window了,至关于把show方法赋值给了window。等同于
window.show==function(){ console.log('funcA'); }
2.原型继承
var funcA = function() { this.show = function() { console.log('funcA'); } } var funcB = function() { } funcB.prototype = new funcA(); var b = new funcB(); b.show();
这一句 funcB.prototype=new funcA();
至关于把funB的原型指向了funcA的实例
等同于
funcB. Prototype={ Show:function(){ console.log('funcA'); } }
咱们能够写一个函数来实现继承
var extend = function(fn, newfn) { var F = function () { }; F.prototype = fn.prototype; newfn.prototype = new F(); newfn.prototype.constructor = newfn; newfn.prototype.superClass =fn.prototype }
3.属性拷贝
咱们先实现属性拷贝的代码:
var DeepCopy = function(newobj, obj) { for(var prop in obj) { if(obj.hasOwnProperty(prop)) { var item = obj[prop]; if(Object.prototype.toString.call(item) == '[object Object]') { newobj[prop] = {}; arguments.callee(newobj[prop], item); } else if(Object.prototype.toString.call(item) == '[object Array]') { newobj[prop] = []; arguments.callee(newobj[prop], item); } else newobj[prop] = item; } } return newobj; }
而后将A对象里的属性赋给B对象 :
var A = { obj: { name: 'AAAA' }, Arr: [ { name: 'AAAA' }, { name: 'BBBB' } ] } var B = { name: ’BBBBBBBBBB’ } DeepCopy(B, A)
和其余语言同样 ,先定义一个基类,js不存在类的概念,这里我只是类比的说法.
var baseClass=function(){ this.init=function(){ this.test.apply(this,arguments); } }
这里执行init实际上是执行了子类的test方法
咱们再定义A类,B类
var A=function(){ this.test=function(){ alert('AAAAAAAAAA'); } } var B=function(){ this.test=function(){ alert('BBBBBBBBBBBBB'); } }
而后将A类,B类的原型指向baseClass的实例,就是咱们刚才说的原型继承
A.prototype=new baseClass(); B.prototype=new baseClass();
最后咱们实例化
var a=new A(); a.init(); var b=new B(); b.init();
分别输出'AAAAAAAAAA','BBBBBBBBBBBBB'
咱们所实现的js这种多态和强类型语言的多态感受有所不一样,显得不那么直观,由于强类型的语言大都是经过抽象类或者接口声明方法,而后经过子类实现,调用方法的时候实际上是实例化抽象类或者接口,指向子类对象的实例。而咱们这里的实现其实是经过父类声明方法调用子类对父类声明方法的实现。
Js 面向对象的这部分东西我也是大体的讲了一下,讲解的不恰当或者不完善的地方,还请详细指出。若是有兴趣交流学习,请看最上角,加入个人qq技术群207058575.