视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
利用原型和闭包,完成组件方法
2020-11-27 20:31:10 责编:小采
文档

Java代码
var Player = (function(){
Player = function(){ //这只是个空壳
throw new Error("Can not instantiate a Player object.");
};
Player.MIN_EXTENDED_TIME = 1;
Player.MAX_EXTENDED_TIME = 3;
Player._player = false;
Player.getInstance = function(){
if(!Player._player){
alert("Init...");
Player._player = {
_name : name,
setName : function(name){
this._name = name;
},
toString : function(name){
return "Player: " + this._name;
}
};
}
return Player._player;
};
return Player; //把修缮完工的Player这个组件方法返回
})();

//var player = new Player(); //new Player()会抛出异常
var player1 = Player.getInstance();
var player2 = Player.getInstance();
player2.setName("RealPlayer");
alert(player2.toString()); //输出RealPlayer
终于要定义一个组件方法了,利用原型来实现。看看这样如何:
Java代码
function Player(name){
Player.MIN_EXTENDED_TIME = 1;
Player.MAX_EXTENDED_TIME = 3;
this._name = name;
};
Player.prototype.setName = function(name){
this._name = name;
};
Player.prototype.toString = function(){
return "Player: " + this._name;
};

var player = new Player("WindowsMediaPlayer");
alert(player.toString()); //输出WindowsMediaPlayer
player.setName("RealPlayer");
alert(player.toString()); //输出RealPlayer
alert(Player.MAX_EXTENDED_TIME);

恩,有封装、有常量、也有复写了Object的toString方法,至于继承之类的事情,咱们后面再说,初看看还不错。可是这样的组件方法定义不够优雅,也不够直观,方法都是放在的位置定义的,并没有和最开始的组件方法放置在一起,如果能像Java那样定义岂不更好?

对了,可以用闭包来实现。试试看吧:
Java代码
function Player(name){
Player.MIN_EXTENDED_TIME = 1;
Player.MAX_EXTENDED_TIME = 3;
this._name = name;
this.setName = function(name){
this._name = name;
};
this.toString = function(){
return "Player: " + this._name;
};
};

var player = new Player("WindowsMediaPlayer");
alert(player.toString()); //输出WindowsMediaPlayer
player.setName("RealPlayer");
alert(player.toString()); //输出RealPlayer
alert(Player.MAX_EXTENDED_TIME);

不像Groovy里面,闭包做了很大程度上的强化,包括新的语法的支持;JavaScript的闭包是很简单的闭包,它没有特殊的需要额外学习的语法,任意一个function,里面只要包含未绑定变量,这些变量是在function所属的上下文环境中定义的,那么,这个function就是闭包。顺便罗嗦一句,和闭包相反的,不正是不包含任何未绑定变量的函数式代码吗?
写是写好了,可是转念一想,Player应当只有一份,它是单例的,最好我也能像Java那样弄一个单例模式出来 :),可是事不遂愿,我没有办法在JavaScript做一个private的构造器,用这种思路去实现单例模式似乎不可行……
怎么办?

然而天无绝人之路,我控制不了你new一个Player的对象,我却可以控制你new出来的这个Player对象的属性和行为!当你需要使用你new出来的Player的对象的时候,你发现根本无法完成,或者它只是一个空壳!真正的东西还是要靠单例中经典的getInstance方法来获得:
Java代码
function Player(){
throw new Error("Can not instantiate a Player object.");
}; //这只是个空壳

(function(){ //这才是货真价实的东西
Player.MIN_EXTENDED_TIME = 1;
Player.MAX_EXTENDED_TIME = 3;
Player._player = false;
Player.getInstance = function(){
if(!Player._player){
alert("Init...");
Player._player = {
_name : name,
setName : function(name){
this._name = name;
},
toString : function(name){
return "Player: " + this._name;
}
};
}
return Player._player;
};
})();

//var player = new Player(); //new Player()会抛出异常
var player1 = Player.getInstance();
var player2 = Player.getInstance();
player2.setName("RealPlayer");
alert(player2.toString()); //输出RealPlayer

好,真不错,单例模式在JavaScript下也成功实施了——你要胆敢new Player();就会抛出一个异常,这样什么也得不到,只有用getInstance方法得到的对象才是真真正正的Player对象。上面的代码整个执行的结果,只弹出了一次"Init..."的对话框,说明真正的“构造器逻辑”只调用了一次。

都做到这份上了,依然有小小的遗憾,Player的定义依然被拆分成了两部分,一部分定义空壳,一部分是一个匿名函数来定义Player的常量和getInstance方法。这两部分就不能合二为一么?
能。只需要用到一个小小的匿名函数,如果耐心从头看到这里,也一定能理解:

到此,终于如释重负,深入理解JavaScript面向对象,用好原型和闭包这两把锋利的武器,才能写出优秀的前端代码来。有一些同事私下和我交流,后面我尽量贴简洁的代码,希望有面向对象基础和JavaScript基础的同事都能有所收获。

下载本文
显示全文
专题