原型和閉包
一切皆對象
一切皆對象(類型值除外)
undefined, number, string, boolean屬于簡單的值類型
函數、數組、對象、new Number(10)都是對象。他們都是引用類型
Null是基本數據類型,不是引用數據類型
基本數據類型的值就是它本身的值,引用數據類型是存放的對這個對象引用的指針,Null本身的值就是Null,所以不是引用類型
不同的對象在底層都表示為二進制,在 JavaScript 中二進制前三位都為 0 的話會被判
斷為 object 類型, null 的二進制表示是全 0,自然前三位也是 0,所以執行 typeof 時會返回“ object ”。
http://www.cnblogs.com/xiaoheimiaoer/p/4572558.html
判斷值類型的類型用typeof,引用類型的類型用instanceof
javascript為弱類型,里一切皆是對象,對象里面都是屬性,而它的方法也是一種屬性,用鍵值對的形式來表示,且javascript中的對象可以隨意擴展屬性,沒有class的約束。
簡單示例
var a1={b:10,c:function(n){alert(this.a+n);},d:{car:"000000",long:"五十"}
};
雖然函數是一種對象,但函數和對象那個之間更像是一種相互生成的關系
對象都由函數來創建,我們平時所寫的var let等其實是一種“語法糖”其本質還是函數
prototype原型
每一個函數都有一個屬性叫prototype
prototype的屬性值是一個對象,只有一個默認的叫constructor屬性,指向函數本身,而還我們可以自己采用自定義的形式在prototype中新增自己的屬性
function F1(){}
F1.prototype.age="1982";
F1.prototype.sex=function(){return "man";
};
而這樣做的作用就要聯系到jQuery了
var $("div");
$div.attr('myAge','18');
上面代碼中,$('div')返回的是一個對象,而對象被函數創建的而他的實現過程如下
myjQuery.prototype.attr=function(){};
$('div')=new myjQuery();
其本質就是
function F1(){}
F1.prototype.age="1982";
F1.prototype.sex=function(){return "man";
};
var f2=new F1();
console.log(f2.age);
console.log(f2.sex());
F1是一個函數,f2對象是從F1函數new出來的,這樣f2對象就可以調用F1.prototype中的屬性。
因為每個對象都有一個隱藏的屬性——“proto”,這個屬性引用了創建這個對象的函數的prototype。即:
f2.__proto__ === F1.prototype
這里的"______proto_______"成為“隱式原型”
__ proto__ 原型
每個函數function都有一個prototype,即原型。同時每個對象都有一個__ proto__
其指向創建該對象的函數的prototype
個__ proto __是一個隱藏的屬性,javascript不希望開發者用到這個屬性值,有的低版本瀏覽器甚至不支持這個屬性值
自定義函數的prototype都是被Object創建,所以它的_ _proto__指向的就是Object.prototype
但是Object.prototype確實一個特例——它的__ proto__指向的是null,切記切記
函數也有原型
function fn(x,y){return x+y;
};
console.log(fn(10,20));var f1= new Function("x","y","return x+y;");
console.log(f1(8,7));
第二種為new Function僅作理解使用,Function作為函數,也是一種對象,所以也有 __ proto__ 屬性,而函數本身是被Function創建,所以Function是被自身創建,他的 __ proto __指向了自身的Prototype。
同理Function.prototype指向的對象,它的__ proto __也指向Object.prototype
instanceof
instanceof 用于對引用類型的判斷
Instanceof的判斷隊則是:
function Foo(){}
var f1=new Foo();console.log(f1 instanceof Foo);//true
console.log(f1 instanceof Object);//true
設第一個變量為A,設第二個函數為B
沿著A的__ proto__這條線來找,同時沿著B的prototype這條線來找,如果兩條線能找到同一個引用,即同一個對象,那么就返回true。如果找到終點還未重合,則返回false
將前面的整合為整體如下圖,通過此圖,可以捋清為何返回值為true了
其實instanceof表示的就是一種繼承關系,或者原型鏈的結構
繼承
javascript中的繼承是通過原型鏈來體現的
js是原型繼承,C#是類型繼承。
原型繼承比類型繼承更加靈活,但是又不如類型繼承可控
function foo(){}
var f1=new foo();f1.a=10;Foo.prototype.a=100;
Fpp.prototype.b=200;console.log(f1.a);//10
console.log(f1.b);//200
f1是Foo函數new出來的對象f1.a是f1對象的基本屬性而非。b從Foo.prototype得來
f1.__ proto __指向的是Foo.prototype
**訪問一個對象的屬性時,先在基本屬性中查找,如果沒有,再沿著__ proto__這條鏈向上找,這就是原型鏈 **
訪問f1.b時,f1的基本屬性中沒有b,于是沿著__ proto__找到了Foo.prototype.b
可使用hasOwnProperty區分一個屬性是基本屬性還是原型
原型的靈活性
對象屬性可以隨時改動
在對象或函數new出來后可以隨時加屬性
繼承方法不合適也可以隨時修改
缺少你所要用的方法時,可以隨時去創建
執行上下文
- 變量、函數表達式——變量聲明,默認賦值為undefined;
- this——賦值;
- 函數聲明——賦值;
這三種數據的準備情況我們稱之為“執行上下文”或者“執行上下文環境”。
函數每被調用一次,都會產生一個新的執行上下文環境。因為不同的調用可能就會有不同的參數
另外一點不同在于,函數在定義的時候(不是調用的時候),就已經確定了函數體內部自由變量的作用域
大白話理解:在執行代碼之前,把將要用到的所有的變量都事先拿出來,有的直接賦值了,有的先用undefined占個空
了解了執行上下文環境中的數據信息,你就不用再去死記硬背那些可惡的面試題了
this
? 在函數中this到底取何值,是在函數真正被調用執行的時候確定的,函數定義的時候確定不了,因為this的取值是執行上下文環境的一部分,每次調用函數,都會產生一個新的執行上下文環境。
構造函數
所謂構造函數就是用來new對象的函數。其實嚴格來說,所有的函數都可以new一個對象,但是有些函數的定義是為了new一個對象,而有些函數則不是。另外注意,構造函數的函數名第一個字母大寫(規則約定)
函數作為對象的一個屬性
如果函數作為對象的一個屬性時,并且作為對象的一個屬性被調用時,函數中的this指向該對象
函數用call或者apply調用
當一個函數被call和apply調用時,this的值就取傳入的對象的值。
全局 & 調用普通函數
全局環境下,this永遠是window
普通函數在調用時,其中的this也都是window
其實,不僅僅是構造函數的prototype,即便是在整個原型鏈中,this代表的也都是當前對象的值。
執行上下文棧
? 執行全局代碼時,會產生一個執行上下文環境,每次調用函數都又會產生執行上下文環境。當函數調用完成時,這個上下文環境以及其中的數據都會被消除,再重新回到全局上下文環境。處于活動狀態的執行上下文環境只有一個。
其實這是一個壓棧出棧的過程——執行上下文棧
作用域
通常大家認為“javascript沒有塊級作用域”。所謂“塊”,就是大括號“{}”中間的語句
javascript除了全局作用域之外,只有函數可以創建的作用域。
? 所以,我們在聲明變量時,全局代碼要在代碼前端聲明,函數中要在函數體一開始就聲明好。除了這兩個地方,其他地方都不要出現變量聲明。而且建議用“單var”形式
作用域有上下級的關系,上下級關系的確定就看函數是在哪個作用域下創建的
作用域最大的用處就是隔離變量,不同作用域下同名變量不會有沖突
作用域在函數定義時就已經確定了。而不是在函數調用時確定
? 作用域只是一個“地盤”,一個抽象的概念,其中沒有變量。要通過作用域對應的執行上下文環境來獲取變量的值。同一個作用域下,不同的調用會產生不同的執行上下文環境,繼而產生不同的變量的值。所以,作用域中變量的值是在執行過程中產生的確定的,而作用域卻是在函數創建時就確定了。
所以,如果要查找一個作用域下某個變量的值,就需要找到這個作用域對應的執行上下文環境,再在其中尋找變量的值
自由變量到作用域鏈
將變量在作用域外聲明在作用域中調用的變量為自由變量
要到創建這個函數的那個作用域中取值——是“創建”,而不是“調用”,無論函數將在哪里調用
閉包
閉包,可理解為:函數作為返回值,函數作為參數傳遞
有些情況下函數在被調用完后其上下文環境不會被銷毀,如返回值唯一個函數,函數的特別之處在于可以創建一個獨立的作用域
使用閉包會增加內容開銷,只有在所有閉包相關作用域執行完畢后才會銷毀
圖片來源于網絡