前言
在Es6之前,由于javascript沒有對(duì)類的支持,也就是說它并不具備如傳統(tǒng)后臺(tái)語言(比如java)擁有類的功能,所謂類就是用來描述事物中的屬性和行為的,類的特征是由成員組成的,而屬性對(duì)應(yīng)的就是類中的成員變量,而方法對(duì)應(yīng)的就是類中的成員方法,這是傳統(tǒng)oop語言的描述,然而在javascript中,雖沒有類的概念,但是它往往是通過構(gòu)造函數(shù)和原型對(duì)象來給對(duì)象模擬與類相似的功能,但是這些相似的功能并不一定表現(xiàn)的與類完全一致,其實(shí)創(chuàng)建構(gòu)造函數(shù)的過程,就是創(chuàng)建模板的過程,類一定程度上與此相似,創(chuàng)建多個(gè)共享的特定的屬性和方法,用于生成對(duì)象的餅干工具,主要目的是提高代碼的可復(fù)用性,也提高了代碼的性能,有時(shí)候,在我們無意間就已經(jīng)在使用了這些特性,什么構(gòu)造函數(shù),原型,個(gè)人覺得,初次理解起來很是抽象,也稀里糊涂的覺得實(shí)際開發(fā)中到底有什么卵用,也許后者在不涉及復(fù)雜的功能需求時(shí),平時(shí)用得不多,顯然Es6中已新增了類class的功能,越來越嚴(yán)格,越來越像后端語言,Es6,Es7,Es8新增的諸多方法也越來越強(qiáng)大,標(biāo)準(zhǔn)也越來越完善,但是我覺得理解構(gòu)造函數(shù)與原型對(duì)象是必須的,是js面向?qū)ο缶幊痰幕A(chǔ),今天就我的學(xué)習(xí)和使用跟大家分享一下學(xué)習(xí)心得
正文從這里開始~
什么是函數(shù)
先看下面一簡(jiǎn)易代碼
var funA = function(){
console.log("我是匿名函數(shù)保存在變量funA中");
}
var funB = [function(){
console.log("我是匿名函數(shù)保存在數(shù)組funB中");
}]
var funC = {
method:function(){
console.log("我是匿名函數(shù)保存在對(duì)象funC中");
}
}
// 函數(shù)的調(diào)用
funA(); // 普通函數(shù)的調(diào)用
funB[0](); // 函數(shù)存入數(shù)組中的調(diào)用
funC.method(); // 對(duì)象調(diào)用方法的使用
// 函數(shù)可以作為參數(shù)傳遞,也可以作為返回值返回
var funD = function(funParm){
return funParm;
}
var runFunParmPassedToFunD = funD(function(){
console.log("歡迎關(guān)注微信itclanCoder公眾號(hào)");
})
runFunParmPassedToFunD();
// 函數(shù)是對(duì)象,也就是說函數(shù)也擁有屬性
var FunE = function(){}
FunE.property = "隨筆川跡";
console.log(FunE.property);
// 證明函數(shù)式對(duì)象
console.log("funA的數(shù)據(jù)類型是",typeof funA);
console.log("funA具體所屬",Object.prototype.toString.call(funA));
console.log("funA是由Object的一個(gè)實(shí)例對(duì)象?",funA instanceof Object);
console.log("funA函數(shù)下面的構(gòu)造器是",funA.constructor);
console.log("funA函數(shù)是由Object構(gòu)造出來的?",funA.constructor == Object); // false
console.log("funA下面的原型",funA.prototype); // funA下面的原型
console.log("Object下的原型",Object.prototype); // Object對(duì)象下原型
console.log("funA原型下構(gòu)造器",funA.prototype.constructor);//function fun(){}
console.log("對(duì)象原型下的構(gòu)造器",Object.prototype.constructor);
控制臺(tái)輸出結(jié)果如下:
結(jié)論:
- 函數(shù)也是功能代碼塊,一個(gè)封閉區(qū)間短小的腳本,如果多次使用同一段代碼,就可以把它封裝成一個(gè)函數(shù),允許在代碼里隨調(diào)隨用,利用函數(shù)封裝來避免重復(fù)鍵入大量相同的內(nèi)容,不過函數(shù)的真正威力在于傳參的能力,可以把不同的數(shù)據(jù)傳遞給它們,使用這些數(shù)據(jù)去完成預(yù)定的操作
- 函數(shù)是一等公民,是對(duì)象,是值,可以存儲(chǔ)在一個(gè)變量,數(shù)組或者對(duì)象中
- 函數(shù)可以傳遞給函數(shù),并由函數(shù)返回,函數(shù)擁有屬性
- 函數(shù)總有返回值(換句話說就是有return語句,當(dāng)然構(gòu)造器函數(shù)除外,因?yàn)樗J(rèn)會(huì)返回構(gòu)造器函數(shù)調(diào)用,當(dāng)構(gòu)造函數(shù)的調(diào)用執(zhí)行時(shí),會(huì)顯示的返回返回)
什么是構(gòu)造函數(shù)
定義:構(gòu)造函數(shù)就是你用new關(guān)鍵字創(chuàng)建對(duì)象時(shí)調(diào)用的函數(shù)
作用(優(yōu)點(diǎn)):創(chuàng)建多個(gè)共享特定屬性和行為的對(duì)象,主要是用于生成對(duì)象的餅干模具
缺點(diǎn):當(dāng)實(shí)例化多個(gè)對(duì)象時(shí),會(huì)重復(fù)的創(chuàng)建對(duì)象,造成內(nèi)存空間的浪費(fèi),增大CPU的開銷,并沒有消除代碼的冗余,(如后面代碼所示,原型正好解決了此類問題)
// 聲明一構(gòu)造函數(shù),首字母大寫
function Animal(name,age){
// this == new Animal();new會(huì)自動(dòng)的創(chuàng)建this對(duì)象,且類型就是該構(gòu)造安徽省農(nóng)戶的類型,構(gòu)造函數(shù)不需要返回值,因?yàn)閚ew會(huì)顯示的返回,return的值就等于函數(shù)名+()的調(diào)用
this.name = name; // 自定義屬性
this.age = age; // 同上
this.fun = function(){ // 自定義方法
return this.name+" "+this.age+"歲了";
}
}
// 實(shí)例化對(duì)象
var animal1 = new Animal("cat",2);
var animal2 = new Animal("dog",3); console.log(animal1.name,animal1.age,animal2.name,animal2.age); // cat 2 dog 3
console.log(animal1.fun(),animal2.fun()); // cat 2歲了 dog 3歲了
console.log(animal1.hasOwnProperty("name"));
console.log(animal1.hasOwnProperty("age"));
console.log(animal1 instanceof Animal); // true,證明animal1是Animal1是Animal構(gòu)造函數(shù)創(chuàng)建出來的
console.log(animal2 instanceof Animal);
console.log(animal1.constructor === Animal); // true
console.log(animal2.constructor === Animal); // true
console.log(animal1.fun == animal2.fun); // false
示例代碼截圖如下
問題:同一個(gè)構(gòu)造函數(shù)創(chuàng)建出來不同的實(shí)例化對(duì)象,公用的方法不等同,也就是說,當(dāng)你new一個(gè)構(gòu)造器對(duì)象,上面的構(gòu)造函數(shù)就執(zhí)行一遍,每次都會(huì)新建一個(gè)function,會(huì)新開辟一個(gè)內(nèi)存空間,每次都是指向一個(gè)新的堆的對(duì)象 ,這樣占用內(nèi)存消耗非常的大,怎么解決這個(gè)問題
解決辦法1:將構(gòu)造函數(shù)里面自定義的方法拿出來,獨(dú)立放在構(gòu)造函數(shù)外如下示例代碼所示
// 聲明一構(gòu)造函數(shù),首字母大寫
function Animal(name,age){
this.name = name; // 自定義屬性
this.age = age; // 同上
this.fun = fun;
}
// 把構(gòu)造函數(shù)里面自定義的方法拿出來
function fun(){
return this.name+" "+this.age+"歲了";
}
// 實(shí)例化對(duì)象
var animal1 = new Animal("cat",2);
var animal2 = new Animal("dog",3);
console.log(animal1.fun === animal2.fun); // true
控制臺(tái)截圖如下所示
解決辦法2利用原型正好解決實(shí)例化多個(gè)對(duì)象時(shí),避免構(gòu)造函數(shù)內(nèi)的方法重復(fù)創(chuàng)建(如后面的示例代碼所示)
普通函數(shù)與構(gòu)造函數(shù)的區(qū)別
- 有new與無new的差別
- 寫法上,構(gòu)造函數(shù)首字母大寫(目的只是用于區(qū)分普通函數(shù)與構(gòu)造函數(shù),提醒你在創(chuàng)建實(shí)例化對(duì)象前加new操作符)
- 當(dāng)函數(shù)沒有被new調(diào)用時(shí),構(gòu)造函數(shù)中的this就能與全局this對(duì)象(即window)
示例代碼如下所示:
// 聲明函數(shù)
function Animal(name,age){
this.name = name;
this.age = age;
this.fun = function(){
return this.name+" "+this.age+"歲了";
}
//console.log(this); window
}
// 無new的情況
var animal1 = Animal("cat",2);
var animal2 = Animal("dog",3);
console.log(animal1 instanceof Animal); // false
console.log(animal2 instanceof Animal); // false
console.log(Object.prototype.toString.call(animal1));//[object Undefined]
console.log(Object.prototype.toString.call(animal2));
console.log(name,age); // dog 3
console.log(animal1.name,animal1.age); //報(bào)錯(cuò)
控制臺(tái)輸出結(jié)果
從上面的代碼中可以看出,當(dāng)一個(gè)函數(shù)無new關(guān)鍵字的調(diào)用時(shí),構(gòu)造函數(shù)中的this對(duì)象指向的是全局對(duì)象window,所以構(gòu)造函數(shù)式依靠new提供返回值,上面的類型檢測(cè),值為undefined,正是如此,沒有使用new,則為普通函數(shù),只不過是一個(gè)沒有返回值的語句函數(shù),對(duì)this賦值屬性和方法,相當(dāng)于在全局下添加屬性和方法,如果加了use strict,在嚴(yán)格模式下,還會(huì)報(bào),嚴(yán)格模式下,并沒有全局對(duì)象設(shè)置this,返回的是undefined
針對(duì)以上問題,如果想普通函數(shù)也具有構(gòu)造函數(shù)的功能,怎么做?如下代碼所示
// 聲明構(gòu)造函數(shù)
function Animal(name,age){
// 加一this條件判斷,用instanceof來檢查自己是否被new調(diào)用
if(this instanceof Animal){
this.name = name;
this.age = age;
this.fun = function(){
return this.name+" "+this.age+"歲了"; }
}else{
// 以new遞歸調(diào)用自己來為對(duì)象創(chuàng)建正確的實(shí)例,這樣做的目的是在不同的情況下表現(xiàn)出一致的行為,常常是為了保護(hù)那些忘記了使用new的情況
return new Animal(name,age);
}
}
// 無new的情況
var animal1 = new Animal("cat",2);
var animal2 = Animal("dog",3);
console.log(animal1 instanceof Animal); // true
console.log(animal2 instanceof Animal); // true
console.log(Object.prototype.toString.call(animal1));//[object object]
console.log(Object.prototype.toString.call(animal2));//[object object]
console.log(animal1.name,animal1.age);
console.log(animal2.name,animal2.age);
控制臺(tái)輸出結(jié)果如下
為何內(nèi)置構(gòu)造函數(shù)無new也能工作
示例代碼如下所示
var arr = Array; // 當(dāng)沒有參數(shù)時(shí),構(gòu)造函數(shù)后面的圓括號(hào)可以省略
var obj = Object({
name:"隨筆川跡",
sex:"boy",
fun:function(){
return this.name+" "+this.sex+" "+Object.prototype.toString.call(this);
}});
console.log(obj.fun());
截圖如下所示
原因:因?yàn)槟切﹥?nèi)置系統(tǒng)構(gòu)造函數(shù)(Array,Object,RegExp,Date,Error,String等)都被設(shè)計(jì)為作用域安全的構(gòu)造函數(shù),也就是說在整個(gè)全局范圍內(nèi)都是可見的,一個(gè)作用域安全的構(gòu)造函數(shù)無new也可以工作,并返回同樣類型的對(duì)象
原型對(duì)象
protype:
- 作用1:去改寫對(duì)象下面公用的方法或者屬性,讓公用方法或者屬性在內(nèi)存中存在一份(也就是更改構(gòu)造器函數(shù)底下屬性和方法,解決了不會(huì)重復(fù)創(chuàng)建構(gòu)造的過程,目的是提高性能),可以看作是對(duì)象的基類
- 作用二:在原有的對(duì)象基礎(chǔ)上上,通過prototype進(jìn)行額外的,封裝,拓展(如后面示例代碼)
- 原型是基于構(gòu)造函數(shù)的(也就是說原型是掛載在構(gòu)造函數(shù)下的,先有構(gòu)造函數(shù),才會(huì)有原型)
如下原型示例代碼
function ProtoFun(width,height){
this.width = width;
this.height = height;
this.method = function(){
return "我是構(gòu)造函數(shù)下自定義的方法"
}
}
// 構(gòu)造函數(shù).原型下添加屬性
ProtoFun.prototype.color = "red";
// 構(gòu)造函數(shù).原型下添加方法
ProtoFun.prototype.fun = function(){
return this.width+" "+this.height+" "+this.color;
}
// 上面兩個(gè)通??梢院喜⒊上旅嬉粋€(gè)
ProtoFun.prototype.init = {
color:"red",
fun:function(){
return this.width+" "+this.height+" "+this.color;
}
}
var elemObj1 = new ProtoFun(100,100);
var elemObj2 = new ProtoFun(200,200);
console.log(elemObj1.width,elemObj1.height); // 100 100
console.log(elemObj2.width,elemObj2.height); // 200 200
console.log(elemObj1.color,elemObj1.fun(),elemObj1.init.color,elemObj1.init.fun());
console.log(elemObj2.color,elemObj2.fun(),elemObj2.init.color,elemObj2.init.fun());
console.log(elemObj1.method===elemObj2.method); // false
console.log(elemObj1.fun === elemObj2.fun); // true
控制臺(tái)輸出結(jié)果如下
如下示例:
// 未用原型寫法,普通寫法求和
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = [2,4,6,8,10,12,14,16]
arr1.sum = function(){
var result = 0;
for(var i = 0;i<arr1.length;i++){ // 這里也可以換成this.length
result += this[i];
}
return result; // 返回結(jié)果
}
arr2.sum = function(){
var result = 0;
for(var i = 0;i<arr2.length;i++){
result += this[i];
}
return result; // 返回結(jié)果
}
console.log("數(shù)組arr1和為",arr1.sum()); // 55
console.log("數(shù)組arr2和為",arr2.sum()); // 72
控制臺(tái)截圖如下:
原型寫法
// 原型寫法
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = [2,4,6,8,10,12,14,16]
Array.prototype.sum = function(){
var result = 0;
for(var i = 0;i<this.length;i++){
result += this[i];
}
return result;
}
console.log("數(shù)組arr1的和為",arr1.sum()); // 數(shù)組arr1的和為55
console.log("數(shù)組arr2的和為",arr2.sum()); // 數(shù)組arr2的和為72
console.log(arr1.sum === arr1.sum); // true
//普通函數(shù)封裝寫法,也就是閉包寫法
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = [2,4,6,8,10,12,14,16]
function AddResult(arr){
arr.sum = function(){
var result = 0;
for(var i = 0;i<this.length;i++){ // 這里也可以換成this.length
result += this[i];
}
return result; // 返回結(jié)果
}
return arr.sum();
}
console.log("數(shù)組arr1和為",AddResult(arr1)); // 數(shù)組arr1和為55
console.log("數(shù)組arr2和為",AddResult(arr2)); // 數(shù)組arr2和為72
區(qū)分構(gòu)造函數(shù)自定義屬性與原型屬性
如下示例代碼所示:
function Person(name,publicNum){
this.name = name;
this.publicNum = publicNum;
this.userDefined = function(){
return "我是構(gòu)造函數(shù)自定義方法unerDefined"
}
}
Person.prototype.age = 25;
Person.prototype.init = {
city:"beijing",
job:"coder",
method:function(){
return "我是原型下的方法輸出"
}
}
// 定義鑒別原型屬性方法
function hasPrototypeProperty(object,variable){
return !object.hasOwnProperty(variable) && (variable in object);
}
var person = new Person("隨筆川跡","itclancoder");
console.log(person.name,person.publicNum,person.userDefined(),person.init.city,person.init.job,person.init.method());
console.log(hasPrototypeProperty(person,"name"));
console.log(person.hasOwnProperty("name"));
console.log(person.hasOwnProperty("age")); // hasOwnProperty只能檢測(cè)自定義屬性,false
console.log(hasPrototypeProperty(person,"age"));
console.log("age" in person); // true,in操作符既能檢測(cè)自定義屬性也能檢測(cè)出原型下的屬性
控制臺(tái)輸出結(jié)果如下:
使用對(duì)象字面量形式改寫原型對(duì)象會(huì)改變構(gòu)造函數(shù)的屬性,指向問題
function Person(name,job){
this.name = name;
this.job = job;
}
Person.prototype.init = {
name:"小川",
job:"碼男",
outName:function(){
return this.name;
},
outJob:function(){
return this.job;
}
}
var person = new Person("隨筆川跡","coder");
console.log(person.name,person.job);
console.log(person.init.outName(),person.init.outJob());
console.log(person.constructor === Person); // true
console.log(person instanceof Person); // true
控制臺(tái)輸出結(jié)果如下:
若將上面的代碼更改如下:
function Person(name,job){
this.name = name;
this.job = job;
}
// 使用對(duì)象字面量形式改寫原型對(duì)象
Person.prototype ={
name:"小川",
job:"碼男",
outName:function(){
return this.name;
},
outJob:function(){
return this.job;
}
}
var person = new Person("隨筆川跡","coder");
console.log(person.name,person.job);
console.log(person.outName(),person.outJob());
console.log(person.constructor === Person); // false
console.log(person.constructor === Object); // true
console.log(person instanceof Person); // true
控制臺(tái)輸出結(jié)果如下
正確寫法:當(dāng)一個(gè)函數(shù)被創(chuàng)建時(shí),它的prototype屬性也被創(chuàng)建,且該原型對(duì)象的constructor屬性指向該函數(shù),當(dāng)使用對(duì)象字面量形式改寫原型對(duì)象Person.prototype時(shí),則該constructor指向指向的是Object,為了避免這一點(diǎn),需要手動(dòng)的改寫原型對(duì)象手動(dòng)設(shè)置constructor屬性,更改如下:
function Person(name,job){
this.name = name;
this.job = job;
}
// 使用對(duì)象字面量形式改寫原型獨(dú)享
Person.prototype ={
constructor:Person, // 手動(dòng)指定這里的指向該構(gòu)造函數(shù)
outName:function(){
return this.name;
},
outJob:function(){
return this.job;
}
}
var person = new Person("隨筆川跡","coder");
console.log(person.name,person.job);
console.log(person.outName(),person.outJob());
console.log(person.constructor === Person); // true
console.log(person.constructor === Object); // false
console.log(person instanceof Person); // true
在原有的對(duì)象基礎(chǔ)上上,通過prototype進(jìn)行額外的,封裝,拓展
實(shí)例代碼如下:
// 通過原型prototype對(duì)現(xiàn)有的內(nèi)容進(jìn)行額外的拓展,給數(shù)組Array添加方法
Array.prototype.sum = function(){
return this.reduce(function(m,n){
return m+n;
})
}
var arrNums = [1,2,3,4,5,6,7,8,9,10];
var result = arrNums.sum();
console.log("arrNums的和為",result); // arrNums的和為 55
// 給String添加額外的方法
String.prototype.capitalize = function(){
return this.charAt(0).toUpperCase()+this.substring(1);
}
var message = "suibichuanji hello";
console.log(message.capitalize()); // Suibichuanji hello
控制臺(tái)輸出如下:
以上例子中,我們是可以通過對(duì)系統(tǒng)提供的內(nèi)置對(duì)象進(jìn)行額外拓展的,也就是說系統(tǒng)對(duì)象(Date,String,Object,Array,RegExp等)是構(gòu)造函數(shù),當(dāng)現(xiàn)有提供的功能沒法滿足時(shí),就可以根據(jù)prototype進(jìn)行拓展,因此都有原型對(duì)象給你去改變,在該新增的方法前面添加構(gòu)造函數(shù).prototype就可以了,上面的例子中是給Array.prototype添加了一個(gè)sum()求和的方法,該方法對(duì)數(shù)組所有元素進(jìn)行求和并返回,arrNums數(shù)組通過原型對(duì)象自動(dòng)就有了這個(gè)sum()方法,在sum()方法內(nèi)部,this指向數(shù)組對(duì)象實(shí)例arrNums,所以該方法也可以使用數(shù)組的其他方法,什么reduce(),substring(),等都可以
原型中的屬性優(yōu)先級(jí)
示例代碼如下所示
var arr = [];
arr.name = "隨筆川跡";
Array.prototype.name = "川流不息";
console.log(arr.name); // 隨筆川跡
控制臺(tái)輸出如下
從上結(jié)果中可以得出:當(dāng)構(gòu)造函數(shù)自定義的屬性名與該構(gòu)造函數(shù)下原型屬性名相同時(shí),
構(gòu)造函數(shù)的自定義屬性優(yōu)先于原型屬性(可以把構(gòu)造函數(shù)理解為內(nèi)聯(lián)樣式),而原型屬性或者原型方法可以看做是class)小結(jié):構(gòu)造函數(shù)就是用new關(guān)鍵字調(diào)用的普通函數(shù),可以隨時(shí)定義自己的構(gòu)造函數(shù)來創(chuàng)建多個(gè)具有同樣的屬性的對(duì)象,可以用instanceof操作符(建議用這個(gè))者直接訪問constructor屬性來鑒別對(duì)象是被哪個(gè)構(gòu)造函數(shù)創(chuàng)建的,每一個(gè)函數(shù)都具有prototype屬性,它定義了構(gòu)造函數(shù)所有對(duì)象共享屬性
- 自定義的屬性和方法放在構(gòu)造函數(shù)里面
- 凡是共享的屬性和方法掛載在該構(gòu)造函數(shù)原型下面
- javascript的查找變量的機(jī)制,是沿著作用域鏈逐級(jí)向上查找的,在原型里,是原型鏈,構(gòu)造函數(shù)與原型之間的連接就是原型鏈,當(dāng)訪問對(duì)象的某個(gè)屬性時(shí),js首先在自定義的屬性的作用域內(nèi)查找該變量是否存在,如果不存在,則會(huì)沿著原型鏈向原型下的查找該屬性,直至頂層Object的原型對(duì)象,若有則返回,若無,則返回undefined
面向?qū)ο笮?shí)例
效果圖:
css層疊樣式代碼
*{
padding:0;
margin:0;
}
#wrap{
width:300px;
height:260px;
border:1px solid #ccc;
margin:0 auto;
}
#wrap:after{
content:"";
height:0;
display:block;
clear:both;
zoom:1;
}
#wrap div{
height:100%;
display:none;
text-indent:10px;
background:#2263A3;
color:#fff;
}
#wrap div:nth-of-type(1){
display:block;
}
#wrap input.active{
background:#2263A3;
color:#fff;
}
#wrap input{
width:100px;
height:30px;
background:#abcdef;
text-align:center;
line-height:30px;
outline:none;
border:none;
float:left;
cursor:pointer;
margin-bottom:30px;
}
html結(jié)構(gòu)
<div id="wrap">
<input type="button" class="active" value="公告" name="">
<input type="button" value="規(guī)則" name="">
<input type="button" value="論壇" name="">
<div>歡迎關(guān)注微信itclancoder公眾號(hào)</div>
<div>點(diǎn)擊右上方藍(lán)字即可關(guān)注</div>
<div>什么都沒有留下</div>
</div>
js代碼
// 普通寫法
// 獲取元素
var oWrap = document.querySelector("#wrap");
var aBtns = oWrap.getElementsByTagName("input");
var aDivs = oWrap.getElementsByTagName("div");
// 循環(huán)
for(var i = 0;i<aBtns.length;i++){
aBtns[i].index = i; //添加索引
aBtns[i].onclick = function(){ // 添加事件
for(var j = 0;j<aBtns.length;j++){
aBtns[j].className = ""; // 先去除掉所有的className
aDivs[j].style.display = "none"; // 先隱藏
}
// 添加class
this.className = "active";
aDivs[this.index].style.display = "block"; // 內(nèi)容顯示
}
}
jquery寫法
$(function(){
$(" #wrap input").click(function(){
var $index = $(this).index(); // 獲取索引
$(this).addClass("active").siblings().removeClass("active");
$("#wrap div").eq($index).show().siblings("div").hide();
})
})```
`面向?qū)ο髮懛╜
function 構(gòu)造函數(shù)(){
this.屬性 // 對(duì)象.屬性
}
構(gòu)造函數(shù).原型.方法 = function(){}
var 對(duì)象1 = new 構(gòu)造函數(shù)();
對(duì)象1.方法();
`面向?qū)ο筮x項(xiàng)卡代碼示例如下所示`
window.onload = function(){
var t = new TabSelect(); // 實(shí)例化對(duì)象
t.init(); // 實(shí)例化對(duì)象調(diào)用方法
}
// 聲明構(gòu)造函數(shù)
function TabSelect(){
// this == TabSelect,添加自定義屬性,獲取對(duì)象
this.oWrap = document.querySelector("#wrap");
this.aBtns = this.oWrap.getElementsByTagName("input");
this.aDivs = this.oWrap.getElementsByTagName("div");
}
// 構(gòu)造函數(shù)的原型下添加方法(初始化)
TabSelect.prototype.init = function(){
var that = this; // 注意這里的this指向,是TabSelect,用一個(gè)局部變量將this給存儲(chǔ)起來,其實(shí)這種方式是根據(jù)詞法作用域,閉包的方式來解決的
for(var i = 0;i<this.aBtns.length;i++){
this.aBtns[i].index = i; //添加索引
this.aBtns[i].onclick = function(){
that.change(this);
//console.log(this);匿名函數(shù)里面的this指向的是input按鈕元素
};
}
}
// 構(gòu)造器函數(shù)原型對(duì)象下添加方法
TabSelect.prototype.change = function(obj){
//console.log(obj); // input點(diǎn)擊按鈕元素
for(var j = 0;j<this.aBtns.length;j++){
this.aBtns[j].className = ""; // 先去除掉所有的className
this.aDivs[j].style.display = "none"; // 先隱藏
}
// 添加class
obj.className = "active";
this.aDivs[obj.index].style.display = "block"; // 內(nèi)容顯示
}
`小結(jié)`:
本例從普通寫法,jquery寫法,在到最后面向?qū)ο筮x項(xiàng)卡寫法,完成一簡(jiǎn)易的選項(xiàng)卡,其中jquery寫法最為簡(jiǎn)單,容易懂,但是這里我只是為了嘗試用面向?qū)ο蟮乃枷肴憫?yīng)用,實(shí)際開發(fā)中,無論哪種方式,只要能實(shí)現(xiàn)出來就行,從普通的寫法,也就是原生js寫法,到面向?qū)ο髮懛?可以看出首先通過變形,把局部的功能給拎出來,封裝成一個(gè)函數(shù),這個(gè)過程中盡量不要出現(xiàn)函數(shù)嵌套函數(shù),因?yàn)閠his是指向是個(gè)令人頭疼的問題,可以有全局變量,window.onload里面盡量是實(shí)例化對(duì)象,與對(duì)象的調(diào)用的方式,把不是賦值的語句單獨(dú)放到一個(gè)函數(shù)當(dāng)中(比如上文中的獲取元素,給TabSelect添加自定義屬性),最后就是改變this指向問題,事件或者定時(shí)器,讓面向?qū)ο笾械膖his指向該對(duì)象
`總結(jié)`:
本篇主要是本人對(duì)構(gòu)造器函數(shù)與原型對(duì)象的一點(diǎn)點(diǎn)理解,new操作符調(diào)用的函數(shù)為構(gòu)造函,功能上與內(nèi)置的函數(shù)并沒有多大的區(qū)別,構(gòu)造函數(shù)首字母大寫用來區(qū)分普通函數(shù)還是構(gòu)造函數(shù),構(gòu)造函數(shù)中的this指向該實(shí)例化的構(gòu)造函數(shù),主要是創(chuàng)建多個(gè)共享特定屬性和行為的對(duì)象,用于創(chuàng)建模板,作為餅干工具,而原型對(duì)象主要是改寫構(gòu)造函數(shù)(對(duì)象)下面的方法和屬性,讓公用方法或者屬性在內(nèi)存中存在一份,解決了當(dāng)創(chuàng)建多個(gè)實(shí)例化對(duì)象時(shí),重復(fù)的創(chuàng)建構(gòu)造函數(shù)的過程,目的是減少內(nèi)存開銷,提高性能,還有就是原型在原有的對(duì)象基礎(chǔ)上,通過prototype進(jìn)行額外的,封裝,拓展,原型是掛載在構(gòu)造函數(shù)下面的,以及最后用面向?qū)ο髮懛▽?shí)現(xiàn)了一個(gè)小實(shí)例,其實(shí)設(shè)計(jì)模式中的原型模式就是面向?qū)ο蟮膶懛?殺雞焉用牛刀,適合自己的才是好方法,面向?qū)ο蟮膶懛?對(duì)于簡(jiǎn)單的實(shí)例,面向過程就可以了,對(duì)于復(fù)雜的實(shí)例,什么組件,插件,我覺得都是面向?qū)ο蟮膽?yīng)用,關(guān)于面向?qū)ο?我也只是略知皮毛,在不斷的學(xué)習(xí)當(dāng)中...
`以下是本篇提點(diǎn)概要`
* 什么是函數(shù):function關(guān)鍵字聲明,一獨(dú)立封閉功能的代碼塊,也是對(duì)象
* 什么是構(gòu)造函數(shù):new關(guān)鍵字創(chuàng)建對(duì)象時(shí)調(diào)用的函數(shù),用于創(chuàng)建模板,生成餅干工具
* 普通函數(shù)與構(gòu)造函數(shù)的區(qū)別,有new無new區(qū)別,this的指向,普通函數(shù),this指向全局window,而構(gòu)造器函數(shù)this,指向該new 構(gòu)造器函數(shù)調(diào)用
* 為何內(nèi)置構(gòu)造函數(shù)無new也能工作,因?yàn)槟切﹥?nèi)置系統(tǒng)構(gòu)造函數(shù),都被設(shè)計(jì)為作用域安全的構(gòu)造函數(shù),一個(gè)作用域安全的構(gòu)造函數(shù)無new也可以工作,并返回同樣類型的對(duì)象
* 原型對(duì)象,prototype,函數(shù)一旦聲明,就有該屬性,作用1:去改寫對(duì)象下面公用的方法或者屬性,讓公用方法或者屬性在內(nèi)存中存在一份,可以看作是對(duì)象的基類
作用2:在原有的對(duì)象基礎(chǔ)上上,通過prototype進(jìn)行額外的,封裝,拓展
* 區(qū)分構(gòu)造函數(shù)自定義屬性與原型屬性,用in操作符,hasOwnProperty組合使用進(jìn)行判斷(見上示例代碼)
* 使用對(duì)象字面量形式改寫原型對(duì)象會(huì)改變構(gòu)造函數(shù)的屬性,指向問題,需手動(dòng)的改寫原型對(duì)象手動(dòng)設(shè)置constructor屬性
* 在原有的對(duì)象基礎(chǔ)上,通過prototype進(jìn)行額外的,封裝,拓展
* 原型中的屬性優(yōu)先級(jí),構(gòu)造函數(shù)自定義的屬性優(yōu)先級(jí)優(yōu)先于原型屬性,查找變量的方式是沿著原型鏈逐級(jí)查找的,直至頂層Object,有則返回,無則返回undeinfed
* 面向?qū)ο笮?shí)例,普通寫法,JQuery寫法與面向?qū)ο筮x項(xiàng)卡寫法