javaScript闭包实现类与继承(非ES6)

首先我们都知道js的一个函数定义是这样的

function func(){ //声明一个普通的函数 //省略代码 } 

而没有名字的函数叫匿名函数,是长这样的

function(){ //声明一个匿名函数,一般这样声明方式是用于回调函数 //省略代码 } 

或者我们习惯用一个变量来保存匿名函数,那么这个变量成为函数本身

var func = function(){ //匿名函数保存到func变量内 //省略代码 } 

同样,在调用函数的时候,使用函数名或者变量名后面加上一个括号,像以下这样

func(); //调用函数 

再者,也可以在声明函数的时候直接调用,这种叫做即时执行函数,需要加上两个括号,像这样

(function(){ //即时执行函数 //省略代码 })(); 

当然,函数是可以有返回值的,如果是即时执行函数有返回值,那么效果会怎样?

var func = (function(){ //即时执行函数 var i = 1; return i; })(); 

这样写,func获取的就不只匿名函数,而是函数的返回结果,func = 1

----------------------------------------我是分割线-----------------------------------------

刚刚,我们的函数返回的是一个整数,但如果我们的函数返回的是另一个函数呢?就成了闭包,像这样:

var func = (function(){ //即时执行函数 var resultFunc = function(){ //要返回的函数 //省略代码 } return resultFunc; })(); 

以上这样写 func就是resultFunc函数,这样有什么用呢?看下面的代码,注意变量 i 的作用域

var func = (function(){ var i = 5; //声明了局部变量 var resultFunc = function(){ console.log(i); //返回的函数体内能访问变量 i //省略代码 } //作用域内能访问变量 i return resultFunc; })(); //函数体外能不能访问变量 i 

这样声明的 i 变量外部是不能访问的,是不是很像面向对象的私有成员变量?接下来我们试试内部方法

var func = (function(){ var i = 5; var privateFunc = function(){ //声明了局部变量 //省略代码 } var resultFunc = function(){ privateFunc(); //调用局部函数 //省略代码 } return resultFunc; })(); 

小结一下:实际情况下,我们可以尝试这样写:

var func = (function(){ var i = 5; var privateFunc = function(){ //声明了局部变量 console.log("执行了privateFunc局部函数"); } var resultFunc = function(j){ console.log("外部变量:"+j); console.log("内部变量:"+i); privateFunc(); //调用局部函数 //省略代码 } return resultFunc; })(); var test = new func(9); 
 
执行结果

这样看,其实 resultFunc 更像是一个构造函数,这个函数在我们new func()的时候,必须且只执行一次,并且这个函数可以接受外部参数的哦。

----------------------------------------我是分割线-----------------------------------------

好了,接下来我们可以通过prototype的方式为其添加一些公开函数,而这些函数都是可以访问局部变量及局部函数的:

var func = (function(){ var i = 5; var privateFunc = function(){ //声明了局部变量 console.log("执行了privateFunc局部函数"); } var resultFunc = function(j){ console.log("外部变量:"+j); console.log("内部变量:"+i); privateFunc(); //调用局部函数 //省略代码 } var _proto = resultFunc.prototype; //取出prototype变量 _proto.myName = "ken"; //prototype的变量 _proto.publicFunc = function(){ //prototype的方法 console.log("这个是公共的方法,还有我的名字是"+this.myName); } return resultFunc; })(); var test = new func(9); test.publicFunc(); console.log(test.myName); 
 
运行结果

在外部能通过"."的方式调用prototype的内容,prototype函数体内通过this.访问自身变量。
就这样,公共及私有成员方法都通过闭包实现出来。

----------------------------------------再次分割线-----------------------------------------

通过以上的,公共变量及方法都是保存在prototype内,那么其实如果想模拟面向对象的继承,只要把prototype拷贝就可以了,先整理一下父类的代码:

var Super = (function(){ function _super(){ console.log("Super constructor"); this.name = "Super"; } var _proto = _super.prototype; _proto.sayHi = function(){ console.log("hello ! my name is "+this.getMyName()); } _proto.getMyName = function(){ return this.name; } return _super; })(); var s = new Super(); s.sayHi(); 
 
父类被new的时候结果

以下是子类的继承方式

var child = (function(){ var extend = Super; //定义要继承的父类 function _child(){ //子类的构造函数 extend.call(this); //让父类内部的this替换成子类的this,执行函数 console.log("child constructor"); this.name="child"; //覆盖子类的name } var nullObj = function(){}; //这里建立一个空白对象 nullObj.prototype = extend.prototype; //空白对象的prototype指向父类的prototype _child.prototype = new nullObj(); //新建nullObj(实际上是复制一份)的prototype给_child _child.prototype.constructor = _child;//把_child的构造函数放回prototype里,因prototype刚刚已经被覆盖了 var _proto = _child.prototype; //取得prototype ///这里可以继续添加子类的方法 return _child; })(); 

注意:nullObj.prototype = extend.prototype; 这里nullObj.prototype是引用,不能直接修改nullObj.prototype内容,不然会影响父类的代码,只能通过new nullObj 复制给 _child.prototype

调用测试

var c = new child(); c.sayHi(); console.log(c.name); 
 
运行结果

可以看到这里首先是调用了父类的构造函数,再调用子类的构造函数,而后sayHi方法被子类继承过来,而name内容变成了子类的child字符串。

采用闭包的方式跟ES6的class有什么不一样?
● ES6的class不存在私有成员,内部通过this访问变量或函数,外部通过"."方式访问。
● 采用闭包方式可以拥有私有成员,公共成员跟ES6访问方式一样,访问私有成员因为作用域的关系,只要直接调用就好了。
● ES6的使用比闭包方式要简单,可以根据自己的情况选择使用。




链接:https://www.jianshu.com/p/77e5840fc514

猜你喜欢

转载自www.cnblogs.com/porter/p/12152455.html