JavaScript中的小技巧和注意點(一)

/**僅記錄自己的學習歷程,爲以後的自己留一個參考/


1.檢查變量是否存在

var result = "";
if (typeof somevar !== "undefined"){
    result = "yes";
}

在這種情況下,typeof 返回的是一個字符串(先執行 typeof 再比較),這樣就可以與字符串"undefined"進行直接比對,如果 somevar 這個變量存在,則執行if條件語句。實際上是在用 typeof 測試一個變量是否已經被初始化(或者說測試變量值是否爲 undefined )


2.parseInt()
parseInt()會試圖將其收到的任何輸入值(通常是字符串)轉換成整數類型輸出。如果轉換失敗就返回NaN。
> parseInt('123');
123

> parseInt('1abc123');
1

> parseInt('abc123');
NaN
除此之外,該函數還有個可選的第二參數:基數(radix),它負責設定函數所期望的數字類型——十進制、十六進制、二進制。
> parseInt('FF',10);
NaN

> parseInt('FF',16);
255
在調用parseInt時沒有指定第二參數,函數會將其默認爲十進制,但有兩種情況例外:
(1)如果首參數字符串是0x開頭,第二參數就會被默認指定爲16;
(2)如果首參數以0開頭,第二參數就會被默認指定爲8。
> parseInt('377');
377

> parseInt('0377');
255

> parseInt('0x377');
887
當然,爲了保證程序的正確運行,明確指定radix值總是最安全的。值得一提的是,從ES5開始,在嚴格模式下,八進制數值就不再允許使用前綴0表示;ES6進一步明確,要使用前綴0o表示,並且將全局方法 parseInt()和 parseFloat()移植到了Number對象上,行爲完全保持不變。當然在非嚴格模式下,前綴 0o 和 0 均可以表示八進制。


3.能重寫自己的函數
由於一個函數可以返回另一個函數,所以我們可以讓函數從內部重寫自己,比如:
function a(){
    alert('A!');
    a=function(){
        alert('B!');
    };
}
這項技術對於某些瀏覽器相關的操作會相當有用。因爲在不同瀏覽器中,實現相同任務的技術可能是不同的,我們都知道瀏覽器的特性不可能因爲函數調用而發生任何改變,因此,最好的選擇就是讓函數根據其當前所在的瀏覽器來重新定義自己,即"瀏覽器兼容性探測"技術。


4.函數對象的長度

函數對象中也有一個length屬性,用於記錄該函數聲明時所決定的參數數量(ES6中函數參數可以指定默認值,指定了默認值以後函數length屬性將返回沒有指定默認值的參數的個數)。

arguments.length用於表示函數實際接受到的參數的個數,可用於模擬多態

function mufun(a,b,c){
    return arguments.length;
}

> myfun.length
3

>myfun(1,2)
2

5.call()和apply()
在JavaScript中,每個函數都有call()和apply()兩個方法,它們可以讓一個對象去"借用"另一個對象的方法,併爲己所用。這也是一種非常簡單而實用的代碼重用。
var some_obj = {
    name:'Jack',
    say:function(who){
        return 'Hi ' + who + ', I am ' + this.name;
    }
};
var my_obj = {
    name:'Rose'
}

>some_obj.say.call(my_obj,'Obama')
"Hi Obama, I am Rose"

當調用say()函數的對象方法call()時,this自動指向傳入的對象。另外,如果沒有將對象傳遞給call()的首參數,或者傳遞給它的是null,它的調用對象將會被默認爲全局對象,即this指向的是全局對象。

apply()的工作方式與call()基本相同,唯一的不同之處在於參數的傳遞形式,apply()所需要的參數都是通過一個數組來傳遞。所以下面兩行代碼的作用是等效的。

some_obj.say.call(my_obj,'Obama');

some_obj.say.apply(my_obj,['Obama']);

arguments看上去像是一個數組,但它實際上是一個類數組對象,它和數組相似是因爲其中也包含了索引元素和length屬性。但相似之處也就到此爲止了,因爲arguments不提供一些像sort()、slice()這樣的數組方法。但我們可以通過call()調用數組的各種方法,而不用重新編寫對應的代碼。

function fun(){
    var args = [].slice.call(arguments);
    return args.reverse();
}

> fun(1,2,3,4);
[4,3,2,1]

這裏的做法是新建一個空數組 [ ] ,再使用它的slice方法,當然,也可以通過Array.prototype.slice來調用同一個函數。


call()和apply()的另一個用法就是推斷對象類型。

在JS中是無法使用 typeof 區分對象和數組的,要想區分數組和對象,方法之一就是使用Object對象的toString()方法,這個方法會返回所創建對象的內部類名。

> Object.prototype.toString.call({});
"[Object Object]"

> Object.prototype.toString,call([]);
"[Object Array]"

> (function (){
    return Object.prototype.toString.call(arguments);
    })();
"[Object Arguments]"

在這裏,toString()方法必須要來自於Object構造器的prototype屬性。直接調用Array的toString()方法是不行的,因爲在Array對象中,這個方法已經出於其他目的被重寫了。


6.枚舉屬性

如果想獲得某個對象所有屬性的列表,我們可以使用for-in循環,但並不是所有的屬性都會在for-in循環中顯示。例如數組的length屬性和constructor屬性就不會被顯示。那些會顯示的屬性被稱爲可枚舉的,我們可以通過各個對象所提供的propertyIsEnumerable()方法來判斷對象的某個屬性是否可枚舉。

在for-in循環中,原型鏈中的各個原型屬性也會被顯示出來,當然前提是它們是可枚舉的。我們可以通過對象的hasOwnProperty()方法來判斷一個屬性是對象自身屬性還是原型屬性。

對於所有的原型屬性,propertyIsEnumerable()都會返回false,包括那些在for-in循環中可枚舉的屬性。如果propertyIsEnumerable()的調用是來自原型鏈上的某個對象,那麼該對象的屬性是可枚舉的。


7.原型陷阱

在處理原型問題時,需要特別注意以下兩種行爲

(1)對原型對象執行完全替換時,可能會觸發原型鏈中某種異常;

(2)prototype.constructor屬性是不可靠的。

>function Dog() {
    this.tail=true;
 }
>var benji = new Dog();

>Dog.prototype.say=function () {
     return 'Woof!';
 };
 
>benji.say()
"Woof!"

>benji.constructor === Dog
true

//用一個自定義的新對象完全覆蓋掉原有的原型對象
>Dog.prototype = {
     paws:4,
     hair:true
 };
 
>typeof benji.paws
"undefined"

>benji.say()
"Woof!"

>typeof benji.__proto__.say
"function"

>typeof benji.__proto__.paws
"undefined"

>var lucy = new Dog();
>lucy.say()
TypeError:lucy.say is not a function

>lucy.paws
4

>typeof lucy.__proto__.say
"undefined"

>typeof lucy.__proto__.paws
"number"

>lucy.constructor
function Object(){[native code]}

>benji.constructor
function Dog(){this.tail=true;}

事實證明,這會使原有對象不能訪問原型的新增屬性,它們依然可以通過__proto__與原有的原型對象保持聯繫。而之後創建的所有對象使用的都是被更新後的prototype對象,並且__proto__也指向了新的prototype對象。但這時候,新對象的constructor屬性就不能再保持正確了,原本應該是Dog()的引用卻指向了Object()。所以當我們重寫某對象的prototype時,需要重置相應的constructor屬性。

>Dog.prototype.constructor = Dog;
>new Dog().constructor === Dog
true

>lucy.constructor
function Dog() {
    this.tail=true;
}








發佈了25 篇原創文章 · 獲贊 23 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章