符合中小企业对网站设计、功能常规化式的企业展示型网站建设
本套餐主要针对企业品牌型网站、中高端设计、前端互动体验...
商城网站建设因基本功能的需求不同费用上面也有很大的差别...
手机微信网站开发、微信官网、微信商城网站...
1、JS封装就是尽量把使用的方式简单化,内部逻辑和使用解耦。通俗的说就是使用的时候只需要知道参数和返回值,其他条件尽量不要使用人员进行设置。
创新互联专业为企业提供会同网站建设、会同做网站、会同网站设计、会同网站制作等企业网站建设、网页设计与制作、会同企业网站模板建站服务,10年会同做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
2、JS封装的方法有函数方式、对象的方式、闭包的方式。
举例
1)函数方式
function kk(a,b){
内部对a,b怎么处理就不需要关心了
}
2)对象方式
function kk(a,b){
this.x = a;
this.y = b;
}
var k = new kk(1,2);//通过面向对象的方式
alert(k.x);
3)闭包方式
function kk(a,b){
var k = 1;
return function tt(){
k++;
}
}
var u = kk(1,2);
u();//闭包实现累加
u();//闭包实现累加
js实现不了继承的..所谓的继承都是模拟的~
封装也就是用js模拟出类的写法..因为js有些特殊..所有的function都是对象~
所以可以
var A = function() {
this.a1 = "a1";
this.a1show = function() {
alert(this.a1);
};
}
var a_obj = new A();
a_obj.a1show();
a_obj.a1 = "new a1";
a_obj.a1show();
至于继承...js的继承方法有很多...有个比较好理解的是复制..从原本的"类"中获取对象..然后用for(each)做循环复制~~~ 因为每个js的对象可以被"索引"~
就是 alert(a_obj["a1"]); 也是正确的~
1. 定义js类
js并不是一种面向对向的语言, 没有提供对类的支持, 因此我们不能像在传统的语言里那样 用class来定义类, 但我们可以利用js的闭包封装机制来实现js类, 我们来封装一个简的Shape类.
代码如下:
function ShapeBase() {
this.show = function(){
alert("ShapeBase show");
};
this.init = function(){
alert("ShapeBase init");
};
}
这个类里定义了两个方法:show和init, 需要注意的是这里用到了this来声明, 而不是var, 因为用var是用来定义私有方法的.
另外, 我们还可以用prototype属性来定义Shape的方法.
代码如下:
ShapeBase.prototype.show=function()
{
alert("ShapeBase show");
}
ShapeBase.prototype.init=function()
{
alert("ShapeBase init");
}
上面这种写法看起来不太直观,我们可以将所有的方法写在一起.
代码如下:
ShapeBase.prototype={
show:function(){
alert("ShapeBase show");
},
init:function() {
alert("ShapeBase init");
}
};
现在, 类是写好了, 让我们写个js来测试下, 看看结果是不是跟我们想象的一样呢?
代码如下:
function test(src){
var s=new ShapeBase();
s.init();
s.show();
}
看到了吧, 其调用方式和C#一模一样, 而结果也如我们所料.
到目前为止, 我们学会了如何创建js的类了, 但还只是实例方法,要是实现跟C#中的静态方法要怎么做呢?
其实, 实现js的静态方法很简单, 看下面如何实现:
代码如下:
//静态方法
ShapeBase.StaticDraw = function()
{
alert("method draw is static");
}
2. 实现JS类抽象和继承
同样, js中也不支持类继承机制,但我们可以通过将父类prototype中的成员方法复制到子类的prototype中来实现.
和类的继承一样,JavaScript也没有任何机制用于支持抽象类.但利用JavaScript语言本身的性质.可以实现自己的抽象类.
首先来看看js中的虚方法, 在传统语言中虚方法是要先定义的, 而包含虚方法的类就是抽象类,不能被实例化,而在JavaScript中,虚方法就可以看作该类中没有定义的方法,但已经通过this指针使用了.
和传统面向对象不同的是,这里虚方法不需经过声明,而直接使用了, 并且类也可以被实例化.
先定义object的extend方法, 一个为静态方法,一个为实例方法, 这两个方法用于实现继承的prototype复制
代码如下:
Object.extend = function(destination, source) {
for (property in source) {
destination[property] = source[property];
}
return destination;
}
Object.prototype.extend = function(object) {
return Object.extend.apply(this, [this, object]);
}
接下来我们实现一个继承类Rect, 这里先用一种简单的方法来实现。
代码如下:
function Rect() { }
Rect.prototype = ShapeBase.prototype; //只这一句就行了
//扩充新的方法
Rect.prototype.add=function() {
alert("Rect add");
}
这种方法不能用于重写,如果改变了show方法, ShapeBase的show也会指向同一函数可能是由于prototype赋值只是简单的改变指向地址.
如果上面也定义了:
Rect.prototype.show=function() {
alert("Rect show");
}
那么执行结果如下:
function test(){
var s=new ShapeBase();
s.show(); //结果:Rect show
var r=new Rect();
r.show(); //结果:Rect show
r.add();
}
我们再使用object.extend实现继承, 并实现一个oninit虚方法, 修改ShapeBase如下:
代码如下:
ShapeBase.prototype={
show:function()
{
alert("ShapeBase show");
},
initialize:function () {
this.oninit();
}
};
实现Rect类继承.
代码如下:
Rect.prototype=(new ShapeBase).extend({
//添加新的方法
add:function() {
alert("Rect add");
},
//使用这种方法可以重写show方法
show:function() {
alert("Rect show");
},
//实现虚方法
oninit:function() {
alert("Rect oninit");
}
})
JavaScript创建对象的几种方式 潜意识里,JavaScript不能算是面向对象的语言,要算也只能说是趋向面向对象的一种语言,至少它不能很好的吻合面向对象最基本的三大特性(继承、封装、多态),当然有很多人就认为JavaScript是面向对象语言,好像也说得...
JS虽然是一个面向对象的语言,但是不是典型的面向对象语言。Java/C++的面向对象是object - class的关系,而JS是object - object的关系,中间通过原型prototype连接,父类和子类形成一条原型链。本文通过分析JS的对象的封装,再探讨正确实现继承的方式,然后讨论几个问题,最后再对ES6新引入的类class关键字作一个简单的说明。
JS的类其实是一个函数function,由于不是典型的OOP的类,因此也叫伪类。理解JS的类,需要对JS里的function有一个比较好的认识。首先,function本身就是一个object,可以当作函数的参数,也可以当作返回值,跟普通的object无异。然后function可以当作一个类来使用,例如要实现一个String类
1 var MyString = function(str){2 this.content = str;3 };4 5 var name = new MyString("hanMeimei");6 var addr = new MyString("China");7 console.log(name.content + " live in " + addr.content);
第一行声明了一个MyString的函数,得到一个MyString类,同时这个函数也是MyString的构造函数。第5行new一个对象,会去执行构造函数,this指向新产生的对象,第2行给这个对象添加一个content的属性,然后将新对象的地址赋值给name。第6行又去新建一object,注意这里的this指向了新的对象,因此新产生的content和前面是不一样的。
上面的代码在浏览器运行有一点问题,因为这段代码是在全局作用域下运行,定义的name变量也是全局的,因此实际上执行var name = new MyString("")等同于window.name = new MyString(""),由于name是window已经存在的一个变量,作为window.open的第二个参数,可用来跨域的时候传数据。但由于window.name不支持设置成自定义函数的实例,因此设置无效,还是保持默认值:值为"[object Object]"的String。解决办法是把代码的运行环境改成局部的,也就是说用一个function包起来:
(function(){ var name = new MyString("hanMeimei");
console.log(name.content); //正确,输出hanMeimei})();
所以从此处看到,代码用一个function包起来,不去污染全局作用域,还是挺有必要的。接下来,回到正题。
JS里的每一个function都有一个prototype属性,这个属性指向一个普通的object,即存放了这个object的地址。这个function new出来的每个实例都会被带上一个指针(通常为__proto__)指向prototype指向的那个object。其过程类似于:
var name = new MyString(); //产生一个对象,执行构造函数name.__proto__ = MyString.prototype; //添加一个__proto__属性,指向类的prototype(这行代码仅为说明)
如下图所示,name和addr的__proto__指向MyString的prototype对象:
可以看出在JS里,将类的方法放在function的prototype里面,它的每个实例都将获得类的方法。
现在为MyString添加一个toString的方法:
MyString.prototype.toString = function(){ return this.content;
};
MyString的prototype对象(object)将会添加一个新的属性。
这个时候实例name和addr就拥有了这个方法,调用这个方法:
console.log(name.toString()); //输出hanMeimeiconsole.log(name + " lives in " + addr); //“+”连接字符时,自动调用toString,输出hanMeimei lives in China
这样就实现了基本的封装——类的属性在构造函数里定义,如MyString的content;而类的方法在函数的prototype里添加,如MyString的toString方法。
这个时候,考虑一个基础的问题,为什么在原型上添加的方法就可以被类的对象引用到呢?因为JS首先会在该对象上查找该方法,如果没有找到就会去它的原型上查找。例如执行name.toString(),第一步name这个object本身没有toString(只有一个content属性),于是向name的原型对象查找,即__proto__指向的那个object,发现有toString这个属性,因此就找到了。
要是没有为MyString添加toString方法呢?由于MyString实际上是一个Function对象,上面定义MyString语法作用等效于:
//只是为了示例,应避免使用这种语法形式,因为会导致两次编译,影响效率
var MyString = new Function("str", "this.content = str");
通过比较MyString和Function的__proto__,可以从侧面看出MyString其实是Function的一个实例:
console.log(MyString.__proto__); //输出[Function: Empty]console.log(Function.__proto__); //输出[Function: Empty]
MyString的__proto__的指针,指向Function的prototype,通过浏览器的调试功能,可以看到,这个原型就是Object的原型,如下图所示:
因为Object是JS里面的根类,所有其它的类都继承于它,这个根类提供了toString、valueOf等6个方法。
因此,找到了Object原型的toString方法,查找完成并执行:
console.log(name.toString()); //输出{ content: 'hanMeimei' }
到这里可以看到,JS里的继承就是让function(如MyString)的原型的__proto__指向另一个function(如Object)的原型。基于此,写一个自定义的类UnicodeString继承于MyString
var UString = function(){ };
实现继承:
UString.prototype = MyString.prototype; //错误实现
注意上面的继承方法是错误的,这样只是简单的将UString的原型指向了MyString的原型,即UString和MyString使用了相同的原型,子类UString增删改原型的方法,MyString也会相应地变化,另外一个继承MyString如AsciiString的类也会相应地变化。依照上文分析,应该是让UString的原型里的的__proto__属性指向MyString的原型,而不是让UString的原型指向MyString。也就是说,得让UString有自己的独立的原型,在它的原型上添加一个指针指向父类的原型:
UString.prototype.__proto__ = MyString.prototype; //不是正确的实现
因为__proto__不是一个标准的语法,在有些浏览器上是不可见的,如果在Firefox上运行上面这段代码,Firefox会给出警告:
mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create
合理的做法应该是让prototype等于一个object,这个object的__proto__指向父类的原型,因此这个object须要是一个function的实例,而这个function的prototype指向父类的原型,所以得出以下实现:
1 Object.create = function(o){2 var F = function(){};3 F.prototype = o;4 return new F();5 };6 7 UString.prototype = Object.create(MyString.prototype);
代码第2行,定义一个临时的function,第3行让这个function的原型指向父类的原型,第4行返回一个实例,这个实例的__proto__就指向了父类的prototype,第7行再把这个实例赋值给子类的prototype。继承的实现到这里基本上就完成了。
但是还有一个小问题。正常的prototype里面会有一个constructor指向构造函数function本身,例如上面的MyString:
这个constructor的作用就在于,可在原型里面调用构造函数,例如给MyString类增加一个copy拷贝函数:
1 MyString.prototype.copy = function(){2 // return MyString(this.content); //这样实现有问题,下面再作分析3 return new this.constructor(this.content); //正确实现4 };5 6 var anotherName = name.copy();7 console.log(anotherName.toString()); //输出hanMeimei8 console.log(anotherName instanceof MyString); //输出true
问题就于:Object.create的那段代码里第7行,完全覆盖掉了UString的prototype,取代的是一个新的object,这个object的__proto__指向父类即MyString的原型,因此UString.prototype.constructor在查找的时候,UString.prototype没有constructor这个属性,于是向它指向的__proto__查找,找到了MyString的constructor,因此UString的constructor实际上是MyString的constuctor,如下所示,ustr2实际上是MyString的实例,而不是期望的UString。而不用constructor,直接使用名字进行调用(上面代码第2行)也会有这个问题。
var ustr = new UString();var ustr2 = ustr.copy();
console.log(ustr instanceof UString); //输出trueconsole.log(ustr2 instanceof UString); //输出falseconsole.log(ustr2 instanceof Mystring); //输出true
所以实现继承后需要加多一步操作,将子类UString原型里的constructor指回它自己:
UString.prototype.constructor = UString;
在执行copy函数里的this.constructor()时,实际上就是UString()。这时候再做instanseof判断就正常了:
console.log(ustr2 instanceof Ustring); //输出true
可以把相关操作封装成一个函数,方便复用。
基本的继承核心的地方到这里就结束了,接下来还有几个问题需要考虑。
第一个是子类构造函数里如何调用父类的构造函数,直接把父类的构造函数当作一个普通的函数用,同时传一个子类的this指针:
1 var UString = function(str){2 // MyString(str); //不正确的实现3 MyString.call(this, str);4 };5 6 var ustr = new UString("hanMeimei");7 console.log(ustr + ""); //输出hanMeimei
注意第3行传了一个this指针,在调用MyString的时候,这个this就指向了新产生的UString对象,如果直接使用第2行,那么执行的上下文是window,this将会指向window,this.content = str等价于window.content = str。
第二个问题是私有属性的实现,在最开始的构造函数里定义的变量,其实例是公有的,可以直接访问,如下:
var MyString = function(str){ this.content = str;
};var str = new MyString("hello");console.log(str.content); //直接访问,输出hello
但是典型的面向对象编程里,属性应该是私有的,操作属性应该通过类提供的方法/接口进行访问,这样才能达到封装的目的。在JS里面要实现私有,得借助function的作用域:
var MyString = function(str){ this.sayHi = function(){ return "hi " + str;
}
};var str = new MyString("hanMeimei");
console.log(str.sayHi()); //输出hi, hanMeimei
但是这样的一个问题是,必须将函数的定义放在构造函数里,而不是之前讨论的原型,导致每生成一个实例,就会给这个实例添加一个一模一样的函数,造成内存空间的浪费。所以这样的实现是内存为代价的。如果产生很多实例,内存空间会大幅增加,这个问题是不可忽略的,因此在JS里面实现属性私有不太现实,即使在ES6的class语法也没有实现。但是可以给类添加静态的私有成员变量,这个私有的变量为类的所有实例所共享,如下面的案例:
var Worker;
(function(){ var id = 1000;
Worker = function(){
id++;
};
Worker.prototype.getId = function(){ return id;
};
})();var worker1 = new Worker();
console.log(worker1.getId()); //输出1001var worker2 = new Worker();
console.log(worker2.getId()); //输出1002
上面的例子使用了类的静态变量,给每个worker产生唯一的id。同时这个id是不允许worker实例直接修改的。
第三个问题是虚函数,在JS里面讨论虚函数是没有太大的意义的。虚函数的一个很大的作用是实现运行时的动态,这个运行时的动态是根据子类的类型决定的,但是JS是一种弱类型的语言,子类的类型都是var,只要子类有相应的方法,就可以传参“多态”运行了。比强类型的语言如C++/Java作了很大的简化。
最后再简单说下ES6新引入的class关键字
1 //需要在strict模式运行 2 'use strict'; 3 class MyString{ 4 constructor(str){ 5 this.content = str; 6 } 7 toString(){ 8 return this.content; 9 }10 //添加了static静态函数关键字11 static concat(str1, str2){12 return str1 + str2;13 }14 }15 16 //extends继承关键字17 class UString extends MyString{18 constructor(str){19 //使用super调用父类的方法20 super(str);21 }22 }23 24 var str1 = new MyString("hello"),25 str2 = new MyString(" world");26 console.log(str1); //输出MyString {content: "hello"}27 console.log(str1.content); //输出hello28 console.log(str1.toString()); //输出hello29 console.log(MyString.concat(str1, str2));//输出hello world
3031 var ustr = new UString("ustring");
32 console.log(ustr); //输出MyString {content: "ustring"}33 console.log(ustr.toString()); //输出ustring
从输出的结果来看,新的class还是没有实现属性私有的功能,见第27行。并且从第26行看出,所谓的class其实就是编译器帮我们实现了上面复杂的过程,其本质是一样的,但是让代码变得更加简化明了。一个不同点是,多了static关键字,直接用类名调用类的函数。ES6的支持度还不高,最新的chrome和safari已经支持class,firefox的支持性还不太好。
最后,虽然一般的网页的JS很多都是小工程,看似不需要封装、继承这些技术,但是如果如果能够用面向对象的思想编写代码,不管工程大小,只要应用得当,甚至结合一些设计模式的思想,会让代码的可维护性和扩展性更高。所以平时可以尝试着这样写。
比如你要 封装alert("test");
你要封装如
function tanchu(T){
alert(T);
}
你以后再调用 alert()时
只要写 tanchu("test")