1. 原型深層解讀
2. 函數(shù)的三種角色
- 普通函數(shù):形成一個(gè)私有作用域,形參賦值像樊,預(yù)解釋尤莺,代碼執(zhí)行,內(nèi)存和內(nèi)存釋放生棍;
- 作用域鏈:查找的是變量颤霎,如果變量未定義,則會報(bào)錯;
<script>
//普通函數(shù):當(dāng)調(diào)用時(shí)友酱,不是實(shí)例對象創(chuàng)建調(diào)用晴音,都可以當(dāng)成普通函數(shù)調(diào)用使用;
//注意:普通函數(shù)的this指向缔杉,一般都為window锤躁;
//如果作用域鏈中查找變量時(shí),沒有找到變量則會報(bào)錯或详;如果變量已經(jīng)聲明系羞,但是未賦值,則返回undefined;
function Fn(){
alert(x);//進(jìn)行預(yù)解釋聲明x霸琴,但是還未賦值椒振,所以彈出undefined;
var x=100;
this.x=200;//沒有進(jìn)行實(shí)例對象創(chuàng)建梧乘,所以此時(shí)澎迎,this指向window,即設(shè)置全局變量x為200;
return x;//返回函數(shù)的私有屬性x值选调,為100夹供;
}
var res=Fn();//調(diào)用普通函數(shù);//此時(shí)調(diào)用時(shí)為window調(diào)用学歧,所以函數(shù)定義里面的this指向window;
console.log(x);//此時(shí)輸出的全局量屬性x,為200罩引;
console.log(res);//打印結(jié)果為100;
</script>
- 類:類,prototype,constructor,實(shí)例枝笨,_ proto _,原型鏈(找不到屬性返回undefined)
- 保證函數(shù)名首字母大寫揭蜒,然后實(shí)例對象創(chuàng)建后横浑,才會形成類,函數(shù)里面就會默認(rèn)新建一個(gè)this對象屉更;this指向?qū)嵗龑ο?即 “new 函數(shù)名”賦給的變量)徙融;
- 在函數(shù)定義里設(shè)置的this.屬性名,全都是實(shí)例對象的私有屬性瑰谜,與函數(shù)本身的普通對象特性無關(guān)欺冀;
<script>
//類:只有當(dāng)創(chuàng)建實(shí)例對象的時(shí)候,函數(shù)才會作為類調(diào)用萨脑;
//重點(diǎn):當(dāng)創(chuàng)建實(shí)例對象的時(shí)候隐轩,會對普通函數(shù)角色產(chǎn)生沖突;
//1)函數(shù)定義中的this指向則為實(shí)例對象渤早,返回值职车,會默認(rèn)為this對象,如果函數(shù)定義中存在返回值,判斷返回值的類型悴灵,a)返回值為基本數(shù)據(jù)類型扛芽,則無視,b)返回值為引用數(shù)據(jù)類型积瞒,則對實(shí)例對象重新賦值為此引用數(shù)據(jù)類型的地址川尖;
function Fn(){
alert(x);//進(jìn)行預(yù)解釋聲明x,但是還未賦值茫孔,所以彈出undefined空厌;
var x=100;
this.x=200;//實(shí)例創(chuàng)建后,此時(shí)this指向f1對象银酬;給f1對象設(shè)置私有屬性x,屬性值為200嘲更;
return x;
//此時(shí)的返回值無效,因?yàn)槭腔緮?shù)據(jù)類型揩瞪,類會返回默認(rèn)的對象this;
//但是如果return返回的是引用數(shù)據(jù)類型赋朦,即 return {},此時(shí)就會重新賦給f1一個(gè)地址李破,里面為空的鍵值對宠哄;f1.x結(jié)果則為undefined;
}
var f1=new Fn();//實(shí)例創(chuàng)建,為一個(gè)對象嗤攻,
alert(f1.x);//結(jié)果為200毛嫉;
console.dir(f1);//此時(shí)f1為對象,里面有屬性妇菱,私有屬性x,屬性值為200承粤;屬性_ _proto_ _,屬性值為Object對象;即類Fn的prototype屬性的地址;
</script>
- 普通對象:只要是函數(shù)闯团,就具有普通對象的特征:屬性和方法辛臊;與類無關(guān);
- 定義函數(shù):function add(){}房交;函數(shù)里面可以放鍵值對彻舰,也可以通過“add.屬性名”來添加對象屬性
- 對象屬性的刪除:delete 對象.屬性名;此處刪除函數(shù)內(nèi)的對象屬性候味,用:delete 函數(shù)名.屬性名刃唤;
<script>
//注意:函數(shù)當(dāng)做普通對象角色與類產(chǎn)生的實(shí)例對象互不干擾,沒有任何關(guān)系白群;
function Fn(){
alert(x);//進(jìn)行預(yù)解釋聲明x尚胞,但是還未賦值,所以彈出undefined川抡;
var x=100;
this.x=200;
return x;
}
Fn.x=300;//此時(shí)將Fn作為普通對象辐真,給其添加一組鍵值對须尚,屬性名為x,屬性值為300侍咱;
console.log(Fn.x);//輸出結(jié)果為300耐床;此時(shí)打印的是Fn對象中的x屬性名的屬性值;
var f1=new Fn();
console.log(f1.x);//輸出結(jié)果為200楔脯;此時(shí)打印的是f1實(shí)例對象中x屬性名的屬性值撩轰;
</script>
- 函數(shù)的三種角色的面試題
<script>
function Foo() {
getName=function () { alert(1);};
return this;
}
Foo.getName=function () { alert(2);};//將函數(shù)當(dāng)做對象添加屬性名及屬性值;
Foo.prototype.getName=function(){ alert(3);};//將函數(shù)作為類昧廷,添加公共的屬性及方法堪嫂,只有當(dāng)函數(shù)被作為類調(diào)用時(shí),才進(jìn)行這步木柬;
var getName=function (){alert(4);};//預(yù)解釋:只聲明不賦值皆串,var getName;
function getName(){ alert(5);}//預(yù)解釋:聲明加定義,由于名字一樣眉枕,所以會覆蓋var的聲明恶复;
Foo.getName();//結(jié)果為2;考察的是將函數(shù)作為普通對象使用速挑;
getName();//結(jié)果為4谤牡;考察的是全局變量;
Foo().getName();//結(jié)果為1姥宝;
//1)考察函數(shù)執(zhí)行中自己私有作用域中查找不到變量翅萤,去上級作用域中查找;并重新賦值腊满;考察作用域鏈套么;
// 2)考察的是Foo()函數(shù)執(zhí)行中的this指向,為window;再進(jìn)行window.getName()執(zhí)行糜烹;
getName();//結(jié)果為1违诗;此時(shí)的全局變量已經(jīng)被重新賦值,考察全局變量疮蹦;
new Foo.getName();//結(jié)果為2;考察:先執(zhí)行Foo.getName茸炒,對象屬性愕乎,在通過new把Foo.getName看作類;
new Foo().getName();//結(jié)果為3壁公;考察:先執(zhí)行new Foo(),把Foo看作類感论;然后再查找實(shí)例對象中的getName屬性,沒有去所屬類的原型中查找紊册;考察原型鏈比肄;
</script>
3. 對象屬性判斷
-
in
:判斷某個(gè)屬性是否在對象上幢踏;(包含了私有+共有);
- 代碼:
"x" in f1
许师,其中x為屬性名房蝉,f1為對象;注意屬性名不是變量枯跑,要加雙引號惨驶;
-
hasOwnProperty
:判斷某個(gè)屬性是否為對象的私有屬性;
- 代碼:
f1.hasOwnProperty("x")
:其中x為屬性名敛助,f1為對象粗卜;
<script>
//注意:屬性名不是變量,在判斷是要加雙引號纳击;
function Fn(){
this.x=100;//構(gòu)造函數(shù)中都是私有屬性
}
Fn.prototype.showX=function(){};//公有屬性
var f1=new Fn();
console.log("x" in f1);//結(jié)果為true,說明x為f1的屬性续扔;
console.log("showX" in f1);//結(jié)果為true,說明showX為f1的屬性;
console.log(f1.hasOwnProperty("x"));//結(jié)果為true焕数,說明x為f1的私有屬性纱昧;
console.log(f1.hasOwnProperty("showX"));//結(jié)果為false,說明showX不是f1的私有屬性堡赔;
</script>
- 實(shí)例:創(chuàng)建一個(gè)方法识脆,判斷某個(gè)屬性是否為對象身上的公有屬性
- 思路:
- 需求:判斷 某個(gè)屬性 是否為 對象 身上的公有屬性
- 參數(shù):屬性 attr,對象 obj;
- 返回值:布爾值;
- 考點(diǎn):
&&
邏輯運(yùn)算符的運(yùn)用善已,用于判斷灼捂,當(dāng)前面的語句為真時(shí),才會執(zhí)行后面的代碼换团,返回值為后面的代碼悉稠;
- 代碼:
alert(1 && 5)
結(jié)果彈出的是5;
<script>
function Fn(){
this.x=100;//構(gòu)造函數(shù)中都是私有屬性
}
Fn.prototype.showX=function(){};//公有屬性
var f1=new Fn();
//需求:判斷 某個(gè)屬性 是否為 對象 身上的公有屬性
//參數(shù):屬性 attr,對象 obj;
//返回值:布爾值艘包;
function hasPubProperty(attr,obj){
//是屬性并且不是私有屬性
return attr in obj ? !obj.hasOwnProperty(attr): (attr+"不是實(shí)例對象上的屬性");
//重點(diǎn):&&邏輯判斷語句的作用是判斷條件的猛,只有在前面的語句為真的情況下耀盗,才會執(zhí)行后面語句,并返回后面語句的值卦尊;
}
console.log(hasPubProperty("showX",f1));//結(jié)果為true
console.log(hasPubProperty("show",f1));//結(jié)果為fasle;
console.log(hasPubProperty("showY",f1));//結(jié)果為"showY不是實(shí)例對象上的屬性";
</script>
- 第二種方法:給Fn()添加一個(gè)公共的屬性叛拷,模仿hasOwnProperty();
<script>
function Fn(){
this.x=100;//構(gòu)造函數(shù)中都是私有屬性
}
Fn.prototype.showX=function(){};//公有屬性
var f1=new Fn();
Fn.prototype.hasPubProperty=function (attr) {
return attr in this ? !this.hasOwnProperty(attr): (attr+"不是實(shí)例對象上的屬性");
};
console.log(f1.hasPubProperty("x"));//結(jié)果為false;
console.log(f1.hasPubProperty("showX"));//結(jié)果為true;
console.log(f1.hasPubProperty("y"));//結(jié)果為"y不是實(shí)例對象上的屬性";
</script>
4. 對象的遍歷方法for-in解讀
- 知識點(diǎn):for...in..方法遍歷對象,可以拿到對象身上自定義的私有屬性和對象原型上的公有屬性猫牡,還能拿到基類原型上自定義的屬性胡诗;但是不包含系統(tǒng)自帶的屬性和方法;
<script>
//自定義的對象
var obj={
name:"guobin",
age:26
};
function Fn(){
this.x=100;
}
Fn.prototype=obj;//將自定義的obj對象地址賦給Fn的prototype對象淌友,則obj里面的屬性和方法為公有的屬性和方法煌恢;
Object.prototype.aa=function () {};//此時(shí)Object.prototype為f1的祖級原型;
var f1=new Fn;//創(chuàng)建實(shí)例對象震庭,F(xiàn)n后面的()瑰抵,可以省略;
for(var attr in f1){
console.log(attr+":"+f1[attr]);
/*打印出來的結(jié)果為
* x: 100
* name: "guobin"
* age: 26
* aa: function () {}//能夠打印出祖級原型中自定義的屬性和方法器联;
* */
}
//總結(jié):for...in..可以拿到對象身上自定義的屬性和方法二汛,其中包含自定義的私有和公有屬性;但是不包含系統(tǒng)自帶的屬性和方法拨拓;
</script>
5.Object.prototype對象上的系統(tǒng)屬性方法
-
isPrototypeOf
:判斷前一個(gè)對象是否在后一個(gè)對象的原型鏈上肴颊;返回布爾值;
-
hasOwnProperty
:判斷某個(gè)屬性是否為對象身上的私有屬性渣磷;返回布爾值婿着;
-
propertyIsEnumerable
:枚舉,它的作用與hasOwnProperty類似醋界;返回布爾值竟宋;
-
toString()
:基類Object.prototype上的toString()
- 作用:打印出this所屬的類的詳細(xì)信息;
- 通過call來改變上述this形纺,用于打印其他類型的所屬類的詳細(xì)信息丘侠;代碼
Object.prototype.toString.call(arg)
;
- 打印數(shù)組的所屬類詳細(xì)信息:
Object.prototype.toString.call(ary)
,輸出結(jié)果為字符串"[object Array]"
;
- 打印字符串的所屬類詳細(xì)信息:
Object.prototype.toString.call(str)
,輸出結(jié)果為字符串"[object String]"
;
- 實(shí)例驗(yàn)證:
- 考點(diǎn):Object.prototype對象上toString方法作用是輸出this所屬類的詳細(xì)信息逐样;
- 只要是Object.prototype原型鏈上的對象都能使用蜗字,即:通過"_ proto _"屬性能夠找到它;
- 但是要注意的是脂新,Object.prototype對象是最終的原型秽澳;如果在查找上級原型過程中,碰到了toString戏羽,就不會去尋找Object.prototype上的toString方法;
- 數(shù)組對象所屬類Array上就存在toString方法楼吃,所以不會再繼續(xù)向上查找始花;
- 如何打印數(shù)組對象所屬類的信息妄讯;就通過call方法,改變this指向酷宵;
- 只有對象才有屬性和方法亥贸,而基本數(shù)據(jù)類型是沒有屬性和方法的;
- 字符串str浇垦,正常情況下是不能執(zhí)行屬性和方法的炕置;
- 執(zhí)行
str.chaAt()
方法:str屬于基本數(shù)據(jù)類型,在使用方法時(shí)男韧,不能使用朴摊,它默認(rèn)會去找它所屬包裝類,字符串類此虑,來執(zhí)行該屬性方法甚纲,然后再回到基本數(shù)據(jù)類型;
<script>
var obj={};
var ary=[1,2,3,4,5];
var str="12345";
console.log(ary.toString());//結(jié)果為字符串"1,2,3,4,5"朦前;說明運(yùn)行的是Array類自身的toString方法介杆;
console.log(Object.prototype.toString.call(ary));//結(jié)果為字符串"[object Array]"
//通過call將Object.prototype.toString()函數(shù)中的this改變?yōu)閍ry,所以就會輸出ary的所屬類信息韭寸;
console.log(obj.toString());//結(jié)果為字符串"[object Object]";當(dāng)obj對象查找toString屬性時(shí)春哨,自己對象上沒有,就會向自己所屬類的原型上查找恩伺,也就是Object.prototype對象上查找赴背;所以能夠使用;
console.log(Object.prototype.toString.call(str));//結(jié)果為字符串"[object String]";
//下列為所有數(shù)據(jù)類型的詳細(xì)信息莫其;
console.log(obj.toString.call(str));//結(jié)果為字符串"[object String]";
console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]
</script>
6.Function.prototype對象上的系統(tǒng)屬性解讀
- 函數(shù)不管是否添加new癞尚,相對于Function類來說,自身都是一個(gè)實(shí)例對象乱陡,具有_ ptoto _屬性浇揩,它指向該實(shí)例對象所屬函數(shù)的原型,即Function.prototype對象憨颠;
- call屬性是Function.prototype對象中的公有屬性胳徽,當(dāng)函數(shù)使用call()方法時(shí),會先在自身的對象中查找爽彤,查找不到就在所屬類的原型中查找使用养盗;
- 只有函數(shù)才能使用call()方法;
- call屬性的參數(shù)有兩個(gè):
- 參1:把call前面的函數(shù)(實(shí)例對象this)中的this關(guān)鍵字改成call的第一個(gè)參數(shù)适篙;
- 參2:給call前面的函數(shù)從左往右一個(gè)個(gè)的傳參往核;
- 代碼:`f1.call(obj,30,40);
<script>
/* Function.prototype.call=function(arg1){
//1.把this(實(shí)例)函數(shù)中的"this"關(guān)鍵字改成call的第一個(gè)參數(shù)
//2.把call前面的函數(shù)(實(shí)例this)立即執(zhí)行;
this();
}*/
function f1() {
alert(1);
}
function f2() {
alert(2);
}
f1.call(f2);//結(jié)果彈出1嚷节;解讀:call會把f1中的this關(guān)鍵字(字符串)改成f2聂儒;但是此時(shí)f1中無this關(guān)鍵字虎锚;所以執(zhí)行后彈出1;
f1.call.call(f2);//結(jié)果彈出2衩婚;解讀:call會把f1.call函數(shù)中的this關(guān)鍵字改成f2窜护;然后再執(zhí)行會執(zhí)行f2函數(shù);彈出2非春;
f1.call.call.call.call(f2);//結(jié)果彈出2柱徙;解讀:只要兩個(gè)call以上,彈出的結(jié)果都為2奇昙;
</script>
7. 改變this指向的函數(shù)
- call(arg1,arg2,arg3,arg4....),如call(obj,2,3,4)
- call的一個(gè)參數(shù)用來改變call前面的函數(shù)中的this關(guān)鍵字护侮;如果不需要傳第一個(gè)參數(shù),則傳null;
- call從第二個(gè)參數(shù)開始敬矩,相當(dāng)于給call前面的函數(shù)從左往右一個(gè)個(gè)的賦值概行;
- call當(dāng)改完this指向,傳完參數(shù)后弧岳,立即執(zhí)行凳忙;
- apply(arg1,arg2),只有兩個(gè)參數(shù),其中arg2為數(shù)組禽炬,arg2可傳可不傳涧卵,如apply(obj,[2,3,4])
- arg1用于改變this指向,具體跟call一樣腹尖;如果不需要傳第一個(gè)參數(shù)柳恐,則傳null;
- 區(qū)別:apply的第二個(gè)參數(shù)arg2是個(gè)數(shù)組,存放所有需要給形參的值热幔;
- 雖然apply的第二個(gè)參數(shù)是個(gè)數(shù)組乐设,但是對于形參來說,也是按照數(shù)組元素從左往右一個(gè)個(gè)給形參賦值绎巨;
<script>
var obj={};
function fn(n,m,o){
console.log(this+(n+m+o));
}
//fn.call(obj,2,3,4);//輸出的結(jié)果為:[object Object]9近尚;其中obj改變this關(guān)鍵字,后面參數(shù)依次傳入實(shí)參场勤;
fn.apply(obj,[2,3,4]);//輸出的結(jié)果也為:[object Object]9戈锻;其中obj改變this關(guān)鍵字,數(shù)組中的元素按照順序依次傳入實(shí)參和媳;
</script>
- bind():預(yù)處理機(jī)制
- 相同:bind的傳參形式跟call一樣格遭;如bind(obj,2,3,4)
- 注意:bind屬于預(yù)處理機(jī)制,當(dāng)調(diào)用bind()函數(shù)的時(shí)候留瞳,會修改bind前面函數(shù)中的this關(guān)鍵字和傳入實(shí)參拒迅,但是不會立即執(zhí)行;會返回一個(gè)函數(shù),當(dāng)需要執(zhí)行函數(shù)的時(shí)候坪它,再重新執(zhí)行新函數(shù)骤竹;
- 兼容性:IE8及其以下瀏覽器不支持bind屬性;
8. 實(shí)例
- 需求:求數(shù)組中元素的最大值和最小值
- 思路1:數(shù)組sort排序
var ary=[12,3,8,98,25,34,45];
ary.sort(function (a,b) {
return a-b;
});//升序排列
var max=ary[ary.length-1];
var min=ary[0];
console.log(max);
console.log(min);
- 思路2:假設(shè)法
var ary=[12,3,8,98,25,34,45];
var max=ary[0];
var min=ary[0];
for(var i=0;i<ary.length;i++){
if(ary[i]>max){
max=ary[i];
}
if(ary[i]<min){
min=ary[i];
}
}
console.log(max);
console.log(min);
- 思路3:對象的特性:當(dāng)對象的屬性名為數(shù)字時(shí)往毡,默認(rèn)按照從小到大的順序排列;
var ary=[12,3,8,98,25,34,45];
var obj={};
for(var i=0; i<ary.length; i++){
obj[ary[i]]=ary[i];
}
console.log(obj);
var max=obj[98];
var min=obj[3];
console.log(max);
console.log(min);
- 思路4:利用Math.max()和toString()方法和eval()方法
/*考點(diǎn):1)Math.max(m,n,v,d)為JS代碼執(zhí)行靶溜;
2)toString()方法將數(shù)組轉(zhuǎn)化為字符串开瞭,即ary[2,3,4,5,6],轉(zhuǎn)化為"2,3,4,5,6";其中","為字符罩息;
3)eval()方法嗤详,將字符串作為JS代碼執(zhí)行;*/
var ary=[12,3,8,98,25,34,45];
var str=ary.toString();//將數(shù)組轉(zhuǎn)化為字符串瓷炮;
var max=eval("Math.max("+str+")");//將Math.max()作為字符串與str相連葱色,然后用eval()方法轉(zhuǎn)化為JS代碼執(zhí)行;
var min=eval("Math.min("+str+")");
console.log(max);
console.log(min);
- 思路5:利用Math.max函數(shù)和apply()方法
<script>
/*考點(diǎn):1)利用Math.max(m,n,b,d,v,d)方法可以快速找出最大值娘香;
2)但是數(shù)組不能直接當(dāng)做實(shí)參傳入苍狰,所以利用apply()方法,讓ary數(shù)組中的元素依次作為實(shí)參傳入烘绽;*/
/*在函數(shù)定義時(shí) Math.max(m,n,b,d,v,d){};其中Math.max為函數(shù)名淋昭,只要是函數(shù)都可以使用apply()方法
當(dāng)傳入ary時(shí),會把數(shù)組中的元素按順序賦給m,n,b,d,v,d這些形參安接;然后執(zhí)行取最大值的方法*/
var ary=[12,3,8,98,25,34,45];
var max=Math.max.apply(null,ary);//不需要改變this指向翔忽,則傳入null;
var min=Math.min.apply(null,ary);
console.log(max);
console.log(min);
</script>