封装性:
类型于其它的面向对象语言一样,用大括号来限定变量的作用域。这样就有了局部变量。如果声明在最外面,那就是全局变量。那么现在还少的是什么?就是成员变量。幸好js支持闭包特性,例如:
function Person(){
var name="张三";
var sex ="男";
return {
"getName":function(){
return name;
},
"getSex":function(){
return sex;
},
"setName":function(_name){
name=_name;
},
"setSex":function(_sex){
sex = _sex;
},
"aliasName":name;
};
}
var person = new Person();
alert(person.name);//undefined
alert(person.getName());//张三
person.setName("李国");
alert(person.getName());//李国
这样就有了私有成员变量。公有成员变量,就是可以直接访问的,像aliasName属性。这是很基础的。我们都知道,在像java语言里,有this关键字来代表对像本身。而恰恰js也有这个this.但是这个this和其它面向对向语言的this是有区别的,这里先不说区别在哪。
我们先利用this实现一下类:
function Person(){
var name="王五";
var sex ="男";
this.aliasName="123";
this.setName=function( _name){
name=_name;
};
this.getName=function(){
return name;
};
this.setSex=function( _sex){
sex=_sex;
};
this.getSex=function(){
return sex;
}
}
//测试
var person = new Person();
alert(person.name);//undefined
alert(person.getName());//张三
person.setName("李国");
alert(person.getName());//李国
person.aliasName="nnd";
alert(person.aliasName);
下面我们来看一下 person = new Person()执行过程:person=new Object()-->把Person类型的this绑定到this-->person有什么,person就是有什么。
我们想一下,这里也是利用的闭包特性。因person的相关方法,引用了外部变量,当person不被回收时,外部变量也不会被回收,但是只能通过
person的方法才能访问的到。被包住了。但是,这一种方式,要比第一个实现起来要灵活多了。这里就要说一下,this为什么和其它语言的this
有区别,想一下,js 的function 即可以以函数用也可以做类用,当一个function 含有this,但被直接调用了,像Person().这时js就会把相关的方法
属性给了window(this没有被指向一个对象,会默认指向window),执行过程:this-->window.这样的话,就有一定的凶险性了,因为这个类里的所有东西都是全局的了。比如:
Person();
alert(window.getName());
到现在为止,我们把对像的封闭性创建对象算是说完了。目前,我们看它像是一个类的,但还有一点就是每new Person(),不但属性产生副本,方法也会产生副本
属性产生副本,这个是应该的,但是方法产生副本就没有毕要了,对吧?js function类型的数据(切记),它提供了prototype这个属性,即原型。这么写
Person:
function Person(){
var name="王五";
var sex ="男";
this.aliasName="123";
this.self = Person;
this.self.prototype.setName=function( _name){
name=_name;
};
this.self.prototype.getName=function(){
return name;
};
this.self.prototype.setSex=function( _sex){
sex=_sex;
};
this.self.prototype.getSex=function(){
return sex;
}
}
创建的所有Person就会有一个方法副本了。为了证明正确性,可以分别:
var person = new Person();
var person1 = new Person();
alert(person.getName===person1.getName);
看一下效果就可以了。原型为什么能提供这个效果?
下面,就说一下原型prototype及其相关应用吧
原型是Js中非常重要的概念,每个函数(在Js里面函数也是对象)都有一个叫prototype即原型)的属性,不过在一般情况下它的值都是null,但它他有一项非常重要的功能就是所以实例都会共享它里面的属性和方法(这就是Js里面实现继承的基础)!
还是举例吧:
function auth(){
alert(this.name);
//此处一定要加this关键字
}
auth.prototype.name='shiran';//这句可以放到对象定义之后,但必须在被调用之前
new auth();//这里一定要用new
这里需要注意三点:
第一、name前面一定要加关键字this,不然的话就会得不到任何,因为如果你不加this,他就不会到原型中查找(加上this是属性它会在上下文查找,看看前面我讲的变量的查找那一章)。
第二、如果实例对象在对象中找不到属性或方法就会到对象的prototype中去查找,所以如果想在函数被调用的时候调用对象的属性或方法,就必须把调用语句放在prototype定义之后(在这里,就是new auth必须放到auth.prototype.name之后)!
第三、只有实例对象才会到原型中查找,因为对于原对象来说prototype是他的属性必需通过prototype才能访问(在这里,要用new auth()生成一个实例,而不能用auth)!
原型对于对象的实例来说是共享的,这既给程序带来方便,同时也会让人感到迷惑,出现好多让人意想不到的结果!
auth=function(){ };
auth.prototype={
name:[],
getNameLen:function(){
alert(this.name.length);
},
setName:function(n){
this.name.push(n);
}
}
var lwx=new auth();
lwx.setName('lwx');
lwx.getNameLen();
var shiran=new auth();
shiran.setName('shiran');
shiran.getNameLen();
第二次弹出的对话框显示name的长度已经是2,为什么呢?这就是原型的共享引起的,因为变量lwx和shiran都是auth对象,而且name属性是在auth对象的原型中定义的,所以lwx和shiran实例之间共享name这个属性!!可是这并不是我们想要看到的结果,因为我们希望,每个实例之间是相互隔离的。
这里我们可以把name属性从原型中去掉,放在auth对象的定义中即:
auth=function(){
this.name=[];//切记,一定要在前面加上this关键词
};
这样一来,每个auth的实例都会拥有自己的name属性!所以推荐大家,以后在定义对象的时候:把属性放到定义里,而把对象的方法放到原型里!
有了原型,也其实就有了类方法和类属性。完整的类有了,封装成员也有了。以后要说一下继承啦!模拟继承,我们需要熟悉js中function 的两个函数,它们是call及apply.下面,我们介绍一下它们。知道它们了,继承也就差不多了,写信demo就可以了。
JavaScript中有一个call和apply方法,其作用基本相同,但也有略微的区别。先来看看JS手册中对call的解释:
call 方法
调用一个对象的一个方法,以另一个对象替换当前对象。
call([thisObj[,arg1[, arg2[, [,.argN]]]]])
参数
thisObj
可选项。将被用作当前对象的对象。
arg1, arg2, , argN
可选项。将被传递方法参数序列。
说明
call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。说明白一点其实就是更改对象的内部指针,即改变对象的this指向的内容。这在面向对象的js编程过程中有时是很有用的。
引用网上一个代码段,运行后自然就明白其道理。
call函数和apply方法的第一个参数都是要传入给当前对象的对象,及函数内部的this。后面的参数都是传递给当前对象的参数。
运行如下代码
可见分别弹出了func和var。到这里就对call的每个参数的意义有所了解了。
对于apply和call两者在作用上是相同的,但两者在参数上有区别的。对于第一个参数意义都一样,但对第二个参数:apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。如
func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])
同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入。我们用call,写一个继承的例子,像:
function Animal(){
this.name=“gg”;
this.getName=function(){alert(this.name)};
};
function Person(){
Animal.call(this);//构造父类
this.aliasName="";
}
function init(){
var p = new Person();
p.getName();
}
再写一个apply函数的:
function Animal(name,sex){
this.name="gg";
this.getName=function(){alert(this.name+name+sex)};
};
function Person(name,sex){
Animal.apply(this,[name,sex]);//构造父类,参数是数组
this.aliasName="";
}
function init(){
var p = new Person("wsk",'man');
p.getName();
}
到这里,继承有了。
多态性:
多态性,对于js来讲更简单了,因为,它是一个弱类型的语言,可以对一个变量付任何类型的变量。虽然不完全符合,但也能凑合用了。
js 面向对象编程三大特征: 封装:把抽象出的属性和对属性的操作封装在一起,属性被保护在内部,程序的其它部分只 有通过被授权的操作(函数) ,才能对属性进行操作。 继承:
冒充方法
function Animal(name){ this.name=name; this.show=function(){ window.alert("父类:"+this.name); } } function Dog(name){ this.a=Animal; this.a(name);//这句不能少 this.show=function(){ //覆盖掉父类的方法了 window.alert("子类:"+this.name); } } var dog=new Dog("大黄"); dog.show(); //结果:子类:大黄 function Cat(name){ this.a=Animal; this.a(name); } var cat=new Cat("小红"); cat.show(); //
结果:父类:小红 注:可实现多重继承。 js 的重载和重写: js 不支持重载, 不可以通过参数的个数来确定调用哪个函数。 但是 js 本身支持可变参数, 因此可认为 js 天然支持重载。 重写:子类可以重新写函数来覆盖父类的同名函数。 多态: js 实际上是无态的, 是一种多态语言, 一个变量的类型是在运行的过程中由 js 引擎来决 定的,所以 js 天生就支持多态。
闭包
闭包是和 gc 相关联的。 闭包实际上是涉及到一个对象的属性,何时被 gc 处理的问题。 怎样才能对对象的属性形成一个闭包。
注意:以上文章转载于CSDN博主 wustzbq0713的博客–JavaScript的模块化:继承(原型)、封装(闭包)、多态