我的學習筆記是根據我的學習情況來定期更新的,預計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
現在我們就能直接把對象打印出來了。
