javaScript-类的封装

本文介绍JavaScript中类封装的多种方式,从原始构造函数到原型继承,再到类方法集中定义及构造函数替代方法_init的使用,并最终形成一种简洁的类定义方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类 
实际上就是一个function 
同时也是这个类的构造方法 
可以使用new创建该类的实例 
new出的对象 有属性 有方法 

属性和方法性质不同 因此定义的方式也不同 
——实际上方法是一种特殊形式的属性 

属性 
每个实例单独所有 多个实例之间互不影响 
因此要在构造方法中初始化实例的属性 
——每次执行new时构造方法都会执行 会产生新的属性 
——在其中定义方法当然也会 但不推荐 耗内存 

方法 
所有实例共有 创建多个实例不会产生新的function 
因此要在类的prototype中定义实例的方法 
——prototype中的方法会被所有实例共有 
——在其中定义属性当然也会 但不推荐 除非你知道自己在做什么 

至于private 算了吧 
Javascript里没private 硬要实现的话虽然也能 但是有点不伦不类 
按照惯例 在属性或方法名前加一个下划线 声明为私有 
外面看见这个不要动 假装没看见就好了 

至于static 这个容易 
var Class = function() {}; 
Class.field = ""; 
Class.method = function() {}; 
没必要多说 

单例 
更不用费什么劲了 

var Singleton = { 
    field: "", 
    method: function() { 
    } 
}; 

一、类封装的演化过程

1、原始的基于构造函数的类模拟方式,这是JS语法默认支持的创建类的方式 

function ClassA(){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
this.f1 = function(){};
this.f2 = function(){};
this.f3 = function(){};
this.f4 = function(){};
}

2、基于原型的类模拟方式,也是JS语法默认支持的 

function ClassA(){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
}
ClassA.prototype.f1 = function(){};
ClassA.prototype.f2 = function(){};
ClassA.prototype.f3 = function(){};
ClassA.prototype.f4 = function(){};

3、简化ClassA.prototype的书写,目的只是为了省键盘 

function ClassA(){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
}
var _p = ClassA.prototype;  //p是prototype的首字母
_p.f1 = function(){};
_p.f2 = function(){};
_p.f3 = function(){};
_p.f4 = function(){};

4、松散的类方法定义集中到一个函数classImp中,隐藏变量_p 

function ClassA(){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
}
function classImp(){
var _p = ClassA.prototype;
_p.f1 = function(){};
_p.f2 = function(){};
_p.f3 = function(){};
_p.f4 = function(){};
}
classImp();

5、类具体实现函数classImp匿名化,取消不必要的全局变量 

function ClassA(){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
}
(function(){
var _p = ClassA.prototype;
_p.f1 = function(){};
_p.f2 = function(){};
_p.f3 = function(){};
_p.f4 = function(){};
})();

6、参数传递_p变量 

function ClassA(){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
}
(function(_p){
_p.f1 = function(){};
_p.f2 = function(){};
_p.f3 = function(){};
_p.f4 = function(){};
})(ClassA.prototype);

7、引入apply,取消_p变量 

function ClassA(){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
}
(function(){
this.f1 = function(){};
this.f2 = function(){};
this.f3 = function(){};
this.f4 = function(){};
}).apply(ClassA.prototype);

8、定义构造函数替代方法_init,封装属性声明代码 
_init方法名字来源自java class字节码文件中每个类的构造函数的的名字都是_init 
这一步里面实际的构造函数被架空,慢慢被演化为一个傀儡了:) 

function ClassA(){
this._init();
}
(function(){
this._init = function(){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
};
this.f1 = function(){};
this.f2 = function(){};
this.f3 = function(){};
this.f4 = function(){};
}).apply(ClassA.prototype);

9、构造函数改进,实现参数传递 
这样new一个类的时候,参数能够正常传递给_init这个模拟的构造函数了 

function ClassA(){
this._init.apply(this, arguments);
}
(function(){
this._init = function(p1, p2){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
};
this.f1 = function(){};
this.f2 = function(){};
this.f3 = function(){};
this.f4 = function(){};
}).apply(ClassA.prototype);

10、建立基础支持代码,类形式化定义最终稳定下来 

function _class(className, classImp){
var clazz = (function(){
return function(){
this._init.apply(this, arguments);
};
})();
window[className] = clazz;
classImp.apply(clazz.prototype);
}
//----下面两个函数是上面的代码改进后的形式
function __newClass(){
return function(){  //<--这就是那个傀儡!
this._init.apply(this, arguments);
};
}
function _class(className, classImp){
var clazz = __newClass();
window[className] = clazz;
classImp.apply(clazz.prototype);
}


_class("ClassA", function(){
this._init = function(p1, p2){
this.a = "a";
this.b = "a";
this.c = "a";
this.d = "a";
this.e = "a";
};
this.f1 = function(){};
this.f2 = function(){};
this.f3 = function(){};
this.f4 = function(){};
});


二、对上面的封装继续简化

1. 干掉了那个傀儡 并改用constructor 
因为那个傀儡实际上也没什么用 
而_init是Java的 而且是见不得人的 
constructor 才是Javascript的 
Javascript内置的所有的类的对象 都有constructor属性 
指向类的本身 详见本文末尾测试代码 

window.$Class = function(name, SRC) {
    var src = new SRC();
    window[name] = src.constructor;
    window[name].prototype = src;
};


定义类 
// define Class
$Class("Guy", function() {
    this.constructor = function(name) {
        this.name = name;
        this.friends = [];
    };
    this.makeFriendWith = function(guy) {
        this.friends.push(guy.name);
    };
});


2. 再省掉一步 用JSON的格式定义类 更直观 
或许这个可以叫做final版了 没办法再简单了 
window.$Class = function(name, src) {
    src.constructor.prototype = src;
    window[name] = src.constructor;
};


定义类 
$Class("Guy", {
    constructor: function(name) {
        this.name = name;
        this.friends = [];
    },
    makeFriendWith: function(guy) {
        this.friends.push(guy.name);
    }
});
测试 
适用于以上两种方式 
要用到JSON2 
Firefox已内置 其它浏览器未知 sb-ie肯定没有 
json2.js  
测试代码 
var minglq = new Guy("MingLQ");
var luoluo = new Guy("Luoluo");
minglq.makeFriendWith(luoluo);
luoluo.makeFriendWith(minglq);
var ____ = "--------------------------------";
alert([
    JSON.stringify(minglq),
    JSON.stringify(luoluo),
    ____,
    "minglq.friends !== luoluo.friends:\n    " + (minglq.friends !== luoluo.friends),
    "minglq.makeFriendWith === luoluo.makeFriendWith:\n    " + (minglq.makeFriendWith === luoluo.makeFriendWith),
    ____,
    "minglq instanceof Guy:\n    " + (minglq instanceof Guy),
    "luoluo.constructor === Guy:\n    " + (luoluo.constructor === Guy),
    ____,
    "Guy:\n" + Guy.toString()
].join("\n"));


测试结果 
{"name":"MingLQ","friends":["Luoluo"]}
{"name":"Luoluo","friends":["MingLQ"]}
--------------------------------
minglq.friends !== luoluo.friends:
    true
minglq.makeFriendWith === luoluo.makeFriendWith:
    true
--------------------------------
minglq instanceof Guy:
    true
luoluo.constructor === Guy:
    true
--------------------------------
Guy:
function (name) {
    this.name = name;
    this.friends = [];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值