1 关于对象的
静态属性、私有属性、局部变量、原型变量 的使用与区别
1)局部变量:是指在函数中用 var 声明过的变量,在函数运行完之后内存被自动回收!2)私有属性:是指在函数中用 this 标记过的变量,在实例对象后一直被对象所独有!3)静态属性:是指函数(类)本身所独有的属性,他是不能被实例对象所调用的,这与java不同。4)原型变量:是指通过类或者对象本身的原型所所创建的变量,他能被所有的实例对象以及类本身原型所共享。具体如下:function Person( ){ this.name = 'zhangsan' ; // 私有属性 var value = 'wuyun' ; // 局部变量 this.process = function(){ // 使用方法 console.info(this.name + value ); // 如果局部变量和私有变量同名,那么会使用局部变量,所以使用this作为标记! } } var person = new Person( ); console.info( person.__proto__ == Person.prototype ); // true,说明二者可以通过原型相互共享属性 Person.age = 21 ; // Person的静态属性,person是无法调用的 person.age = 22 ; // person的静态属性,Person是无法调用的 Person.prototype.sex = 'man' ; // 原型变量,二者共享 person.__proto__.height = 185 ; // 原型变量,二者共享
2 关于
prototype 和
__proto__ 的调用
1)prototype 是Function对象独有的属性,也就是说只有类才能够调用这个属性2)__proto__ 是实例对象独有的属性,也就是用new,或者{ } 声明过的变量才能调用3)以上二者如果是对应类的实例的话,那么他们引用的是同一个原型,所以 person.__proto__ == Person.prototype 为 true
3 关于
原型链继承(必不可少的
两步)
1)子类的原型指向父类的实例,SubClass.prototype = new SuperClass( );2)子类的原型构造函数指向子类本身,SubClass.prototype.constructor = SubClass ;3 ) 由于js使用原型继承导致,构造函数也别继承,然而面向对象来说这是错误的,所以才有第二步重新指向构造函数。4 ) 个人理解 Person 与 Person.prototype 的差异是:前者是指构造函数或类本身,后者其实是一个所有实例都共享的实例对象。5)如果单纯只是继承某一个函数那么可以使用 call 函数进行处理,而无需继承。 Class.method.call(Self , param) ;具体如下:function extends( SubClass , SuperClass){ /*第一步 : 构建桥梁类Bridge,他的作用就是完全替代父类本身,包括构造方法*/ var Bridge = function( ){ } ; Bridge.prototype = new SuperClass( ); // Bridge.prototype.constructor = SuperClass ;这一步原型链默认完成 /*第二步 : 使用子类的原型链继承桥梁父类*/ SubClass.prototype = new Bridge( ); SubClass.prototype.constructor = SubClass ; /*第三步 : 扩展子类属性,把父类的引用作为子类的共享属性,为子类中所调用 */ SubClass.prototype.superClass = SuperClass.prototype ; // 这里必须是prototype,而不能是函数本身 /*第四步 : 为保证程序正常运行机制,做个小判断*/ if( SuperClass.prototype.constructor == Object.prototype.constructor ){ SuperClass.prototype.constructor = SuperClass ; } }
6)javascript是单继承的,但如果想在一个类中拥有多个类的方法,那么就要使用聚合(掺元类,把其他类的方法为自己所用)。具体如下:function mixin(ReceivingClass,GivingClass){ for(var method in GivingClass.prototype ){ if(ReceivingClass.prototype[method] == undefined){ /* 这里特别注意使用prototype而不使用原因就是 静态属性 与 原型属性的差异 */ ReceivingClass.prototype[method]= GivingClass.prototype[method]; } } }
4 关于接口的三种模仿方式
1)注释式模仿接口(其实就是用注释语句把接口给解释,说白了就是通过规范文档来说明某给类就是一个接口),如下
/* 这里就是接口的注释声明文档,这个是核心 interface Composite { function add(child); function remove(child); function getChild(index); } interface FormItem { function save(); } */ var CompositeForm = function(id, method, action) { //自定义任意一个类,用来模拟实现接口 ... }; CompositeForm.prototype.add = function(child) { // 通过实现方法说明实现接口 ... };
2)属性检查式模仿接口(同样使用注释,但是它与上面的唯一不同点就是在方法的实现中,对该类有个判断是否实现了该接口,所谓判断,就是该类有一个属性用来保存他实现类的名称,所以称之为属性检查)
function addForm(formInstance) { if(!implements(formInstance, 'Composite', 'FormItem')) { throw new Error("Object does not implement a required interface."); } ... }
3)鸭式辨型式模仿接口(它采用的思想是说只要在实现类中实现了接口中的所有方法就等同于实现了接口,那么他的核心就是判断是否实现了接口中的方法,否则报错)
5 创建对象的模式(成员变量的保护)
1)门户大开型(使用 this.property 的方式定义成员变量,但是为了成员数据的安全性,我们使用接口实现,只能调用接口来访问成员变量,不过这必须自觉遵守,因为本身是可以直接通过instance.property 调研的)
function Book( id , name ){ this.setId(id); //使用接口方法对属性赋值,而不是直接赋值属性 this.setName(name); } Book.prototype = { // 通过字面量方式注册实现接口的方法,同时还使用检查规则,这样就避免了自己赋值属性导致的问题 setId:function(id){ this.checkId(id); // 这个才是核心 this.id = id ; } check:function(id){ // some code } ....... }
2)用户命名规范区别私有成员(这种方法其实和上面的方法是一模一样的,唯一的差异就在于对象的成员变量名称都用下划线开头,一些不希望外部调用的方法也使用下划线开头,说白了这就是约定俗成,并不是真正的达到目的)如下:
Book.prototype = { // 通过字面量方式注册实现接口的方法,同时还使用检查规则,这样就避免了自己赋值属性导致的问题 setId:function(id){ this._checkId(id); // 这个才是核心 this._id = id ; } _check:function(id){ // some code } ....... }
3)使用闭包函数实现真正的私有成员(这种做法就是充分借用函数作用域以及闭包原来达到私有成员变量的保护)
1)函数作用域:在javascript中只有函数作用域没有块级作用域,换句话说,如果A函数内部有个B函数,那么B函数是能够调用A函数的所有成员的。2)闭包:对以上的A B , 如果在B中使用了A的成员变量,不管B在哪里执行,是在A内部执行,或是在A外部的任意函数作用域中执行,B所执行的作用域还是A,也就是说不管A是否执行完,A都会为B保留一份执行空间的内存,那么B使用A的成员变量将会被保留。这就是说 在不同函数作用域中也能调用之前作用域的成员变量,为闭包!3)使用函数作用域和闭包的概念完成私有成员的保护:
var Book = function(newIsbn, newTitle, newAuthor) { // implements Publication // Private attributes. 私有成员变量 var isbn, title, author; // Private method. 私有方法 function checkIsbn(isbn) { ... } // Privileged methods. 公共方法 this.getIsbn = function() { return isbn; }; this.setIsbn = function(newIsbn) { if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.'); isbn = newIsbn; }; // Constructor code. 初始化 this.setIsbn(newIsbn); }; // Public, non-privileged methods. 公共共享方法 Book.prototype = { display: function() { ... } };
这里很值得注意的是:使用 var 声明的变量就是为私有的属性,因为没有使用this标记所以是无法再外部调用的,但是通过闭包达到私有变量的保护有个很大的缺点就是:公共方法(为了闭包)写在了类里面,从而导致每个对象实例化后都会保存这个方法的副本,那么导致占了大量内存,如果把方法写在prototype中,即成为公共共享的方法,只保存一份副本在内存,但是在公共共享的方法中只能使用this来调用变量,而不能调用用var声明的变量。所以这就是这种方法的弊端!而且还有一个弊端就是:继承该类之后的子类是不能访问到该类的私有成员的,因为只能使用闭包访问了!!!
6 关于属性和方法的相关概念
1)实例公共属性: this.name2)实例私有属性: person.name(单个实例);var name ( 所有实例 )3)类公共属性:Person.name ;4 ) 静态属性:Person.prototype.name ; person.__proto__.name ;5)使用闭包重新定义属性的概念:
var Person = ( function( ){ var name = ''; //类的静态私有属性 this.sex = ''; // 类的静态共有属性 var ocr = function( ){ var age = ''; //实例私有属性 this.height = ''; //实例共享属性 } ocr.getName = function( ){ //定义类的方法 return name ; } return ocr ; //返回的是类本身 } )( ) alert(Person.getName( ));
这里说说执行流程:1)使用小括号就是为了让他自动执行,这里直接使用Person,实际上已经把Person定义的function执行完成了,等价于没有小括号时的,Person();
2)返回一个function,实际上就是闭包的原理,也可以看成就是返回一个类,是这个类本身而不是类实例,所以我们定义方法是用类名定义类的属性,而不是定义实例属性,不然的话无法在外面调用。
7 常量(就是使用属性的定义以及闭包原理)
var Class = (function() { // Private static attributes. var constants = { UPPER_BOUND: 100, LOWER_BOUND: -100 } // Return the constructor. var ctor = function( ) { } ctor.getConstant=function(name) { return constants[name]; } return ctor; })( ); /* Usage. */ alert(Class.getConstant('UPPER_BOUND'));
8 函数的链式调用
1)所谓链式调用不过就是可以person.fn( ).process( ).property ;2)实现很简单,就是在每一个方法的返回值为当前实例,即 this ;3)关于Jquery 的链式调用,如下:
//为了类(Function)扩展函数,我们顶一个他的静态函数 Function.prototype.method = function(name,fn){ // Function.prototype是为所有的Function对象增加属性,原因是继承 this.prototype[name] = fn; // 这里的this是指函数类本身,所以要用prototype,这样才能使属性共享 return this; // 返回this,达到链式调用 }; (function(){ function _$(els){ }; // 声明一个类 _$.onready = function(obj,fn){ if(obj){ // 如果对象存在,把$对象绑定在对象身上 obj.$ = function(){ return new _$(arguments); } }else{ // 对象(_$)注册到window上 window.$ = function(){ return new _$(arguments); } } fn(); } _$.method("addEvent",function(type,fn){ //注册方法 fn(); }).method("getEvent",function(fn,e){ //注册方法 fn(); }); _$.onready(com,function(){ com.$("div01").addEvent("click",function(){ alert("click Event"); com.$(this).getEvent(function(){ alert("click getEvent"); }) }) }) )()