javascript沒那麼簡單

來源:互聯網
上載者:User

寫在前面
似乎園子裡最近少了些人,各個文章的閱讀量少了許多
廢話不說,寫此文目的是為了讓更多的程式員理解javascript的一些概念,對是理解不是瞭解
我們已經瞭解得夠多了,該是向深入理解的方向靠攏的時候了
為什麼這麼說,前些日子收到面試邀請,那就去試試唄,有幾年沒有面試過了吧
和面試官坐在沙發上,聊天式的他問我答,以下就是幾個javascript方面的問題
>請建立一個對象,包括幾個公有屬性,接下來是為對象建立一個公有方法,然後為對象建立幾個私人屬性,一個私人方法
說實話,這幾個問題我默名其妙,要是他讓我用jquery寫個拖動外掛程式什麼的,我估計我能寫挺好,原生的javascript,暈,雖然我看過jquery源碼解讀,但這些基本概念要命
稍後,我會在本文中將答案寫出來
如果面試官在看,說聲謝謝,面試完之後我就去深圳購書中心買javascript去了,好貴,呵呵,看中之後,到卓越定了一個《javascript王者歸來》
然後基本上啥也沒幹,仔細的看了一個多星期,看不明白的就到園子裡來找,園子裡的寶貝真不少!(還壓了個韻。。。)
本文的例子輸出使用如下方法,便於查看
function dwn(s){
 document.write(s+"<br/>");
}
以下內容有些是原創,有些來自於網路,或者可以看成是讀書筆記,如果有哪裡不對的,請各位不吝賜教,在下感激不盡

 

本文開始
一,function
從一開始接觸到js就感覺好靈活,每個人的寫法都不一樣,比如一個function就有N種寫法
如:function showMsg(){},var showMsg=function(){},showMsg=function(){}
似乎沒有什麼區別,都是一樣的嘛,真的是一樣的嗎,大家看看下面的例子
///------------------------------------------------------------------------------------------------
//函數定義:命名函數(聲明式),匿名函數(引用式)
//聲明式,定義代碼先於函數執行代碼被解析
function t1(){
 dwn("t1");
}
t1();
function t1(){
 dwn("new t1");
}
t1();
//引用式,在函數運行中進行動態解析
var t1=function(){
 dwn("new new t1");
}
t1();
var t1=function(){
 dwn("new new new t1");
}
t1();
//以上輸出:new t1,new t1,new new t1,new new new t1
可能想著應該是輸出t1,new t1,new newt1,new new new t1,結果卻並不是這樣,應該理解這句話:聲明式,定義代碼先於函數執行代碼被解析
如果深入一步,應該說是scope鏈問題,實際上前面兩個方法等價於window.t1,可以理解為t1是window的一個公有屬性,被賦了兩次值,以最後一次賦值為最終值
而後面兩個方法,可以理解為是t1是個變數,第四個方法的var去掉之後的結果仍然不會改變
然而,當第四個方法改成function t1(){}這樣的聲明式時,結果變成了new new new t1,new new new t1,new new t1,new new t1
前面兩個按照我的理解可以很好的理解為什麼是這個答案,第三個也可以理解,但是最後一個輸出讓我比較糾結,希望有高手出現解答一下
另外匿名函數還有(function(){...})()這樣的寫法,最後一個括弧用於參數輸入
還有var t1=new function(){..}這樣的聲明,實際上t1已經是一個對象了
例:
var t2 = new function()
{
     var temp = 100; //私人成員
     this.temp = 200; //公有成員,這兩個概念會在第三點以後展開說明
     return temp + this.temp;
}

alert(typeof(t2));   //object
alert(t2.constructor());  //300
除此之外,還有使用系統內建函數對象來構建一個函數,例:
var t3 = new Function('var temp = 100; this.temp = 200; return temp + this.temp;'); //這個位置加不加new結果都一樣,WHY
alert(typeof(t3)); //function
alert(t3());  //300

 

二,建立對象
首先我們理解一下物件導向編程(Object-Oriented Programming,OOP),使用OOP技術,常常要使用許多代碼模組,每個模組都提供特定的功能,每個模組都是孤立的,甚至與其它模組完全獨立
。這種模組化編程方法提供了非常大的多樣性,大大增加了代碼的重用機會。可以舉例進一步說明這個問題,假定電腦上的一個高效能應用程式是一輛一流賽車。如果使用傳統的編程技巧,這輛賽車就是
一個單元。如果要改進該車,就必須替換整個單元,把它送回廠商,讓汽車專家升級它,或者購買一個新車。如果使用OOP技術,就只需從廠商處購買新的引擎,自己按照說明替換它,而不必用鋼鋸切割車體。
不過大部分的論點是,javascript並不是直接的物件導向的語言,但是通過類比可以做到很多物件導向語言才能做到的事,如繼承,多態,封裝,javascript都能幹(沒有做不到,只是想不到)
///------------------------------------------------------------------------------------------------
//以下三種構造對象的方法
//new Object,執行個體化一個Object
var a=new Object();
a.x=1,a.y=2;
//對象直接量
var b={x:1,y:2};
//定義類型
function Point(x,y){ //類似於C#中的類
 this.x=x;
 this.y=y;
}
var p=new Point(1,2); //執行個體化類
第一種方法通過構造基本對象直接添加屬性的方法來實現,第二種和第一種差不多,可以看成是第一種方法的快捷標記法
第三種方法中,可以以”類“為基礎,創造多個類型相同的對象

 

三,對象屬性的封裝(公有和私人)
以例子來說明
function List(){
 var m_elements=[]; //私人成員,在對象外無法訪問,如果此處無var聲明,則m_elements將變成全域變數,這樣外部是可以直接存取到的,如alert(m_elements[0])
 m_elements=Array.apply(m_elements,arguments);
 //此處類比getter,使用時alist.length;
 //等價於getName()方式:this.length=function(){return m_elements.length;},使用時alist.length();
 //公有屬性,可以通過"."運算子或下標來訪問
 this.length={
  valueOf:function(){
   return m_elements.length;
  },
  toString:function(){
   return m_elements.length;
  }
 }
 //公有方法,此方法使用得alert(alist)相當於alert(alist.toString())
 this.toString=function(){ 
  return m_elements.toString();
 }
 //公有方法
 this.add=function(){
  m_elements.push.apply(m_elements,arguments);
 }
 //私人方法如下形式,這裡涉及到了閉包的概念,接下來繼續說明
 //var add=function()或function add()
 //{
 //m_elements.push.apply(m_elements,arguments);
 //}
}
var alist=new List(1,2,3);
dwn(alist);  //=alert(alist.toString()),輸出1,2,3
dwn(alist.length); //輸出3
alist.add(4,5,6); 
dwn(alist);  //輸出1,2,3,4,5,6
dwn(alist.length); //輸出6

 

四,屬性和方法的類型
javascript裡,對象的屬性和方法支援4種不同的類型:private property(私人屬性),dynamic public property(動態公有屬性),static public property/prototype property(靜態公有屬性或原型屬性),
static property(靜態屬性或類屬性)。私人屬性對外界完全不具備訪問性,可以通過內部的getter和setter(都是類比);動態公有屬性外界可以訪問,每個對象執行個體持有一個副本,不會相互影響;原型
屬性每個對象執行個體共用唯一副本;類屬性不作為執行個體的屬性,只作為類的屬性。
以下是例子:
///------------------------------------------------------------------------------------------------
//動態公有類型,靜態公有類型(原型屬性)
function myClass(){
 var p=100; //private property
 this.x=10;  //dynamic public property
}
myClass.prototype.y=20; //static public property or prototype property,動態為myClass的原型添加了屬性,將作用於所有執行個體化了的對象,注意這裡用到了prototype,這是一個非常有用的東東
//要想成為進階javascript階段,prototype和閉包必須得理解和適當應用
myClass.z=30; //static property

var a=new myClass();
dwn(a.p) //undefined
dwn(a.x) //10
dwn(a.y) //20
a.x=20;
a.y=40;
dwn(a.x); //20
dwn(a.y); //40
delete(a.x); //刪除對象a的屬性x
delete(a.y); //刪除對象a的屬性y
dwn(a.x); //undefined
dwn(a.y); //20 靜態公有屬性y被刪除後還原為原型屬性y
dwn(a.z); //undefined 類屬性無法通過對象訪問
dwn(myClass.z);

 

五,原型(prototype)
這裡只講部分,prototype和閉包都不是幾句話都能講清楚的,如果這裡可以給你一些啟蒙,則萬幸矣
習語”照貓畫虎“,這裡的貓就是原型,虎是類型,可以表示成:虎.prototype=某隻貓 or 虎.prototype=new 貓()
因為原型屬性每個對象執行個體共用唯一副本,所以當執行個體中的一個調整了一個原型屬性的值時,所有執行個體調用這個屬性時都將發生變化,這點需要注意
以下是原型關係的類型鏈:
function ClassA(){
}
ClassA.prototype=new Object();
function ClassB(){
}
ClassB.prototype=new ClassA();
function ClassC(){
}
ClassC.prototype=new ClassB();
var obj=new ClassC();
dwn(obj instanceof ClassC); //true
dwn(obj instanceof ClassB); //true
dwn(obj instanceof ClassA); //true
dwn(obj instanceof Object); //true
帶預設值的Point對象:
function Point2(x,y){
 if (x) this.x=x;
 if (y) this.y=y;
}
//設定Point2對象的x,y預設值為0
Point2.prototype.x=0;
Point2.prototype.y=0;
//p1是一個預設(0,0)的對象
var p1=new Point2(); //可以寫成var p1=new Point2也不會出錯,WHY
//p2賦值
var p2=new Point2(1,2);
dwn(p1.x+","+p1.y); //0,0
dwn(p2.x+","+p2.y); //1,2
delete對象的屬性後,原型屬性將回到初始化的狀態:
function ClassD(){
 this.a=100;
 this.b=200;
 this.c=300
}
ClassD.prototype=new ClassD(); //將ClassD原有的屬性設為原型,包括其值
ClassD.prototype.reset=function(){ //將非原型屬性刪除
 for (var each in this) {
  delete this[each];
 }
}
var d=new ClassD();
dwn(d.a); //100
d.a*=2;
d.b*=2;
d.c*=2;
dwn(d.a); //200
dwn(d.b); //400
dwn(d.c); //600
d.reset(); //刪掉非原型屬性,所有回來原型
dwn(d.a); //100
dwn(d.b); //200
dwn(d.c); //300

 

六,繼承
如果兩個類都是同一個執行個體的類型,那麼它們之間存在著某種關係,我們把同一個執行個體的類型之間的泛化關係稱為繼承。C#和JAVA中都有這個,具體的理解就不說了。
在javascript中,並不直接從方法上支援繼承,但是就像前面說的,可以類比啊
方法可以歸納為四種:構造繼承法,原型繼承法,執行個體繼承法和拷貝繼承法。融會貫通之後,還有混合繼續法,這是什麼法,就是前面四種挑幾種混著來~
以下例子來源於王者歸來,其中涉及到了apply,call和一些Array的用法,有興趣的可以自己在園子裡搜尋一下
1,構造繼續法例子:
//定義一個Collection類型
 function Collection(size)
 {
  this.size = function(){return size};  //公有方法,可以被繼承
 }
 
 Collection.prototype.isEmpty = function(){   //靜態方法,不能被繼承
  return this.size() == 0;
 }
 
 //定義一個ArrayList類型,它"繼承"Collection類型
 function ArrayList()
 {
  var m_elements = []; //私人成員,不能被繼承
  m_elements = Array.apply(m_elements, arguments);

  //ArrayList類型繼承Collection
  this.base = Collection;
  this.base.call(this, m_elements.length);
 
  this.add = function()
  {
   return m_elements.push.apply(m_elements, arguments);
  } 
  this.toArray = function()
  {
   return m_elements;
  }
 }

 ArrayList.prototype.toString = function()
 {
  return this.toArray().toString();
 }
 //定義一個SortedList類型,它繼承ArrayList類型
 function SortedList()
 {
  //SortedList類型繼承ArrayList
  this.base = ArrayList;
  this.base.apply(this, arguments);

  this.sort = function()
  {
   var arr = this.toArray();
   arr.sort.apply(arr, arguments);
  }
 }

//構造一個ArrayList
 var a = new ArrayList(1,2,3);
 dwn(a);
 dwn(a.size()); //a從Collection繼承了size()方法
 dwn(a.isEmpty); //但是a沒有繼承到isEmpty()方法

 //構造一個SortedList
 var b = new SortedList(3,1,2);
 b.add(4,0); //b 從ArrayList繼承了add()方法
 dwn(b.toArray()); //b 從ArrayList繼承了toArray()方法
 b.sort();  //b 自己實現的sort()方法
 dwn(b.toArray());
 dwn(b);
 dwn(b.size()); //b從Collection繼承了size()方法
2,原型繼承法例子:
//定義一個Point類型
 function Point(dimension)
 {

  this.dimension = dimension;
 }

 //定義一個Point2D類型,"繼承"Point類型
 function Point2D(x, y)
 {
  this.x = x;
  this.y = y;
 }
 Point2D.prototype.distance = function()
 {
  return Math.sqrt(this.x * this.x + this.y * this.y);
 }
 Point2D.prototype = new Point(2);  //Point2D繼承了Point

 //定義一個Point3D類型,也繼承Point類型
 function Point3D(x, y, z)
 {
  this.x = x;
  this.y = y;
  this.z = z;
 }
 Point3D.prototype = new Point(3);  //Point3D也繼承了Point
 
 //構造一個Point2D對象
 var p1 = new Point2D(0,0);
 //構造一個Point3D對象
 var p2 = new Point3D(0,1,2);

 dwn(p1.dimension);
 dwn(p2.dimension);
 dwn(p1 instanceof Point2D); //p1 是一個 Point2D
 dwn(p1 instanceof Point); //p1 也是一個 Point
 dwn(p2 instanceof Point); //p2 是一個Point
以上兩種方法是最常用的
3,執行個體繼承法例子:
在說此法例子之前,說說構造繼承法的局限,如下:
function MyDate()
{
 this.base = Date;
 this.base.apply(this, arguments);
}
var date = new MyDate();
alert(date.toGMTString); //undefined,date並沒有繼承到Date類型,所以沒有toGMTString方法
核心對象的某些方法不能被構造繼承,原因是核心對象並不像我們自訂的一般對象那樣在建構函式裡進行賦值或初始化操作
換成原型繼承法呢?,如下:
function MyDate(){}
MyDate.prototype=new Date();
var date=new MyDate();
alert(date.toGMTString); //'[object]'不是日期對象,仍然沒有繼承到Date類型!
現在,換成執行個體繼承法:
function MyDate()
 {
  var instance = new Date(); //instance是一個新建立的日期對象
  instance.printDate = function(){
   document.write("<p> "+instance.toLocaleString()+"</p> ");
  } //對instance擴充printDate()方法
  return instance;  //將instance作為建構函式的傳回值返回
 }
 var myDate = new MyDate();
 dwn(myDate.toGMTString()); //這回成功輸出了正確的時間字串,看來myDate已經是一個Date的執行個體了,繼承成功
 myDate.printDate(); //如果沒有return instance,將不能以下標訪問,因為是私人對象的方法
4,拷貝繼承法例子:
Function.prototype.extends = function(obj)
{
 for(var each in obj)
 {
  this.prototype[each] = obj[each]; 
  //對對象的屬性進行一對一的複製,但是它又慢又容易引起問題
  //所以這種“繼承”方式一般不推薦使用
 }
}
var Point2D = function(){
 //……
}
Point2D.extends(new Point())
{
 //……
}
這種繼承法似乎是用得很少的。
5,混合繼承例子:
function Point2D(x, y)
{
 this.x = x;
 this.y = y;
}
function ColorPoint2D(x, y, c)
{
 Point2D.call(this, x, y); //這裡是構造繼承,調用了父類的建構函式
 //從前面的例子看過來,這裡等價於
 //this.base=Point2D;
 //this.base.call(this,x,y);
 this.color = c;
}
ColorPoint2D.prototype = new Point2D();  //這裡用了原型繼承,讓ColorPoint2D以Point2D對象為原型

 

本來還想繼續寫閉包,不過寫這個還真是很難,我也是廢了好幾個小時的力氣才算勉強弄明白,推薦以下博文:
http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1530074.html#1884486

希望以上文章對讀者有啟發,如果有錯誤,請留言,如果覺得還不錯,可以點擊右下的推薦給更多的人

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.