/**僅記錄自己的學習歷程,爲以後的自己留一個參考/
1.檢查變量是否存在
var result = "";
if (typeof somevar !== "undefined"){
result = "yes";
}
在這種情況下,typeof 返回的是一個字符串(先執行 typeof 再比較),這樣就可以與字符串"undefined"進行直接比對,如果 somevar 這個變量存在,則執行if條件語句。實際上是在用 typeof 測試一個變量是否已經被初始化(或者說測試變量值是否爲 undefined )
> parseInt('123');
123
> parseInt('1abc123');
1
> parseInt('abc123');
NaN
> parseInt('FF',10);
NaN
> parseInt('FF',16);
255
> parseInt('377');
377
> parseInt('0377');
255
> parseInt('0x377');
887
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
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;
}