摘要:在這段代碼中,實際上就是閉包函數。定時器例子假設,某個事件處理程序使用設置了一個間隔的重復定時器。在下一個間隔,即處,第一個定時器代碼仍在運行,同時在隊列中已經有了一個定時器代碼的實例。結果是,在這個時間點上的定時器代碼不會被添加到隊列中。
函數
匿名函數立即調用
(function (){alert(1)}())
function的左圓括號是必須的
函數的調用1,作為函數的調用,
2,作為方法的調用
嵌套函數的this沒有作用域的限制,如果作為方法調用,指調用它的對象,如果作為函數調用,this是全局對象或undefined
var o={ m:function(){ var self=this; console.log(this===o) f() function f(){ console.log(this===o)//false console.log(self===o)//true } } }
3,作為構造函數的調用
創(chuàng)建空對象,對象的原型是構造函數的prototype,構造函數使用this引用這個新對象,那么 var a=new o.m()中的m中的this就不是o而是a
4,間接調用
bind apply call的區(qū)別apply和call是對函數的調用,第一個參數是改變this的指向的對象,call第二個以上傳的是每個參數,apply第二個參數是數組,將要傳遞的參數寫在數組里
兩個方法使得任何函數可以作為任意對象的方法來調用
bind的第一個參數是用來改變原函數this的指向,返回一個新函數,不進行調用
function f(y){return this.x+y} var o={x:1} var g=f.bind(o) g(2)//3
柯里化
var sum=function(x,y){return x+y} var suc=sum.bind(null,1) suc(2)//3關于this
函數里的this,誰調用它就是誰,作為誰的構造函數就是誰,通過apply call可以改變
什么是作用域js中每個函數是一個作用域,在函數中聲明的變量和函數的參數在整個函數體包括其嵌套函數都是可見的
var scope="gl" function ch(){ var scope="cb" function ne(){ var scope="ne" return scope } return ne() } ch()//ne // function test(o){ var i=0; if(typeof o=="object"){ var j=0; for(var k=0;k<10;k++){ console.log(k) } console.log(k) } console.log(j) } test({a:7})//i k j都是在該函數作用域內有定義的
聲明提前,只要變量在函數內定義了,這個變量在該作用域的任意地方都可用,甚至在變量之前的位置
// var scope="glo" function f(){ console.log(scope) var scope="local" console.log(scope) } f()//undefined local
作用域鏈
變量層層向上向外的作用域尋找(函數的參數屬于函數的局部變量)
函數自己調用自己
什么是閉包閉包(closure)是Javascript語言的一個難點,也是它的特色,很多高級應用都要依靠閉包實現。
下面就是我的學習筆記,對于Javascript初學者應該是很有用的。
一、變量的作用域
要理解閉包,首先必須理解Javascript特殊的變量作用域。
變量的作用域無非就是兩種:全局變量和局部變量。
Javascript語言的特殊之處,就在于函數內部可以直接讀取全局變量。
var n=999; function f1(){ alert(n); } f1(); // 999
另一方面,在函數外部自然無法讀取函數內的局部變量。
function f1(){ var n=999; } alert(n); // error
這里有一個地方需要注意,函數內部聲明變量的時候,一定要使用var命令。如果不用的話,你實際上聲明了一個全局變量!
function f1(){ n=999; } f1(); alert(n); // 999
二、如何從外部讀取局部變量?
出于種種原因,我們有時候需要得到函數內的局部變量。但是,前面已經說過了,正常情況下,這是辦不到的,只有通過變通方法才能實現。
那就是在函數的內部,再定義一個函數。
function f1(){ var n=999; function f2(){ alert(n); // 999 } }
在上面的代碼中,函數f2就被包括在函數f1內部,這時f1內部的所有局部變量,對f2都是可見的。但是反過來就不行,f2內部的局部變量,對f1就是不可見的。這就是Javascript語言特有的"鏈式作用域"結構(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。
既然f2可以讀取f1中的局部變量,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內部變量了嗎!
function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999
三、閉包的概念
上一節(jié)代碼中的f2函數,就是閉包。
各種專業(yè)文獻上的"閉包"(closure)定義非常抽象,很難看懂。我的理解是,閉包就是能夠讀取其他函數內部變量的函數。
由于在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個函數內部的函數"。
所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋梁。
四、閉包的用途
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。
怎么來理解這句話呢?請看下面的代碼。
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
在這段代碼中,result實際上就是閉包f2函數。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變量n一直保存在內存中,并沒有在f1調用后被自動清除。
為什么會這樣呢?原因就在于f1是f2的父函數,而f2被賦給了一個全局變量,這導致f2始終在內存中,而f2的存在依賴于f1,因此f1也始終在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。
這段代碼中另一個值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以nAdd相當于是一個setter,可以在函數外部對函數內部的局部變量進行操作。
五、使用閉包的注意點
1)由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
六、思考題
如果你能理解下面兩段代碼的運行結果,應該就算理解閉包的運行機制了。
代碼片段一。
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());
代碼片段二。
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());
function cr(){ var n=0; return { count:function(){return n++}, reset:function(){n=0} } } var c=cr(),d=cr() c.count()//0 d.count()//0 c.reset() c.count()//0 d.count()//1
每次函數調用時,形成新的對象來保存局部變量。
垃圾回收
函數內的變量,在函數調用之后會被從內存刪除,但如果函數內的嵌套函數被作為返回值,被某個變量引用,那么嵌套函數里的變量將不會被內存回收
var y=x;如果x指向一個對象的引用,那么y也是,而非這個對象的復本,通過y修改對象,也會對x造成影響
創(chuàng)建對象的三種方式
1,直接量 var obj={a:1}
2,new 構造函數
3,Object.create(對象)
原型鏈
除了Object.prototype和null,任何對象都繼承自另外一個對象,即原型,往上追溯,形成原型鏈(家譜)
繼承的話,不會改變原型的屬性值
function inherit(p){ if(p==null) throw TypeError() if(Object.create) return Object.create(p) var t=typeof p if(t!=="object" && t!=="function") throw TypeError() function f(){} f.prototype=p return new f() } var u={r:1} var c=inherit(u) c.x=1;c.y=1; c.r=3 console.log(u.r)//1
刪除屬性
delete刪除可配置性為true的自有屬性,不能刪除繼承屬性
檢測對象是否有屬性的四個方法
1,in
var o={x:1} "x" in o //true "toString" in o//true
2,hasOwnProperty() 檢測自有屬性,對于繼承屬性返回false
3,propertyIsEnumerable()檢測自有屬性且該屬性可枚舉
4,o.x!==undefined
枚舉屬性
for in
Object.keys() 自有可枚舉的屬性
Object.getOwnPropertyNames() 自有屬性不僅是可枚舉的屬性
getter&setter
var p={ x:1.0, y:1.0, get r(){ return this.x*this.y}, set r(v){//忽略set的返回值 if(v>0){ r=90 } }, get t(){ return 100 } }
屬性的4個特性
Object.getOwnPropertyDescriptor({x:1},"x")
//{value:1,writable:true,enumerable:true,configurable:true}
修改4個特性
var o={}
Object.defineProperty(o,"x",{value:1,writable:true,enumerable:false,configurable:true})
var p=Object.defineProperties({},{x:{value:1},y:{writable:true}})
可配置的優(yōu)先級最高
對象的屬性
原型屬性
Object.getPrototypeOf()查詢原型
isPrototypeOf()是否是它的原型(和instanceof類型,構成判斷是否是對象的方法)
類屬性
Object.prototype.toString.call(obj).slice(8,-1)
可擴展性
事件事件冒泡:由最具體的元素(嵌套層次最深的)接收,然后逐級向上傳到不具體的節(jié)點
事件捕獲:過程相反,
DOM2級事件流:事件捕獲=》處于目標=》事件冒泡
事件的寫法:
1,行內元素
注意some和行內在同一作用域里
點擊我點擊ta
2,DOM0級事件
onclick=function(){}//添加
onclick=null//移除
3,DOM2級事件
addEventListener("click",fn,true捕獲階段調用|false冒泡階段)
大多是是冒泡階段
removeEventListener移除的函數必須等于添加的函數,這時通過多帶帶函數定義實現
4,事件兼容寫法
var EventUtil = { addHandler: function(element, type, handler){ if (element.addEventListener){ element.addEventListener(type, handler, false); } else if (element.attachEvent){ element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function(element, type, handler){ if (element.removeEventListener){ element.removeEventListener(type, handler, false); } else if (element.detachEvent){ element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } }, getEvent: function(event){//獲取事件對象 return event ? event : window.event; }, getTarget: function(event){ return event.target || event.srcElement; }, preventDefault: function(event){//阻止默認事件 if (event.preventDefault){ event.preventDefault(); } else { event.returnValue = false; } }, stopPropagation: function(event){//阻止冒泡 if (event.stopPropagation){ event.stopPropagation(); } else { event.cancelBubble = true; } } };
理解事件函數里的this target currentTarget
點擊頁面的#mybtn按鈕
document.body.onclick=function(event){ alert(event.currentTarget===document.body)///true alert(this===document.body)//true alert(event.target===document.getElementById("mybtn"))//true }
currentTarget始終和對象this相同,target則是事件的實際目標
js進程js是單線程的,除了主js執(zhí)行進程外,還有代碼隊列,隨著時間,代碼按照順序添加到隊列,主進程執(zhí)行完后,是空閑狀態(tài),后面的進程開始執(zhí)行。
定時器例子
假設,某個onclick 事件處理程序使用setInterval()設置了一個200ms 間隔
的重復定時器。如果事件處理程序花了300ms 多一點的時間完成,同時定時器代碼也花了差不多的時間,
就會同時出現跳過間隔且連續(xù)運行定時器代碼的情況
這個例子中的第1 個定時器是在205ms 處添加到隊列中的,但是直到過了300ms 處才能夠執(zhí)行。當
執(zhí)行這個定時器代碼時,在405ms 處又給隊列添加了另外一個副本。在下一個間隔,即605ms 處,第一
個定時器代碼仍在運行,同時在隊列中已經有了一個定時器代碼的實例。結果是,在這個時間點上的定
時器代碼不會被添加到隊列中。結果在5ms 處添加的定時器代碼結束之后,405ms 處添加的定時器代碼
就立刻執(zhí)行。
為了避免setInterval()的重復定時器的這2個缺點,你可以用如下模式使用鏈式setTimeout()
調用。
setTimeout(function(){ //處理中 setTimeout(arguments.callee, interval); }, interval);
setTimeout(function(){ var div = document.getElementById("myDiv"); left = parseInt(div.style.left) + 5; div.style.left = left + "px"; if (left < 200){setTimeout(arguments.callee, 50); } }, 50);http
let xhr=new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){//已接收到全部的相應數據 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){//304表示請求的資源沒被改,直接用瀏覽器的緩存 alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } } }; xhr.open("get", "example.txt", true);//是否異步 true異步 false同步 xhr.send(null); `` 默認情況下,在發(fā)送XHR 請求的同時,還會發(fā)送下列頭部信息。 ? Accept:瀏覽器能夠處理的內容類型。 ? Accept-Charset:瀏覽器能夠顯示的字符集。 ? Accept-Encoding:瀏覽器能夠處理的壓縮編碼。 ? Accept-Language:瀏覽器當前設置的語言。 ? Connection:瀏覽器與服務器之間連接的類型。 ? Cookie:當前頁面設置的任何Cookie。 ? Host:發(fā)出請求的頁面所在的域 。 ? Referer:發(fā)出請求的頁面的URI。注意,HTTP 規(guī)范將這個頭部字段拼寫錯了,而為保證與規(guī) 范一致,也只能將錯就錯了。(這個英文單詞的正確拼法應該是referrer。) ? User-Agent:瀏覽器的用戶代理字符串。 雖然不同瀏覽器實際發(fā)送的頭部信息會有所不同,但以上列出的基本上是所有瀏覽器都會發(fā)送的。 經常設置的請求頭是
xhr.setRequestHeader("Content-Type", "application/json")
//設置header的需要放在open之后 請求參數為JSON.stringify({})
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
//如果請求參數需序列化為查詢字符串
請求參數序列化的方法
1,使用new FormData()的方便之處體現在不必明確地在XHR 對象上設置請求頭部。XHR 對象能夠識別傳
入的數據類型是FormData 的實例,并配置適當的頭部信息。
2,qs.stringify(json)
3,表單序列化serialize(document.getElementById("user-info"))
4,直接傳"a=b&c=d"
文件postexample.php 就可以通過$_POST 取得提交的數據了:
如果不設置Content-Type 頭部信息,那么發(fā)送給服務器的數據就不會出現在$_POST 超級全局變
量中。這時候,要訪問同樣的數據,就必須借助$HTTP_RAW_POST_DATA。
5種
undefined、null、boolean、number、string、object。
typeof判斷返回值
"undefined"——如果這個值未定義;
? "boolean"——如果這個值是布爾值;
? "string"——如果這個值是字符串;
? "number"——如果這個值是數值;
? "object"——如果這個值是對象或null;
? "function"——如果這個值是函數。
alert(null == undefined); //true
數據類型 轉換為true的值 轉換為false的值
Boolean true false
String 任何非空字符串 ""(空字符串)
Number 任何非零數字值(包括無窮大) 0和NaN
Object 任何對象 null
Undefined n/a undefined
NaN與任何數值都不相等,包括本身
NaN==NaN //false
Number()函數的轉換規(guī)則如下。
? 如果是Boolean 值,true 和false 將分別被轉換為1 和0。
? 如果是數字值,只是簡單的傳入和返回。
? 如果是null 值,返回0。
? 如果是undefined,返回NaN。
? 如果是字符串,遵循下列規(guī)則:
? 如果字符串中只包含數字(包括前面帶正號或負號的情況),則將其轉換為十進制數值,即"1"
會變成1,"123"會變成123,而"011"會變成11(注意:前導的零被忽略了);
? 如果字符串中包含有效的浮點格式,如"1.1",則將其轉換為對應的浮點數值(同樣,也會忽
略前導零);
? 如果字符串中包含有效的十六進制格式,例如"0xf",則將其轉換為相同大小的十進制整
數值;
? 如果字符串是空的(不包含任何字符),則將其轉換為0;
? 如果字符串中包含除上述格式之外的字符,則將其轉換為NaN。
? 如果是對象,則調用對象的valueOf()方法,然后依照前面的規(guī)則轉換返回的值。如果轉換
的結果是NaN,則調用對象的toString()方法,然后再次依照前面的規(guī)則轉換返回的字符
串值。
根據這么多的規(guī)則使用Number()把各種數據類型轉換為數值確實有點復雜。下面還是給出幾個具
體的例子吧。
var num1 = Number("Hello world!"); //NaN
var num2 = Number(""); //0
var num3 = Number("000011"); //11
var num4 = Number(true); //1
parseInt()
它會忽略字
符串前面的空格,直至找到第一個非空格字符。如果第一個字符不是數字字符或者負號,parseInt()
就會返回NaN;也就是說,用parseInt()轉換空字符串會返回NaN(Number()對空字符返回0)。如
果第一個字符是數字字符,parseInt()會繼續(xù)解析第二個字符,直到解析完所有后續(xù)字符或者遇到了
一個非數字字符。例如,"1234blue"會被轉換為1234,因為"blue"會被完全忽略。類似地,"22.5"
會被轉換為22,因為小數點并不是有效的數字字符。
如果字符串中的第一個字符是數字字符,parseInt()也能夠識別出各種整數格式(即前面討論的
十進制、八進制和十六進制數)。也就是說,如果字符串以"0x"開頭且后跟數字字符,就會將其當作一
個十六進制整數;如果字符串以"0"開頭且后跟數字字符,則會將其當作一個八進制數來解析。
toString()
null和undefined沒有該方法
var a=["s","d"] a[99]="c" a.length//100 //其他項是undefined
數組的方法
Array.isArray()// true false
sort(比較函數)方法
比較函數接收兩個參數,如果第一個參數應該位于第二個之前則返回一個負數,如果兩個參數相等
則返回0,如果第一個參數應該位于第二個之后則返回一個正數
concat(單個值或數組,...) 數組的副本
正則 實例屬性g
i
m
轉義:模式中使用的所有元字符都必須轉義 ( [ { ^ $ | ) ? * + .]}
字面量:不加字符串 轉義
構造函數:字符串形式 。所有元字符都必須雙重轉義
(字符在字符串中通常被轉義為,而在正則表達式字符串中就
會變成)
/whello123/ "whello123"
var re = null, i; for (i=0; i < 10; i++){ re = /cat/g; re.test("catastrophe"); } for (i=0; i < 10; i++){ re = new RegExp("cat", "g"); re.test("catastrophe"); }實例方法
正則.exec(字符串)
.test
構造函數形式.toString() //字面量
構造函數形式.toLocaleString() //字面量
.search()
.match()
.replace()
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/97955.html
摘要:前端月刊周刊文章百度前端圈奇虎團隊規(guī)范妙趣課堂開發(fā)社區(qū)百度規(guī)范騰訊淘寶攜程前端美團技術博客博客一峰老趙較舊舊深入理解系列安全相關頁凹凸實驗室前端開發(fā)規(guī)范攜程文章瀏覽器是如何工作的英文前端代碼規(guī)范及最佳實踐 前端月刊:https://www.kancloud.cn/jsfro... 周刊文章http://ourjs.com/http://www.feweekly.com/issuesht...
摘要:前端月刊周刊文章百度前端圈奇虎團隊規(guī)范妙趣課堂開發(fā)社區(qū)百度規(guī)范騰訊淘寶攜程前端美團技術博客博客一峰老趙較舊舊深入理解系列安全相關頁凹凸實驗室前端開發(fā)規(guī)范攜程文章瀏覽器是如何工作的英文前端代碼規(guī)范及最佳實踐 前端月刊:https://www.kancloud.cn/jsfro... 周刊文章http://ourjs.com/http://www.feweekly.com/issuesht...
摘要:在做時,不可避免地要上傳一些文件,最常用到的就是圖片。提供電子郵件服務的站點,除了圖片還需要上傳文檔等其他類型文件的功能。當點擊所看到的按鈕時,實際點擊的是原生上傳控件,這樣一來就觸發(fā)了瀏覽器默認行為,即打開選擇文件對話框。 在做 Web App 時,不可避免地要上傳一些文件,最常用到的就是圖片。在線相冊及圖片收藏網站等以提供圖片存儲服務的站點自不用說,社交網絡的頭像、發(fā)布信息等都需要...
摘要:引言半月刊第四期來啦,這段時間新增了道高頻面試題,今天就把最近半月匯總的面試題和部分答案發(fā)給大家,幫助大家查漏補缺,歡迎加群互相學習。更多更全的面試題和答案匯總在下面的項目中,點擊查看。引言 半月刊第四期來啦,這段時間 Daily-Interview-Question 新增了 14 道高頻面試題,今天就把最近半月匯總的面試題和部分答案發(fā)給大家,幫助大家查漏補缺,歡迎 加群 互相學習。 更多更...
閱讀 2875·2021-11-11 10:58
閱讀 1932·2021-10-11 10:59
閱讀 3499·2019-08-29 16:23
閱讀 2347·2019-08-29 11:11
閱讀 2794·2019-08-28 17:59
閱讀 3845·2019-08-27 10:56
閱讀 2087·2019-08-23 18:37
閱讀 3121·2019-08-23 16:53