1. 包裝對象
非空對象可以添加屬性或者方法
var arr = [];
arr.a = 10;
alert(arr.a);
但是基本數(shù)據(jù)類型也有自身的方法和屬性五督,這些屬性是如何調(diào)用的呢拆又?
var str = "abc";
str.charAt(1);
當(dāng)嘗試去調(diào)用簡單類型的屬性或者方法的時候,這個數(shù)據(jù)類型會找到自身的構(gòu)造函數(shù)身上的屬性或者方法,返回查找的屬性或方法之后,構(gòu)造函數(shù)自動銷毀。這個過程叫做包裝對象。
那么上面的字符串調(diào)用方法的過程就是這樣:
var str = "abc";
str.charAt(1);
str -> str的構(gòu)造函數(shù) String -> 找到charAt方法并調(diào)用 ->銷毀這個new String
2.hasOwnProperty
作用:
判斷參數(shù)是否為自身的私有屬性
語法:
obj/fn.hasOwnProperty(屬性名)洽洁;
返回值為Boolean
如果是調(diào)用者自身的私有屬性,返回true
懒鉴,否則返回false
注意诡挂,判斷的參數(shù)只能是調(diào)用者 自身 的私有屬性,如果是它的原型或者原型鏈上的屬性也會返回false
function fn1(name){
this.name = name;
}
fn1.prototype.age = function(){
console.log(1);
}
var f1 = new fn1('cat');
console.log(fn1.hasOwnProperty('name')); //true
console.log(fn1.hasOwnProperty('age')); //false
console.log(fn1.prototype.hasOwnProperty('age')) //true
3.constructor
作用:
查看某個對象的構(gòu)造函數(shù)是誰
當(dāng)一個對象實例化之后临谱,瀏覽器會為它自動加上這個屬性璃俗。
constructor
很容易被修改。當(dāng)構(gòu)造函數(shù)的原型賦值對象的時候悉默,那么constructor
就被修改了城豁,指向Object
這個方法可以手動修正constructor
指向(指向正確的構(gòu)造函數(shù)的名稱)。
4. instanceof
作用:
這是一個二元運(yùn)算符
可以用來判斷左邊的實例化對象是不是右邊的構(gòu)造函數(shù)構(gòu)造出來的
語法:
t2 instanceof t1
t2:實例化對象
t1:構(gòu)造函數(shù)
返回值:
返回一個Boolean抄课,如果t2是t1構(gòu)造出來的唱星,返回true
雳旅,否則返回false
5. call,apply
這兩個方法都是改變函數(shù)中this的指向
語法:
函數(shù)名.call(this指向的對象间聊,函數(shù)的參數(shù)1攒盈,函數(shù)的參數(shù)2,···函數(shù)的參數(shù)n)哎榴;
函數(shù)名.apply(this指向的對象型豁,[函數(shù)的參數(shù)1,函數(shù)的參數(shù)2尚蝌,···,函數(shù)的參數(shù)n])
call
方法有無限個參數(shù)迎变,其中第一個參數(shù)是想要個改變的this指向,剩下的參數(shù)是函數(shù)的實參
apply
方法有2個參數(shù)飘言,其中給第一個參數(shù)是想要改變的那個this指向衣形,第二個參數(shù)是一個數(shù)組,數(shù)組中的每一項數(shù)據(jù)都是函數(shù)的實參
function fn1(a,b){
alert(a+b);
alert(this);
}
fn1.call(document,2,3);
fn1.apply(document,[1,2]);
6.繼承
6.1 拷貝繼承
原理:
屬性繼承 + 方法繼承
如果有一個構(gòu)造函數(shù)init姿鸿,這個時候需要繼承這個init
那么可以創(chuàng)建一個構(gòu)造函數(shù)init2谆吴,調(diào)用init,修改init2的this
指向苛预,這樣就可以完成屬性繼承(使用call
或者apply
方法)纪铺、
那么接下來我們要完成的是方法繼承
如果直接讓
init2.prototype = init.prototype;
由于構(gòu)造函數(shù)的原型是是一個對象,是引用類型數(shù)據(jù)碟渺。由JS的數(shù)據(jù)類型的存放我們可以知道,如果這樣寫突诬,只是把init的prototype
的地址指向復(fù)制給了init2的prototype
苫拍,這樣如果對init2的prototype
進(jìn)行修改的話,init的prototype
也會同步進(jìn)行修改旺隙。因為他們指向的是同一個對象绒极。
所以我們需要使用for··in對init的prototypr
進(jìn)行遍歷,逐一取出它的方法賦值給init2的prototype
蔬捷,這樣就可以是兩個prototype
指向不同的對象垄提,兩個原型互不干涉。這樣就完成了一次拷貝繼承周拐。
function Init(){
this.name = "a";
}
Init.prototype.a = function(){
console.log("這是原型a");
};
//拷貝繼承
function Init2(){
Init.call(this);
}
for(var attr in Init.prototype){
Init2.prototype[attr] = Init.prototype[attr];
}
Init2.prototype.b = function(){
console.log("這回是init2的原型B铡俐,Init1沒有");
};
var in1 = new Init();
var in2 = new Init2();
in2.a();
in2.b();
in1.b(); //這個會報錯
6.2 類式繼承
原理:
原型鏈的查找
現(xiàn)在我們有一個構(gòu)造函數(shù)
function Init(){
this.name = "init1";
}
Init.prototype.a = function(){
console.log("這里是init的a方法");
}
如果我們想要繼承這個構(gòu)造函數(shù)Init的屬性,同時不能使用拷貝繼承妥粟,那么我們可以使用下面這種方法:
首先創(chuàng)建好這個init2
function Init2(){
//這里同樣需要修改init2的this指向來繼承init的屬性
Init.call(this);
}
我們需要創(chuàng)建一個空的函數(shù)
function F(){};
然后修改這個函數(shù)的原型
F.prototyep = Init.prototype;
然后使用new生成這個F的實例化對象
var objF = new F();
最后审丘,我們把這個實例化對象賦值給Init2
的prototype
,并且手動修正constructor
Init2.prototype = objF;
Init2.constructor = Init2;
//實例化對象勾给,調(diào)用
Init2.prototype.b = function () {
console.log("this is Init2.b");
};
var in2 = new Init2();
var in1 = new Init();
in2.a();
in2.b();
in1.b(); //這個會報錯
原理:
使用一個空的構(gòu)造函數(shù)作為橋梁滩报,然后把空的構(gòu)造函數(shù)的實例化對象賦值給需要繼承的構(gòu)造函數(shù)锅知,這樣,當(dāng)調(diào)用in2的a方法的時候脓钾,由于它本身沒有a方法售睹,通過__proto__
去向上找他的構(gòu)造函數(shù)init2.prototype
,但是Init2.prototype = objF
這個實例化對象可训,那么會通過__proto__
去向上找他的構(gòu)造函數(shù)F
和F
的prototype
,但是F.prototype = init.prototype
,這樣就通過原型鏈找到了a方法昌妹。
in2.a -> objF ->objF.__protot__ -> F ->F.prototype ->init.prototype
這個時候,init.prototype和F.prototype指向同一個對象沉噩,init2的prototype和objF指向同一個對象捺宗。
6.3 對象繼承
如果現(xiàn)在有一個對象obj1
var obj1 = {
"name":"momo",
"age":20,
"say":function(){
alert(1);
}
};
利用類式繼承的原理,我們可以完美繼承這個對象川蒙,方法如下
var obj2 = {};
function F(){};
F.prototype = obj1;
var objF = new F;
obj2 = objF;
這個時候調(diào)用obj2.say方法蚜厉,可以alert(1);
給obj2添加新得key也不會影響到obj1
通過原型鏈的查找順序為:
obj2.a -> objF ->objF.__proto__ ->F ->F.prototype ->obj1
7 toString
作用:把數(shù)據(jù)轉(zhuǎn)成字符串。
每個數(shù)據(jù)類型自身都有toString
方法
只要使用alert
彈出引用類型的數(shù)據(jù)畜眨,那么就會調(diào)用toString
方法昼牛。
Array.prototype.toString = function () {
return 'aaa';
};
var arr = [1,2,3];
alert(arr);
但是彈出基本類型就不會調(diào)用toString
方法
如果為數(shù)字類型,那么在toString的括號中寫進(jìn)制康聂,那么就會轉(zhuǎn)成對應(yīng)的進(jìn)制結(jié)果贰健。(轉(zhuǎn)基數(shù))
var num = 255;
var num2 = "11001000"; //200
console.log(num.toString(2)); //11111111
console.log(parseInt(num2,2)); //200
7.1 判斷數(shù)據(jù)類型
1.typeof
typeof 數(shù)據(jù)
缺點(diǎn)是沒辦法判斷數(shù)組的數(shù)據(jù)類型
2.instanceof
數(shù)據(jù) instanceof 數(shù)據(jù)類型(構(gòu)造函數(shù))
3.constructor
數(shù)據(jù).constructor
返回一個數(shù)據(jù)的構(gòu)造函數(shù),但是這個對于判斷ifream中的內(nèi)容有問題
4.Object.prototype.toString.call(數(shù)據(jù))
Object.prototype.toString.call(數(shù)據(jù))
這個方法會返回一個[object 數(shù)據(jù)構(gòu)造函數(shù)]恬汁;
精確返回伶椿,即使在ifream中的數(shù)據(jù)也沒有問題
8.forEach
將一個類數(shù)組轉(zhuǎn)換為數(shù)組
var boxs = document.querySelectorAll("div");
var boxarr = Array.prototype.slice.call(boxs);
//這樣就可以使用forEach來循環(huán)類數(shù)組啦
boxarr.forEach(function(elem,i){
elem.onclick = function(){
alert(i);
}
});
問題:Object.prototype.toString和Array.prototype.slice這兩個都是通過改變this的指向來實現(xiàn)效果,那么Array.prototype.slice的this原來是誰氓侧? 是它的實例化對象脊另? 還是window?