再起航,我的学习笔记之JavaScript设计模式02

时间:2022-08-25 16:21:32
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧!

我们都知道JavaScript是面向对象的语言,但是JavaScript是弱类型语言,没有比如C#这些强类型语言那种通过class等关键字实现类的封装方式,不过我们可以通过一些特性模仿实现类型的功能。
首先我们创建一个类

var Students=function(id,name,age){
this.id=id;
this.name=name;
this.age=age;
}

当然也可以像我们之前讲的那样通过在类的原型上添加属性和方法,比如

Students.prototype={
getStuInfo:function(){

}
};

当然这两种方法不要混合使用
这样我们就把我们需要的属性和方法都封装在我们抽象的Student类里了,当我们使用功能方法的时候我们不能直接使用这个类,需要用new关键字来实例化创建新的对象。

var student=new Students(1,'张三',20);

这样我们通过student.name的方式得到张三这个人的姓名。
或许有人会问了你通过this也可以添加属性和方法通过prototype也能添加属性和方法,那么这两者有什么区别呢?
是这样的JavaScript是一种基于原型prototype的语言所以每创建一个对象时,它都有一个原型prototype用于指向其继承的属性、方法。
那么通过prototype继承的方法并不是对象自身的所以我们每次通过类创建一个新对象时原来的这些属性和方法不会再次创建。
而通过this添加的属性、方法是在当前对象上添加的,每次创建新对象this所指向的属性和方法都会得到相应的创建。
既然我们已经能创建类,那么类里面对属性方法的隐藏和暴露我们也能通过JavaScript的函数级作用域来模仿实现

var Students=function(id,name,age)
{
//私有属性
var hobby='学习';
//私有方法
function learningMethod(){

};
//特权方法
this.getName=function(){};
this.setName=function(){};
this.getAge=function(){};
this.setAge=function(){};
//对象公有属性
this.id=id;
//对象公有方法
this.replication=function(){};
//构造器
this.setName(name);
this.setAge(age);
};

正如我上面示例中一样,声明在函数内部的变量外界是访问不到的通过这个特性我们可以创建类的私有变量以及私有方法。
而通过this创建的属性和方法,在类创建时都会相应创建,因此我们可以看成是公有属性和公有方法。
还有部分通过this创建的方法,可以访问到类或对象自身的私有属性和私有方法,我们可以看做特权方法,特权方法也可以看做是类的构造器。
那么我们在类的外部通过点语法定义的属性和方法以及在外部通过prototype定义的属性和方法又有什么作用呢?
直接通过点语法定义的属性和方法,我们在新创建的对象中无法获取他们,但是可以通过类来使用,所以我们称他们为类的静态公有属性和类的静态公有方法。
而prototye添加的属性和方法,我们在类的实例对象中可以通过this访问所以我们依然称他们为类的公有属性,和类的公有方法。

Students.primary=true;//类的静态公有属性
Students.admissionTime=function(){
console.log("2017");
};//类的静态公有方法
Students.prototype={
//公有属性
middle:false,
//公有方法
resetTime:function(){}
}

通过new关键字创建的对象的实质是对新对象this的不断赋值,并将prototype所指向类的prototype所指向的对象,听上去比较绕口,因为prototype是一级一级来查找得到的所以我们只要理解为最后会找到这个对象原型的根就行了。
所以在类的构造函数外面通过点语法定义的属性和方法是不会添加到新创建的对象上去的而通过类的原型prototype定义的对象就可以直接在新对象里使用,因为新对象的prototype和类的prototype指向的是同一个对象
我们可以测试一下,我们先实例化我们之前的Students类,然后用log打印出来看看具体显示什么

var student=new Students(1,'张三',23);
console.log(student.hobby);//undefined
console.log(student.middle);//false
console.log(student.id);//1
console.log(student.primary);//undefined

我们可以看到类的私有属性和类的静态公有属性在新创建的student对象里是访问不到的,而类的公有属性在student对象中可以通过点语法访问的到。
如同我们之前提到的类的静态公有属性和方法可以通过类的自身访问。

console.log(Students.primary);//true
Students.admissionTime();//2017

一般我们将类的静态变量通过闭包来实现

var Students=(function(){
//静态私有变量
var hobby='学习';
var studentNum=0;
//静态私有方法
function learningMethod(name){

};
//创建类
function student(newId,newName,newAge){
//私有变量
var name,age;
//私有方法
function checkId(id){};
//特权方法
this.getName=function(){};
this.setName=function(){};
this.getAge=function(){};
this.setAge=function(){};
//公有属性
this.id=newId;
//公有方法
this.replication=function(){};
if(studentNum>10)
throw new Error('我们只招收10名学生。');
//构造器
this.setName(name);
this.setAge(age);
}
//构造原型
student.prototype={
//静态公有属性
middle:false,
//静态公有方法
resetTime:function(){}
};
return student;
})();

这里我们介绍一下闭包,闭包是指有权限访问另一个函数作用域中变量的函数,即在一个函数内部创建另一个函数。
我们将这个闭包作为创建对象的构造函数,这样它既是闭包又是可实例的对象函数。
但是这样写好像还是有点美中不足,在JavaScript中并没有像C#那样的智能提示,在创建对象的时候没有使用new关键字不会给出错误提示,现在我们看如果我们创建对象不给new会怎么样。
现在我们用最开始的那个例子做示例

var Students=function(id,name,age){
this.id=id;
this.name=name;
this.age=age;
}
var student=Students(1,'张三',17);

你看我们实例化了一个叫张三的学生现在我们用log打印出来看看

console.log(student);//undefined

实际上他会显示未定义,为什么会出现这种情况呢?
我们再来打印3个属性看看

console.log(window.id);//1
console.log(window.name);//张三
console.log(window.age);//17

我们会发现我们创建了一个Students对象,但是赋值的属性添加到了window上面与,这是因为我们没用new关键字来实例化,new关键字可以看作是对当前对象的this不停的赋值。
所以,这个函数在全局中执行了,而全局作用域的this指向的当前对象就是全局变量,所以添加到Studnets的属性被添加到了window上面。
那我们如何去避免这个问题呢?
我们可以用instanceof去判断实例的是否是当前的对象如果不是,我们重新创建这个对象

var Students=function(id,name,age){
if(this instanceof Students){
this.id=id;
this.name=name;
this.age=age;
}else{
return new Students(id,name,age);
}
}
var student=Students(1,'张三',17);

我们再来看一看打印出来的东西吧

console.log(student);//Students

现在我们就能直接把对象打印出来了。

好了以上就是本次分享的全部内容,本次示例参考自JavaScript设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。