一、全局做用域:當瀏覽器加載HTML頁面的時候,提供了一個全局js代碼執行的環境javascript
二、預解釋(變量提高)php
在當前的做用域中,js代碼執行以前,瀏覽器首先會默認的吧全部帶有var和function的進行提早聲明或定義java
1)理解聲明和定義編程
var num=12;聲明(declare) : var num;->告訴瀏覽器在全局做用域中有一個num的變量了數組
若是一個變量只是聲明可是尚未賦值,默認值是undefined瀏覽器
定義(defined):num=12->給咱們的變量進行賦值閉包
2)對於帶var和function關鍵字的再預解釋的時候操做是不同的編程語言
var ->在預解釋的時候只是提早聲明不定義function->在預解釋的時候提早聲明和定義都完成了模塊化
3)預解釋只發生在當前的做用域下,例如:開始只對window下的進行預解釋,只有在函數執行的時候纔會對函數中的進行預解釋函數
三、JS中內存的分類
棧內存
:用來提供一個供js代碼執行的環境->做用域
堆內存
:用來存儲引用數據類型的值->對象存儲的是屬性名和屬性值,函數存儲的是代碼字符串。
四、若是區分私有變量和全局變量?
1)在全局做用域下聲明(預解釋的時候)的變量是全局變量
2)在私有做用域中聲明的變量和函數的形參都是私有變量
在私有做用域中,咱們代碼執行的時候遇到了一個變量,首先咱們肯定它是不是私有變量,若是是私有變量,那麼和外面沒有任何的關係,若是不是使用的,則往當前做用域的上級做用域進行查處,若是上級做用域也沒有則繼續查找,一直找到window爲止...
做用域鏈
六、當函數執行的時候(直接目的:讓函數體中的代碼執行),首先會造成一個新的私有做用域,而後按照以下的步驟執行:
- 若是有形參,先給形參賦值
- 進行私有做用域的預解釋
- 私有做用域中的代碼從上到下的執行
函數造成一個姓的私有的做用域保護了裏面的私有變量不受外界的干擾(外面修改不了私有的,私有的也修改不了外面的),咱們管這種機制叫作
閉包
七、在全局做用域中,帶var和不帶var的關係
區別:帶var的能夠進行預解釋,因此在賦值的前面執行不會報錯;不帶var的是不能進行預解釋的,在前面執行會報錯
八、預解釋是毫無節操的一種機制
1)預解釋的時候無論你的條件是否成立,都要把帶var的進行提早聲明
if(!("num") in window){ var num=12; } console.log(num);
2)預解釋的時候只預解釋「=」左邊的,右邊的是值,不參與預解釋匿名函數之函數表達式:把函數定義的部分但當作一個值賦值給咱們的變量/元素的某個事件
//window下的預解釋:var fn; fn();//Uncaught TypeError: fn is not a function var fn=function(){ console.log("ok"); }
3)執行函數定義的那個function在全局做用域下不進行預解釋,當前代碼執行到這個位置的時候定義和執行一塊兒完成了自執行函數:定義和執行一塊兒完成了
(function(){})(100);
4)函數體中return下面的代碼雖然再也不執行了,可是須要進行預解釋;return後面跟着的都是咱們返回的值,因此不進行預解釋
function fn(){ console.log(num); retrun function(){ }; var num=100; } fn();//undefined;
5)在預解釋的時候,若是名字已經聲明過了,不須要從新的聲明,可是須要從新的賦值;(在JS中若是變量的名字和函數的名字重複了,也算衝突了)
var fn=13; function fn(){ console.log("ok"); } console.log(fn);//13 //預解釋的時候先預解釋var fn;function fn{console.log("ok")} //而後 fn=13->給fn賦值 //最後控制檯輸出的是13
fn(); function fn(){console.log(1)}; fn(); var fn=10; fn(); function fn(){console.log(2)} fn(); //window 預解釋: /*1)聲明+定義 fn=xxxfff000; 聲明var fn(不須要從新聲明) 聲明(不重複進行)+定義 fn=xxxfff111; 預解釋完成後此時的fn變量指向的是堆內存地址是xxxfff111 即:存儲「console.log(2)」代碼字符串的堆內存空間 2)代碼由上到下執行 fn()//2 fn();//2 fn=10; fn();//Error:fn is not a function js代碼出錯,在沒有任何特殊處理的狀況下後面的代碼不執行 */
九、若是查找當前做用域的上一級做用域?
看當前函數是在那個做用域下定義的,那麼他的上級做用域就是誰->和函數在哪執行沒有任何關係
堆內存釋放
對象數據類型或者函數數據類型在定義的時候首先都會開闢一個堆內存,堆內存有一個引用的地址,若是外面有變量等知道這個地址,咱們就說這個內存被佔用了,就不能銷燬咱們若是想要堆內存釋放/銷燬,只須要把全部引用他的變量賦值爲null便可,若是當前的堆內存沒有任何東西被佔用,那麼瀏覽器會在空閒的時候把他銷燬
var obj1={name:"candy"} var obj2=obj1; obj1=null; obj2=null;
棧內存的銷燬
1)全局做用域只有當前頁面關閉的時候全局做用域纔會被銷燬
2)私有做用域(只有函數執行纔會產生私有做用域)
通常狀況下,函數執行會造成一個新的私有的做用域,當私有做用域中的代碼執行完成後,咱們當前做用域都會主動的進行釋放和銷燬
可是仍是存在特殊狀況的:
當前私有做用域中的部分被做用域之外的東西佔用了,那麼當前的這個做用域就不能銷燬了
- 函數執行返回了一個引用數據類型的值,而且在函數的外面被一個其餘的東西接收了,這種狀況下通常造成的私有做用域都不會銷燬
- 在一個私有做用域中給DOM元素的事件綁定方法,通常狀況下咱們的使用做用域都不能銷燬
- 下述狀況不能當即銷燬:fn返回的函數沒有被其餘的東西佔用,可是還須要執行一次,因此暫時不銷燬,當返回的值執行完成後,瀏覽器會在空閒的時候把他銷燬
function fn(){ var num=100; return function(){ } } var f=fn();//fn執行造成的這個私有做用域就不能被銷燬
var oDiv=document.getElementById("div1"); ~function(){ oDiv.onclick=function(){ } }()//當前自執行函數造成的這個使用做用域也不能銷燬
function fn(){ var num=100; return function(){ } } fn()();//首先執行fn,返回一個小函數對應的內存地址,而後緊接着讓返回的小函數執行
咱們在js中主要研究的都是函數中的thisJS中this代碼的是當前行爲執行的主體;JS中的context表明的是當前行爲執行的環境(區域)
this是誰和函數在哪定義的和在哪執行的都沒有任何的關係*
如何區分this?
- 函數執行,首先看函數名前面是否有「.」,有的話 「.」前面是誰this就是誰,沒有的話就是window
- 自執行函數中的this永遠是window
- 給元素的某一個事件綁定方法,當事件觸發的時候,執行對應的方法,方法中的this是當前的元素
一、對象數據類型的做用:把描述同一個實物(同一個對象)的屬性和方法放在一個內存空間下,起到了分組的做用,這樣不一樣事物以前的屬性名即便相同也不會發生衝突->咱們把這種分組編寫代碼的模式叫作 單例模式
//在單例模式中咱們把person1或者person2也叫作「命名空間」 var person1={ name:"candy", age:28 } var person2={ name:"andy", age:30 }
二、單例模式是一種項目開發中常用的模式,由於項目中咱們可使用單例模式來進行咱們的「模塊化開發」
模塊化開發
:對於一個相對來講比較大的項目,須要多我的協做開發的,咱們通常狀況下會根據當前項目的需求劃分紅幾個功能版塊,每一個人負責一部分,同時開發,最後把每一個人的代碼合併
//公共模塊 var utils={ select:function(){ } } //頁卡模塊中的change->實現選項卡切換 var tabRender={ change:function(){ utils.select();//在本身的命名空間下調用其餘的命名空間的方法 } } //搜索模塊change->搜索內容變化處理 var serchRender={ change:function(){ }, clickEven:function(){ this.change();//在本身的命名空間下調用其餘的命名空間的方法 } }
一、單例模式雖然解決了分組的做用,可是不能 實現批量生產,屬於手工做業模式,爲了應對這種狀況就催生了「工廠模式」;
工廠模式
:把實現同一件事情的相同代碼放到一個函數中,之後若是想再實現這個功能,不須要從新的編寫這些代碼了,只須要執行當前的函數便可;咱們把這種形式又稱爲函數的封裝
做用:
低耦合高內聚
->減小頁面中的冗餘代碼,提升代碼的重複利用率
function createJsPerson(name,age){ var obj={}; obj.name=name; obj.age=age; obj.writeJs=function(){ console.log("my name is "+this.name+",i can write js!"); } return obj; } var p1=createJsPerson("andy",30); p1.writeJS(); var p2=createJsPerson("candy",28); p2.writeJS();
JS中不存在重載
JS是一門輕量級的腳本」編程語言「(HTML+CSS不屬於編程語言,屬於標記語言)全部的編程語言都是面向對象開發的->類的繼承、封裝、多態
繼承:子類繼承父類中的屬性和方法
多態:當前方法的多種形態->後臺語言中:多態包含重載和重寫
//後臺中的重載 public void sum(int num1,int num2){ } public void sum(String num1,int num2){ }
function sum(num1,num2){ } function sum(num1){ } //js中不存在重載,方法名同樣的話,後面的會把前面的覆蓋掉,最後只保留一個
JS中有一個操做相似重載的方法可是不是重載:咱們能夠根據傳遞參數的不同,實現不一樣的功能重寫:子類重寫父類的方法
function sum(num){ if(typeOf num==="undefined"){ return 0; } return num(); } sum(100); sum();
//構造函數模式的目的就是爲了建立一個自定義類,而且建立這個類的實例 function CreateJsPerson(name,age){ //瀏覽器默認建立的對象就是咱們的實例p1->this this.name=name; this.age=age; this.writeJs=function(){ console.log("my name is "+this.name+",i can write js"); } //瀏覽器再把建立的實例默認的進行返回 } var p1=new CreateJsPerson("candy",28); p1.writeJs(); var res=CreateJsPerson("andy",30); console.log(res); /* 這樣寫不是構造函數執行而是普通的函數執行 因爲沒有寫return 因此 res=undefined 而且CreateJsPerson這個方法中的this是window */ var p2=new CreateJsPerson("candice",29); p2.writeJs();
一、構造函數模式和工廠模式的區別?
1)執行的時候
2)在函數代碼執行的時候
二、構造函數解析
1)JS中全部的類都是函數數據類型的,它經過new執行變成了一個類,可是它自己也是一個普通的函數;JS中全部的實例都是對象數據類型
2)在構造函數模式中,類中(函數體中)出現的this.xxx=xxx中的this是當前類的一個實例
3)p1和p2都是CreateJsPerson這個類的實例,因此都擁有writeJs這個方法,可是不一樣實例之間的方法是不同的,在類中給實例增長的屬性(this.xxx=xxx)屬於當前實例的私有屬性,實例和實例之間是單獨的個體,因此私有的屬性之間是不相等的
console.log(p1.writeJs===p2.writeJs);//false
三、構造函數相關知識點
function Fn(){ this.x=100; this.getX=function(){ console.log(this.x); } } var f1=new Fn; f1.getX();//->方法中的this是f1->100 var ss=f1.getX; ss();//方法中的this是window->undefined
function Fn(){ var num=10; this.x=100; this.getX=function(){ console.log(this.x); } } var f1=new Fn; console.log(f1.num);//=>undefined
function Fn(){ this.x=100; this.getX=function(){ console.log(this.x); } return {name:"candy"}; } var f1=new Fn; console.log(f1);//{name:"candy"}
在構造函數模式中,瀏覽器會默認的把咱們的實例返回(返回的是一個對象數據類型的值);若是咱們本身手動寫了return返回:
function Fn(){ this.x=100; this.getX=function(){ console.log(this.x); } } var f1=new Fn; console.log(f1 instanceof Fn);//true console.log(f1 instanceof Array);//false console.log(f1 instanceof Object);//true //由於全部的實例都是對象數據類型的,而每個對象數據類型都是Object這個內置類的一個實例,因此f1也是它的一個實例
instanceof
function Fn(){ this.x=100; this.getX=function(){ console.log(this.x); } } var f1=new Fn; var f1=new Fn; console.log(f1.getX===f2.getX);//false console.log("getX" in f1);//true console.log(f1.hasOwnProperty("getX"));//ture
hasOwnProperty
:用來檢測某個屬性是否爲這個對象的私有屬性,這個方法只能檢測私有的屬性檢測某一個屬性是否爲該對象的「公有屬性」->hasPubProperty
function hasPubProperty(obj,attr){ return (attr in obj) && !obj.hasOwnProperty(attr); }
實例識別
:構造函數模式中擁有類和實例的概念,而且實例和實例之間是相互獨立開的基於構造函數模式的原型模式解決了 方法或者屬性公有的問題->把實例之間相同的屬性和方法提取成公有的屬性和方法(想讓誰公有就把他放在當前類的原型上xxx.prototype便可)
function Fn(){ this.x=100; this.sum=function(){} } Fn.prototype.getX=function(){ console.log(this.x); } Fn.prototype.sum=function(){ } var f1=new Fn; var f2=new Fn; console.log(Fn.prototype.constructor===Fn);//true
Object是JS中全部對象數據類型的基類(最頂層的類)
f1 instanceof Object->true 由於f1經過__proto__能夠向上級查找,無論有多少級,最後總能找到Object
在Object.prototype上沒有__proto__這個屬性
f1.hasOwnProperty("x");//hasOwnProperty是f1的一個屬性可是咱們發如今f1的私有屬性上沒有這個方法,那如何處理的呢?
經過對象名.屬性名的方式獲取屬性值的時候,首先在對象的私有屬性上進行查找,若是私有中存在這個屬性,則獲取的是私有的屬性值;若是私有的沒有,則經過__proto__找到所屬類的原型(類的原型上定義的屬性和方法都是當前實例的公有的屬性和方法),原型上存在的話,獲取的是公有的屬性值若是原型上也沒有,則繼續經過原型上的__proto__繼續向上查找,一直找到Object.prototype爲止
——>這種查找的機制就是咱們的
原型鏈模式
**在IE瀏覽器中,咱們的原型模式也是一樣的原理,可是IE瀏覽器怕你經過__proto__把公有的修改,禁止咱們使__proto__
某一個方法中他的this,看執行的時候「.」前面是誰,this就是誰
function Fn(){ this.x=100; this.y=200; this.getY=function(){ console.log(this.y); } } Fn.prototype={ constructor:fn, y:300, getX:function(){ console.log(this.x); }, getY:function(){ console.log(this.y) } } var f=new Fn(); f.getX();//console.log(f.x)->100 f.__proto__.getX();//this是f.__proto__->console.log(f.__proto__.x)->undefined Fn.prototype.getX();//undefied f.getY();//200 f.__proto__.getY();//300
//在內置類的原型上擴展咱們的方法: Array.prototype.myUnique=function myUnique(){ //this->ary var obj={}; for(var i=0;i<this.length;i++){ var cur=this[i]; if(obj[cur]==cur){ this[i]=this[this.length-1]; this.length--; i--; continue; } obj[cur]=cur; } obj=null; return this;//目的是爲了實現鏈式寫法 } var ary=[12,23,23,13,12,13,24,12,13]; console.log(ary.myUnique().sort(function(a,b){return a-b})); //->[12,13,23,24] //鏈式寫法:執行完成數組的一個方法能夠緊接着執行下一個方法 //由於sort是Array.prototype上的公有方法,而數組ary是Array這個類的一個實例,因此ary可使用sort方法->數組才能是用咱們Array原型上的屬性和方法
//一、起一個別名 function Fn(){ this.x=100; } var pro=Fn.prototype;//把原來原型執行的地址賦值給咱們的pro,如今他們操做的是同一個內存空間 pro.getX=function(){}; pro.getY=function(){}; pro.getZ=function(){}; var f1=new Fn;
//二、重構原型對象的方式->本身新開闢一個堆內存,存儲咱們公有的屬性和方法,把瀏覽器原來給的Fn.prototype開闢的那個替換掉 function Fn(){ this.x=100; } Fn.prototype={ constructor:Fn, a:function(){}, b:function(){} } var f=new Fn; f.a(); f.b(); //A、只有瀏覽器天生給的Fn.prototype開闢的堆內存裏面纔有constructor,而咱們本身開闢的這個堆內存沒有這個屬性,這樣constructor指向就不是Fn而是Object了 爲了和要來保持一致,咱們須要手動的增長constructor的指向 //B.給內之類增長公有的屬性 例:Array.prototype.unique=function(){} //若是咱們用上面代碼中的方法修改內之類的話,瀏覽器會屏蔽掉,可是咱們能夠一個個的修改內置的方法,當咱們經過這種方式增長方法,若是方法名和原來內置的重複了,會把內置的修改掉->因此在之後內之類的原型上增長方法,命名都須要加特殊的前綴
//for in循環在遍歷的時候,默認的話能夠把本身的私有的和他所屬類原型上擴展的屬性和方法均可以遍歷到,可是通常狀況下,咱們遍歷一個對象只須要遍歷私有的便可,咱們可使用如下判斷進行處理 var obj={name:'candy',age:28} for(var key in obj){ if(obj.prototypeIsEnumberable(key)){ console.log(key); } if(obj.hasOwnProperty(key)){ console.log(key) } } //Object.create(proObj)建立一個新的對象,可是還要吧proObj做爲這個對象的原型 在IE6~8不兼容(ECMAScript5) /*** var obj={ getX:function(){} } var obj2=Object.create(obj); obj2.getX(); obj.getY=function(){ console.log(2); } obj2.getY(); ***/ var obj={ getX:function(){ console.log(1); } }; function object(o){ function Fn(){} Fn.prototype=o; return new Fn; } var newObj=object(obj); newObj.getX();
原型繼承
是咱們JS中最經常使用的一種繼承方式
function A(){ this.x=100; } A.prototype.getX=function(){ console.log(this.x); } function B(){ this.y=200; this.x=200; } B.prototype=new A; B.prototype.constructor=B; var n=new B; n.getX();//200;
原型繼承的特色:它是把父類中的私有的+公有的都繼承到子類原型上(子類公有的)
核心:原型鏈繼承並非把父類中的屬性和方法克隆一份如出一轍的給B,而是讓B和A之間增長了原型鏈的連接,之後B的實例n想要用A中的getX方法,須要一級一級的向上查找
call繼承
:把父類私有的屬性和方法 克隆一份如出一轍的做爲子類私有的屬性
function A(){ this.x=100; } A.prototype.getX=function(){ console.log(this.x); } function B(){ A.call(this);//A.call(n) 把A執行讓A中的this變爲n } var n=new B; console.log(n.x);//100 n.getX();//Uncaught TypeError: n.getX is not a function
冒充對象繼承
:把父類私有的+公有的 克隆一份如出一轍的給子類私有的
function A(){ this.x=100; } A.prototype.getX=function(){ console.log(this.x); } function B(){ var temp=new A; for(var key in temp){ this[key]=temp[key]; } temp=null; } var n=new B; n.getX();
function A(){ this.x=100; } A.prototype.getX=function(){ console.log(this.x); } B.prototype=objectCreate(A.prototype); B.prototype.constructor=B; var n=new B; n.getx(); function objectCreate(o){ function Fn(){ } Fn.prototype=o; return new Fn; }
混合模式繼承
:原型繼承+call繼承
function A(){ this.x=100; } A.prototype.getX=function(){ console.log(this.x); } function B(){ A.call(this);//n.x=100 } B.prototype=new A;//B.prototype: x=100 getX... B.prototype.constructor=B;
function avgFn(){ Array.prototype.sort.call(arguments,function(a,b){ return a-b; }) Array.prototype.pop.call(arguments); Array.prototype.shift.call(arguments); return (eval(Array.prototype.join.call(arguments,"+")) /arguments.length).toFixed(2) } console.log(avgFn(10,20,30,10,30,40,40))
function avgFn(){ arguments.__proto__=Array.prototype; arguments.sort(function(a,b){ return a-b; }) arguments.pop(); arguments.shift(); return (eval(arguments.join("+"))/arguments.length).toFixed(2); } console.log(avgFn(10,20,30,10,30,40,40));
1.在數組的原型上有一個方法叫作slice,咱們要求你們本身回去後實現一個方法mySlice,要求和原來的slice功能如出一轍
Array.prototype.mySlice=function mySlice(){ var ary=[]; var num=0, start=parseInt(Number(arguments[0])), end=parseInt(Number(arguments[1])); if(end===undefined||end>this.length){ end=this.length; }else if(!start){ start=0; } start>=0?null:start+=this.length; end>=0?null:end+=this.length; for(var i=start;i<end;i++){ ary[num]=this[i]; num++; } return ary; } var ary=[1,2,3,4,5,6]; console.log(ary.mySlice(1,"px")) console.log(ary.slice(1,"px"));
二、實現一個需求(5).plus(10).reduce(2) =>5+10-2
Number.prototype.plus=function plus(num){ return this.valueOf()+num; } Number.prototype.reduce=function reduce(num){ return this.valueOf()-num; } var m=(5).plus(10).reduce(2); console.log(m);