匿名函數當即調用php
(function (){alert(1)}())
function的左圓括號是必須的正則表達式
1,做爲函數的調用,
2,做爲方法的調用
嵌套函數的this沒有做用域的限制,若是做爲方法調用,指調用它的對象,若是做爲函數調用,this是全局對象或undefinedjson
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,做爲構造函數的調用
建立空對象,對象的原型是構造函數的prototype,構造函數使用this引用這個新對象,那麼 var a=new o.m()中的m中的this就不是o而是a數組
4,間接調用瀏覽器
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,誰調用它就是誰,做爲誰的構造函數就是誰,經過apply call能夠改變app
js中每一個函數是一個做用域,在函數中聲明的變量和函數的參數在整個函數體包括其嵌套函數都是可見的dom
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初學者應該是頗有用的。
1、變量的做用域
要理解閉包,首先必須理解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
2、如何從外部讀取局部變量?
出於種種緣由,咱們有時候須要獲得函數內的局部變量。可是,前面已經說過了,正常狀況下,這是辦不到的,只有經過變通方法才能實現。
那就是在函數的內部,再定義一個函數。
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
3、閉包的概念
上一節代碼中的f2函數,就是閉包。
各類專業文獻上的"閉包"(closure)定義很是抽象,很難看懂。個人理解是,閉包就是可以讀取其餘函數內部變量的函數。
因爲在Javascript語言中,只有函數內部的子函數才能讀取局部變量,所以能夠把閉包簡單理解成"定義在一個函數內部的函數"。
因此,在本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。
4、閉包的用途
閉包能夠用在許多地方。它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中。
怎麼來理解這句話呢?請看下面的代碼。
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,能夠在函數外部對函數內部的局部變量進行操做。
5、使用閉包的注意點
1)因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。
2)閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。
6、思考題
若是你能理解下面兩段代碼的運行結果,應該就算理解閉包的運行機制了。
代碼片斷一。
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形成影響
建立對象的三種方式
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}})
可配置的優先級最高
對象的屬性
原型屬性
Object.getPrototypeOf()查詢原型
isPrototypeOf()是不是它的原型(和instanceof類型,構成判斷是不是對象的方法)
類屬性
Object.prototype.toString.call(obj).slice(8,-1)
可擴展性
事件冒泡:由最具體的元素(嵌套層次最深的)接收,而後逐級向上傳到不具體的節點
事件捕獲:過程相反,
DOM2級事件流:事件捕獲=》處於目標=》事件冒泡
事件的寫法:
1,行內元素
注意some和行內在同一做用域裏
<div id="dom" onclick="alert('de')">點擊我</div> <div onclick="some()">點擊ta</div> <script> function some(){ alert('ta') } </script>
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執行進程外,還有代碼隊列,隨着時間,代碼按照順序添加到隊列,主進程執行完後,是空閒狀態,後面的進程開始執行。
定時器例子
假設,某個onclick 事件處理程序使用setInterval()設置了一個200ms 間隔
的重複定時器。若是事件處理程序花了300ms 多一點的時間完成,同時定時器代碼也花了差很少的時間,
就會同時出現跳過間隔且連續運行定時器代碼的狀況
這個例子中的第1 個定時器是在205ms 處添加到隊列中的,可是直到過了300ms 處纔可以執行。當
執行這個定時器代碼時,在405ms 處又給隊列添加了另一個副本。在下一個間隔,即605ms 處,第一
個定時器代碼仍在運行,同時在隊列中已經有了一個定時器代碼的實例。結果是,在這個時間點上的定
時器代碼不會被添加到隊列中。結果在5ms 處添加的定時器代碼結束以後,405ms 處添加的定時器代碼
就馬上執行。
爲了不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);
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); `` 默認狀況下,在發送XHR 請求的同時,還會發送下列頭部信息。 Accept:瀏覽器可以處理的內容類型。 Accept-Charset:瀏覽器可以顯示的字符集。 Accept-Encoding:瀏覽器可以處理的壓縮編碼。 Accept-Language:瀏覽器當前設置的語言。 Connection:瀏覽器與服務器之間鏈接的類型。 Cookie:當前頁面設置的任何Cookie。 Host:發出請求的頁面所在的域 。 Referer:發出請求的頁面的URI。注意,HTTP 規範將這個頭部字段拼寫錯了,而爲保證與規 範一致,也只能將錯就錯了。(這個英文單詞的正確拼法應該是referrer。) User-Agent:瀏覽器的用戶代理字符串。 雖然不一樣瀏覽器實際發送的頭部信息會有所不一樣,但以上列出的基本上是全部瀏覽器都會發送的。 常常設置的請求頭是
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 取得提交的數據了:
<?php header("Content-Type: text/plain"); echo <<<EOF Name: {$_POST[‘user-name’]} Email: {$_POST[‘user-email’]} EOF; ?>
若是不設置Content-Type 頭部信息,那麼發送給服務器的數據就不會出如今$_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()函數的轉換規則以下。
若是是Boolean 值,true 和false 將分別被轉換爲1 和0。
若是是數字值,只是簡單的傳入和返回。
若是是null 值,返回0。
若是是undefined,返回NaN。
若是是字符串,遵循下列規則:
若是字符串中只包含數字(包括前面帶正號或負號的狀況),則將其轉換爲十進制數值,即"1"
會變成1,"123"會變成123,而"011"會變成11(注意:前導的零被忽略了);
若是字符串中包含有效的浮點格式,如"1.1",則將其轉換爲對應的浮點數值(一樣,也會忽
略前導零);
若是字符串中包含有效的十六進制格式,例如"0xf",則將其轉換爲相同大小的十進制整
數值;
若是字符串是空的(不包含任何字符),則將其轉換爲0;
若是字符串中包含除上述格式以外的字符,則將其轉換爲NaN。
若是是對象,則調用對象的valueOf()方法,而後依照前面的規則轉換返回的值。若是轉換
的結果是NaN,則調用對象的toString()方法,而後再次依照前面的規則轉換返回的字符
串值。
根據這麼多的規則使用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()會繼續解析第二個字符,直到解析完全部後續字符或者遇到了
一個非數字字符。例如,"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
轉義:模式中使用的全部元字符都必須轉義 ( [ { ^ $ | ) ? * + .]}
字面量:不加字符串 轉義\
構造函數:字符串形式 。全部元字符都必須雙重轉義 \
(字符在字符串中一般被轉義爲\,而在正則表達式字符串中就
會變成\\)
/w\hello\123/ "\w\\hello\\123"
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()