javascript
JS笔记
面向對象編程
實例對象與 new 命令
JavaScript 語言的對象體系,不是基于“類”的,而是基于構造函數(constructor)和原型鏈(prototype)。
<script>var Vehicle = function(){this.price = 1000;}var v = Vehicle();console.log(v);console.log(price);</script>上面代碼中,調用Vehicle構造函數時,忘了加上new命令。結果,變量v變成了undefined,而price屬性變成了全局變量。因此,應該非常小心,避免不使用new命令、直接調用構造函數。
為了保證構造函數必須與new命令一起使用,一個解決辦法是,構造函數內部使用嚴格模式,即第一行加上use strict。這樣的話,一旦忘了使用new命令,直接調用構造函數就會報錯。
function Fubar(foo, bar){'use strict';this._foo = foo;this._bar = bar; }Fubar() // TypeError: Cannot set property '_foo' of undefined上面代碼的Fubar為構造函數,use strict命令保證了該函數在嚴格模式下運行。由于嚴格模式中,函數內部的this不能指向全局對象,默認等于undefined,導致不加new調用會報錯(JavaScript 不允許對undefined添加屬性)。
另一個解決辦法,構造函數內部判斷是否使用new命令,如果發現沒有使用,則直接返回一個實例對象。
function Fubar(foo, bar) {if (!(this instanceof Fubar)) {return new Fubar(foo, bar);}this._foo = foo;this._bar = bar; }Fubar(1, 2)._foo // 1 (new Fubar(1, 2))._foo // 1上面代碼中的構造函數,不管加不加new命令,都會得到同樣的結果。
如果構造函數內部有return語句,而且return后面跟著一個對象,new命令會返回return語句指定的對象;否則,就會不管return語句,返回this對象。
var Vehicle = function () {this.price = 1000;return 1000; };(new Vehicle()) === 1000 // false上面代碼中,構造函數Vehicle的return語句返回一個數值。這時,new命令就會忽略這個return語句,返回“構造”后的this對象。
但是,如果return語句返回的是一個跟this無關的新對象,new命令會返回這個新對象,而不是this對象。這一點需要特別引起注意。
var Vehicle = function (){this.price = 1000;return { price: 2000 }; };(new Vehicle()).price // 2000上面代碼中,構造函數Vehicle的return語句,返回的是一個新對象。new命令會返回這個對象,而不是this對象。
另一方面,如果對普通函數(內部沒有this關鍵字的函數)使用new命令,則會返回一個空對象。
function getMessage() {return 'this is a message'; }var msg = new getMessage();msg // {} typeof msg // "object"上面代碼中,getMessage是一個普通函數,返回一個字符串。對它使用new命令,會得到一個空對象。這是因為new命令總是返回一個對象,要么是實例對象,要么是return語句指定的對象。本例中,return語句返回的是字符串,所以new命令就忽略了該語句。
函數內部可以使用new.target屬性。如果當前函數是new命令調用,new.target指向當前函數,否則為undefined。
function f() {console.log(new.target === f); }f() // false new f() // true使用這個屬性,可以判斷函數調用的時候,是否使用new命令。
function f() {if (!new.target) {throw new Error('請使用 new 命令調用!');}// ... }f() // Uncaught Error: 請使用 new 命令調用!上面代碼中,構造函數f調用時,沒有使用new命令,就拋出一個錯誤
構造函數作為模板,可以生成實例對象。但是,有時拿不到構造函數,只能拿到一個現有的對象。我們希望以這個現有的對象作為模板,生成新的實例對象,這時就可以使用Object.create()方法。
var person1 = {name: '張三',age: 38,gre`eting: function() {`console.log('Hi! I\'m ' + this.name + '.');} };var person2 = Object.create(person1);person2.name // 張三 person2.greeting() // Hi! I'm 張三.上面代碼中,對象person1是person2的模板,后者繼承了前者的屬性和方法。
this 關鍵字
this可以用在構造函數之中,表示實例對象。除此之外,this還可以用在別的場合。但不管是什么場合,this都有一個共同點:它總是返回一個對象。
簡單說,this就是屬性或方法“當前”所在的對象。
this.property上面代碼中,this就代表property屬性當前所在的對象。
var person = {name: '張三',describe: function () {return '姓名:'+ this.name;}};person.describe() // "姓名:張三"上面代碼中,this.name表示name屬性所在的那個對象。由于this.name是在describe方法中調用,而describe方法所在的當前對象是person,因此this指向person,this.name就是person.name。
由于對象的屬性可以賦給另一個對象,所以屬性所在的當前對象是可變的,即this的指向是可變的。
var A = {name: '張三',describe: function () {return '姓名:'+ this.name;} };var B = {name: '李四' };B.describe = A.describe; B.describe() // "姓名:李四"上面代碼中,A.describe屬性被賦給B,于是B.describe就表示describe方法所在的當前對象是B,所以this.name就指向B.name。
<input type="text" name="age" size=3 onChange="validate(this, 18, 99);"><script> function validate(obj, lowval, hival){if ((obj.value < lowval) || (obj.value > hival))console.log('Invalid Value!'); } </script>上面代碼是一個文本輸入框,每當用戶輸入一個值,就會調用onChange回調函數,驗證這個值是否在指定范圍。瀏覽器會向回調函數傳入當前對象,因此this就代表傳入當前對象(即文本框),然后就可以從this.value上面讀到用戶的輸入值。
總結一下,JavaScript 語言之中,一切皆對象,運行環境也是對象,所以函數都是在某個對象之中運行,this就是函數運行時所在的對象(環境)。這本來并不會讓用戶糊涂,但是 JavaScript 支持運行環境動態切換,也就是說,this的指向是動態的,沒有辦法事先確定到底指向哪個對象,這才是最讓初學者感到困惑的地方。
總結
- 上一篇: CLIP论文阅读、zero-shot实验
- 下一篇: SpringMVC(3)