閱讀807 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Javascript函數、構造函數、原型、類和對象

函數

函數是JavaScript中特殊的對象,對函數執行typeof運算會返回字符串"function",因為函數也是對象,他們可以擁有屬性和方法。

靜態方法

函數在JS中定義了類的名字,任何添加到函數的屬性都是類字段和類方法(類比Java中類的static variable 和 static method),下麵代碼起到Java裏麵static method的效果:

		var foo = function(){};
		foo.printLog = function(){
			if (arguments.length != 0)
			{
				console.log(Array.prototype.join.call(arguments, " - "));
			}
		};
		foo.printLog("Test", "Static Method");

構造函數

如果函數或者方法的調用之前加上new關鍵字,他就構成了構造函數的調用.,

構造函數的調用和普通函數,方法的調用在實參處理,調用上下文和返回值方麵有不同

(1).實參處理不同

凡是沒有形參的構造函數調用都可以省略圓括號


(2)調用上下文的不同

調用構造函數創建一個新的空對象,這個對象繼承自構造函數的prototype(原型).構造函數試圖初始化這個新對象,並把這個新對象當做構造函數的調用上下文,所以在構造函數中this關鍵字可以用來引用這個新對象,也就能夠在構造函數中初始化這個新對象

注意: 盡管構造函數看起來像一個方法調用,它依然會使用這個新對象作為調用上下文。也就是說,在表達式new o.m() 中,調用上下文並不是o.

例子1-2:

		function A()  
		{  
			this.a = 123; 
			this.init();
		}  
		A.prototype = {
			get: function(){console.log('The value of A is ' + this.a);},
			init: function(){
					console.log('do init here'); 
					this.a=567;
				 }
		}
		var obj = new A;
		obj.get();

(3)返回值不同

構造函數通常不使用return關鍵字調用完畢之後會返回一個對象(就是新創建的這個對象)


this關鍵字

this是一個關鍵字,不是變量也不是屬性.

this關鍵字沒有作用域的限製,他隻有下麵幾種情況

(1)一個函數被當做方法調用,則函數中的this指向的是調用它的對象

(2)一個函數被當做函數調用,則函數中的this指向的全局對象(非嚴格模式)或者是undefined(嚴格模式下)

(3)函數被當做構造函數調用,this就指向構造函數創建的新對象

例子:

		var a = {name:'frank',age:30};//創建一個新對象  
		function A()  
		{  
			console.log(this);
		}  
		A.call(a); //對象a的方法調用:this代表對象a,因為通過call的調用上下文是a 
		A();//全局函數調用:this代表全局變量window
		new A();//構造函數調用: this代表新創建的對象A {}


函數原型,每一個函數都包含一個prototype屬性,而且這個prototype包含唯一一個屬性constructor指向構造函數對象,當將函數用做構造函數的時候,新創建的對象會從原型對象上繼承屬性,按照慣例,構造函數首字母要大寫:

		function Foo(){};//Constructor

		Foo.prototype = {
			constructor:Foo;//因為重寫了prototype,需要顯示的將constructor定義在此
			userName:"Frank"};
		var fooInstance = new Foo();
		console.log(fooInstance.userName);//As an instance of Foo, it inherited all attributes from Foo's prototype
		console.log(Foo.userName);// 函數對象Foo並沒有userName屬性,所以是undefined,但是Foo的原型對象有,並且被fooIntance繼承
		console.log(fooInstance.constructor === Foo);


注意:每個函數自身就是個對象,所以可以被賦值和像參數一樣傳遞,但同時,它也可以被用作構造函數,用來創建其他對象。

構造函數的prototype是被所有實例共享的,例如:

<script type="text/javascript">
	function Person(){};
	Person.prototype = 
		{
			constructor : Person,
			name : "Frank",
			friends : ["Coco","Nancy"],
			sayName: function(){ alert(this.name);}
		};
	var p1 = new Person();
	var p2 = new Person();
	p1.friends.push("Van");
	console.log(p1.friends);
	console.log(p2.friends);
	p1.sayName();

</script>

因為p1和p2共享prototype對象,所以p1的friends改變會影響到p2,這顯然不是我們想要的,我們希望每個Person實例有自己單獨的屬性,但同時,我們又希望對一些公共方法可以共享,以節省內存。

構造方法和原型組合模式

構造方法用於定義實例屬性,而原型模式用於定義方法和共享的屬性。結果,每個實例都會有自己的一份實例屬性的副本,同時又共享著對方法的引用,最大限度地節省了內存。有點類似於繼承,但區別是prototype是單例的。

修改後的寫法:

<script type="text/javascript">
	function Person(name, friends){
		this.name = name;
		this.friends = friends;
	};
	Person.prototype = 
		{
			constructor : Person,
			sayName: function(){ console.log(this.name);}
		};
	var p1 = new Person("Frank",["coco","nancy"]);
	var p2 = new Person("Daniel",["coco","nancy"]);
	p1.friends.push("Van");
	console.log(p1.friends);
	console.log(p2.friends);
	p1.sayName();
	p2.sayName();
</script>


函數直接量(function literal),匿名函數 (anonymous function)

作為命名空間的函數

在函數中聲明的變量在整個函數體內都是可見的,在函數的外部是不可見的(局部變量)。不在任何函數中聲明的變量是全局變量,在整個JavaScript程序中都可見。

一種慣用法是用自調用匿名函數(self-invoking anonymous function) 來實現這個命名空間技術,其寫法如下

function(){

//模塊代碼

  })();

因為默認情況下,JS是運行在全局的namespace下,而對全局變量的暴露是非常危險的,因為任何無意的修改和覆蓋都會來帶意想不到的錯誤。因此在JS開發中,盡量不要定義全局變量。在JS框架開發中此條法則體現的尤為明顯,因為假如框架暴露過多的全局變量,一旦被用戶修改,就可能導致框架不可用。

所以在JQuery中,整個框架通過 window.jQuery = window.$ = jQuery; 隻向外部暴露了2個全局變量,即著名的$和JQuery

詳細請見jQuery源碼分析: https://nuysoft.iteye.com/blog/1177451

對象

JavaScript 中,除了數字、true、false、null和undefined之外,其他值都是對象,每個對象擁有三個相關的對象屬性(object attribute)

  • 對象的原型(prototype)指向另一個對象,本對象的屬性繼承自它的原型對象。對象繼承是通過原型鏈實現的
  • 對象的類(class)是一個標識對象類型的字符串
  • 對象的擴展標記(extensible flag)指明了是否可以向該對象添加新屬性

對象繼承


所有通過對象直接量(Object literals)創建的對象都具有同一個原型對象Object.prototype,對象的屬性繼承自原型對象,對象之所以會繼承是因為屬性的查詢過程是,搜索對象的原型鏈,直到根原型,如果還沒有找到標識為undefined。
		var o = {};// o 從 Object.prototype 繼承對象屬性
		o.x =1; // 定義新屬性x
		var p = inherit(o);//p繼承o和Object.prototype
		p.y = 2;
		var q = inherit(p);//q繼承p、o和Object.prototype
		q.z =3 ;
		console.log(q.x + q.y);
		console.log(q.toString() + q.a);//toString繼承自Object.prototype, q.a is undefined

		function inherit(parent){
			f = function (){};//定義一個空構造函數
			f.prototype = parent; //將其原型屬性設置為p
			return new f();//使用f()創建p的繼承對象
		};

上麵代碼對象在內存中的示意圖:



對象工廠

工廠方法這個設計模式在JS中也是經常用到,下麵以jQuery源碼為例,看一下對象工廠的運用。

	<script language="javascript">
		console.log("Start");

		(function(window){//自調用函數
		   var //定義本地變量
			   core_version = "1.0.0",

			   jQuery = function(){//這是一個工廠方法,專門用來產生jQuery對象
					return new jQuery.fn.init();//下麵的prototype賦值使得init函數和jQuery函數據有相同的prototype,即jQuery對象
				};
			
			jQuery.fn = jQuery.prototype = {
				jquery: core_version,
				constructor: jQuery,
				init: function(){
					return this;
				},
				debug: function(){console.log("debugging")}
			};
			
			// Give the init function the jQuery prototype for later instantiation
			jQuery.fn.init.prototype = jQuery.fn; 

			window.jQuery = window.$ = jQuery;//將本地變量jQuery設置為全局變量
		}(window));//傳入全局變量window

		$().debug();//$()將返回一個通過工廠方法產生的對象。
		console.log("Current version is "+$().jquery);
	</script>




最後更新:2017-04-03 14:54:35

  上一篇:go mooc 第五章 習題
  下一篇:go 基本數據結構和算法在Linux內核中使用