—————————————————————————————————————————————————
用來控制網(wǎng)頁前進和后退,根據(jù)的是網(wǎng)頁歷史紀錄
history.back(); //后退
history.forward();//前進
history.pushState(data:json,title:string,url:string);// 會存儲在url歷史中
history.replaceState(data:json,title:string,url:string);// 不會存儲。。。
data是你要存放的數(shù)據(jù),可以使用history.state獲取,title是標題,為空則不改變,url是新url
用來控制頁面跳轉(zhuǎn)
location.replace("xx");//跳轉(zhuǎn)
location.href = 'xxxx';//同上
location.reload();//刷新頁面
直接使用元素的onclick屬性:
<button onclick="btnhandler(this)"> click me </button>
綁定一個事件:
getElementById("xx").onclick = function(e){ xxxxxxxx; }
事件監(jiān)聽:
document.getElementById("xxx").addEventListener('click',function(e){ xxxxxx; })
注意:事件名稱前不加on
區(qū)別:
使用內(nèi)聯(lián)onclick屬性似乎只能傳遞自身而不能傳遞事件對象
事件監(jiān)聽可以在一種事件上綁定多個方法
注意:
當元素是動態(tài)生成的時候,在元素生成前綁定的事件無效,如:
$('.btn').click(function (e){ ...... }); $('body’).append('<button class="btn">...</button>'); // 單擊事件不能綁定到新的元素上面
element.onclick() || element.onclick.call() ;
jquery方式:$(sel).click();
onchange當表單的值改變時觸發(fā)(需鼠標抬起,不是即時的)
oninput當表單控件受到改變時觸發(fā)(即時的)
事件對象自動傳遞給回調(diào)函數(shù) element.onclick = function(e){};// e就是事件對象
e的常見屬性:
e.target;//獲取觸發(fā)此事件的元素(不一定是綁定元素)
e.currentTarget//獲取觸發(fā)此事件的元素(一定是綁定元素)
e.offsetX ||e.offsetY ;//獲取鼠標基于target元素內(nèi)部的偏移x和y
e.clientX ||e.clientY ;//獲取鼠標基于瀏覽器視窗的偏移x和y
e.keyCode ||e.which;//返回鍵盤上的字符的代碼
事件回調(diào)中的this:指向事件的觸發(fā)元素
----如果事件處理函數(shù)的綁定在元素生成之前,則此元素不能綁定事件處理函數(shù),需重新設(shè)置
var a = document.getElementById("xxx").style.width;
document.getElementById("xxx").style.width = '500px'//注意加上單位
element.currentStyle.width || getComputedStyle( element,null).width ;
前者是ie專用,后者是w3c標準,注意:用這種方法只能獲取不能設(shè)置
$(sel).css(styleName);// 注意數(shù)據(jù)類型以及單位
注:行內(nèi)樣式一定是起作用的樣式,所以用js設(shè)置元素樣式都是設(shè)置行內(nèi)樣式
// 使用變換(transform)不會改變上述屬性的值
// 它們受left,top,margin等css屬性的影響,但不一定等于它們
// 建議使用offsetHeight,因為其他元素排列位置時是會考慮邊框和滾動條的
offsetParent// 用于定位的基元素(默認為最近的設(shè)置過position的父元素)
window.onscroll = function(e){ // 頁面滾動事件(一般加給window) // 頁面被卷起來的高度距離頂部或底部的距離 var juan = document.documentElement.scrollTop; // 獲取頁面被卷起來的高度,documentElement相當于html標簽 var total = document.documentElement.scrollHeight; // 獲取頁面總高度 var visul = window.innerHeight; // 獲取可見區(qū)的高度(即瀏覽器顯示出來的高度) var bot = total - juan - visul; // bot就是可見區(qū)下面的高度(這是我們需要的) ........ // 當bot小于某值時,加載新元素 }
注:document的類型是Document,document.documentElement才是常規(guī)的Element類型的DOM元素
注:onscroll事件觸發(fā)次數(shù)比較頻繁,可以考慮節(jié)流(一段時間內(nèi)只執(zhí)行一次)
需要給被拖動元素和window同時添加mousemove事件,因為即使拖動時鼠標在元素之外,也應(yīng)該能實現(xiàn)拖動。
具體實現(xiàn):
1.鼠標按下目標元素時,存儲clientX,clientY,以及被拖動元素的引用
2.鼠標移動時,設(shè)置被拖動元素的left為當前clientX - 預(yù)先存儲的clientX,clientY同理
3.釋放鼠標時,清除之前存儲的數(shù)據(jù)
就是先不設(shè)置src,而是將路徑放到其他屬性中(如data-src),等到圖片處于顯示區(qū)或接近顯示區(qū)時,再設(shè)置src
HTML DOM事件在:http://www.runoob.com/jsref/dom-obj-event.html
oncontextmenu
實例:
window.keydown = function(e){ //鍵盤事件一般和窗口綁定 var ev = window.event || e; //獲得事件對象(IE和其他瀏覽器均可用) var word = String.fromCharCode(ev.keyCode); //將ascii碼轉(zhuǎn)換成字符 alert(word); }
事件 描述
目前,需要根據(jù)瀏覽器種類加前綴
當元素位置重疊的時候,點擊重疊的位置,每個元素的事件會按照一定的順序觸發(fā)。
若只想讓第一個事件觸發(fā),則可在那個事件的方法體中加入以下代碼:
e.stopPropagation();//中斷此次事件的后續(xù)操作
若顯示層級位于表層的元素沒有事件而里層元素有綁定事件,那么事件不會觸發(fā),因為事件流不是一個通路。這在給父元素加鼠標事件時很常用
事件相互影響的問題:
例如:如按空格讓視頻暫停,在文本框中輸入空格也可能會讓視頻暫停,這是因為事件冒泡到上級,只需要在文本框上的鍵盤事件上中斷事件流即可
例如,在一個ul中為要每個li設(shè)置點擊事件,只需給ul設(shè)置點擊事件即可,li的事件會冒泡至ul上,通過this|e.target獲取li的DOM對象
瀏覽器默認事件:
如:點擊a標簽后,瀏覽器會默認跳轉(zhuǎn)到指定頁面;點擊鼠標右鍵,會彈出上下文菜單等
建立onclick事件方法,加入var ev=window.event; ev.preventDefault();
阻止a標簽的默認事件:
<a href="javascript:void(0)">鏈接</a>
this在事件處理函數(shù)中指向事件源對象(觸發(fā)事件的元素對象)
當元素被批量選中時,this指針對這些元素的事件處理非常有用
使用e.target也能獲取事件源對象// e表示事件對象
e.currentTarget表示最原始的觸發(fā)這個事件的對象(根據(jù)冒泡機制,只要條件符合,子元素也會觸發(fā)父元素身上的事件)
事件的傳遞分成三個階段:
PHASE1.捕獲:事件從祖先元素開始(從window開始)向內(nèi)傳遞
PHASE2.目標:事件已經(jīng)到達目標(最內(nèi)層的命中事件的元素)
PHASE3.冒泡:事件向外(祖先)冒泡至window
默認的偵聽器是偵聽冒泡階段的事件。
以點擊事件為例,鼠標點擊后,會從window對象開始,檢索其子對象的區(qū)域是否包含點擊點,html,body...直到某個元素的所有子元素的區(qū)域都不包含該點但自身包含該點,則此元素為目標元素,接著,從目標元素開始,依次執(zhí)行點擊事件回調(diào),直到window對象。
ClickHandler(window,new MouseEvent(x,y)); bool ClickHandler(DOM * dom, MouseEvent * e){ List<DOM*> children = dom->children; bool hit = false; for(int i=0;i<children.length();i++){ hit = ClickHandler(children[i],e) ; if(hit){ e->phase = 3; e->currentTarget = children[i]; dom->eventListener('click').call(e); return true; } } if(dom.area.include(e->x,e->y)){ e->target = e->currentTarget = dom; e->phase = 2; dom->eventListener('click').call(e); return true; } else { return false; } }
js中時間日期是以對象的形式實現(xiàn)的
var time = new Date();//獲得客戶端當前的時間,返回一堆字符串,還可以用時間戳構(gòu)造(注意:客戶端的時間可能是不準確的)
var time = new Date(年,月,日,時,分,秒);//創(chuàng)建一個具體的時間
方法:
parseInt(str);// 會提取字符串中的整數(shù)部分,遇到非整數(shù)會立即停止提取;適合去掉css中的單位
parseFloat(str)// 同上,可以提取小數(shù)
Number.toFixed(n)// 保留n位小數(shù),為0則只保留整數(shù)
Number.round()// 返回最接近的整數(shù)(相當于四舍五入)
Number.floor()// 向小取整
(function (){ //代碼塊 }());
或者
(function (){ // 代碼塊 })();
可以在前面加上 ; 提升可靠性
—————————————————————————————————————————————————
兄弟:$(sel).siblings(sel);
父級:$(sel).parent(sel);// 只能抓上一級
前輩:$(sel).parents(sel);// 可能是多個
第一個:$(sel).first();
第n個:$(sel).eq(n);
孩子:$(sel).chlidren(sel);// 注意只能抓下一級
取序號:$(sel).index();
$(sel).each(function (i,e)){ // i是序號,e是元素,只有一個參數(shù)時,表示序號 // 代碼 } $.each(obj,function (i,e)){ // i是序號,e是元素,只有一個參數(shù)時,表示序號 // 代碼 }
注:js中數(shù)組API的回調(diào)函數(shù)通常是function(e,i){},即序號在后
關(guān)于表單元素:注意table標簽內(nèi)會自動加一個tbody標簽,獲取<tr>元素:$('table tbody tr');
hide(time,callback) show(...) fadeIn(...) fadeOut(...)
toggle動畫:toggle() toggleFade() toggleSlide() 指根據(jù)display來判斷做什么動畫
animate(json,timer,[timeFunc,callback]);
obj:格式是{attrName:styleValue, ...},表示元素要達到的樣式
timer:int類型,表示動畫的毫秒數(shù)
timeFunc:速度函數(shù),有ease,linear等
callback: 回調(diào)函數(shù)
html()// 獲取或設(shè)置innerHTML
text()// 獲取或設(shè)置innerTEXT,會自動轉(zhuǎn)義
val();// 獲取或設(shè)置value(只能用于表單)
position();//獲取匹配元素相對父元素的偏移,返回的對象包含兩個整型屬性:top 和 left
height();
width();
返回數(shù)字,單位為px
jquery對象不能使用DOM對象的屬性和方法,jquery是對dom對象的封裝,使用jquery的0號元素即可獲得原來的DOM對象
$(sel)[0].setEventListener(....);
定義一個在頁面加載完成后立即執(zhí)行的函數(shù):
$(function (){ // 函數(shù)體 });
—————————————————————————————————————————————————
指向主調(diào)對象,或者window,其值是在運行時可知的
函數(shù)對象:即Function類型的對象
實例對象:new 函數(shù)名()后生成的對象
function Foo(){ this.func = function (){ // 這是為實例添加方法:var obj = new Foo(); obj.func() ... } function func(){ // 沒有為任何對象添加方法,該函數(shù)僅在foo內(nèi)部可用,這是錯的:foo.func() (×) ... } } Foo.func = function (){ // 這是為函數(shù)添加方法:foo.func() ... }
·所有函數(shù)都有prototype對象
·prototype對象是一個Object類型的對象
·該object對象中有一個constructor對象,指向該函數(shù)對象
·可以為prototype對象設(shè)置屬性,這些屬性實際是給實例對象訪問的
示例:
var func = function (){ } var f = new func(); func.prototype.print = function (){ console.log("print..."); } f.print(); // 控制臺輸出print... console.log(func.prototype.constructor === func) // true
所有實例對象都有一個__proto__屬性,也是object類型的,該屬性和其函數(shù)原型(構(gòu)造函數(shù))的prototype屬性是一樣的,它們共享一份Object對象,即函數(shù)對象.prototype = 實例對象.__proto__,在上述例子中就是func.prototype === f.__proto__
使用對象屬性(或方法)時,先測試對象本身有無此方法,若沒有,則在__proto__中查找,直到找到,或不存在。這種查找鏈就是原型鏈(原型鏈使用的是__proto__而不是prototype)。
實例(接上例):
f.toString();
// func中無toString方法,其原型中也無,于是通過__proto__到Object的原型中找,在Object中找到了toString方法,。
·Function和Object是系統(tǒng)內(nèi)置的函數(shù)。
·所有函數(shù)對象都是Function類型的實例(通過new Function()得到)
·Object是內(nèi)置的函數(shù)對象,也是Function類型的實例對象
·Function也是函數(shù)對象,它也是Function類型的實例對象
由以上三點可知:
·所有的函數(shù)對象都有prototype和__proto__兩個屬性,有prototype是因為所有函數(shù)都有prototype對象,有__proto__是因為它是Function類型的實例
·所有函數(shù)對象的__proto__都等于Function對象的prototype,因為所有函數(shù)對象都是Function對象的實例
·Function對象的prototype和__proto__是相等的
·prototype和__proto__的類型是Object,而Object本身也有prototype和__proto__屬性,Object的__proto__屬性等于Function對象的prototype(前面說過),Object對象的prototype屬性中有很多內(nèi)置的方法:
Object對象的prototype屬性不是Object類型的,而且該屬性的__proto__屬性為null,它是原型鏈的終點。
恒成立(假設(shè)用戶定義了一個函數(shù)函數(shù),名為Func):
Func instanceof Function // 因為Func.__proto__ == Function.prototype Func instanceof Object // 因為Func.__proto__.__proto__ == Object.prototype Function instanceof Function // 因為Function.__proto__ == Function.prototype Object instanceof Function // 因為Object.__proto__ == Function.prototype Function instanceof Object // 因為Function.__proto__.__proto__ == Object.prototype Object instanceof Object // 因為Object.__proto__.__proto__ == Object.prototype
L instanceof R當且僅當
L.__proto__......__proto__ === R.prototype
至少有一個__proto__
函數(shù)(或全局代碼)執(zhí)行前,會初始化上下文,包括:
例:
function foo(){ console.log(c) // undefined var c = 1; }
注:如果沒有定義變量,就直接使用,會在作用域鏈上查找,而不是在自身作用域上創(chuàng)建。
例1(以下是全局代碼):
function foo(){ username = 'zhangshan'; }
會設(shè)置window.username為'zhangshan',與函數(shù)中的this是誰無任何關(guān)系
例2:
function foo(){ this.username = 'zhangshan'; }
直接調(diào)用foo()時,效果同上(直接調(diào)用某個函數(shù)時,調(diào)用者一定是windows)
js支持函數(shù)的嵌套定義,內(nèi)部的函數(shù)叫子函數(shù),外部的函數(shù)叫父函數(shù)。
當子函數(shù)引用了父函數(shù)中的變量,就會在子函數(shù)中產(chǎn)生一個閉包,包含了被引用的變量。
來看這個例子:
function foo(){ var msg="hello"; return function(){ return msg + " world"; } } var a = foo(); console.log(a()); // a能否正確使用msg?
foo()執(zhí)行完后,變量msg應(yīng)該被釋放,但是子函數(shù)引用了msg,產(chǎn)生了閉包,所以msg的生命周期變長,不會被釋放,所以執(zhí)行a()可以正確輸出msg的值。
產(chǎn)生閉包需要同時滿足:
1.存在函數(shù)嵌套
2.子函數(shù)中使用了父函數(shù)的變量
3.調(diào)用父函數(shù)
js是單線程的。
定時器回調(diào),DOM事件回調(diào),Ajax回調(diào)都會放在回調(diào)隊列中,待程序順序執(zhí)行完畢時,才會執(zhí)行。
注:并不是回調(diào)都是異步任務(wù),比如Promise()的參數(shù)function會同步執(zhí)行
Object.create(obj,[property])
//使用指定對象作為__proto__產(chǎn)生一個新對象。
Object.defineProperty(obj,propname,conf)
// 給obj定義屬性propname,conf為配置項。
// 該函數(shù)可以監(jiān)視某個對象的改變,這是很多MVVM框架實現(xiàn)數(shù)據(jù)綁定的原理
Object.assign(target,source);
// 復(fù)制source的所有屬性到target并返回
詳詢:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object
Function.property.call(obj,param);
Function.property.apply(obj,[params]);
Function.property.bind(obj,param);
//均是 給函數(shù)指定this,其中bind不會執(zhí)行,而是返回該函數(shù)。
使用某個構(gòu)造函數(shù)new一個對象A實際上就是設(shè)置對象A的__proto__為構(gòu)造函數(shù)的prototype,然后將this設(shè)置為A來執(zhí)行構(gòu)造函數(shù)。
手動實現(xiàn)new的方式:
function NEW(f){ // f為構(gòu)造函數(shù) var obj = Object.create(f.prototype); f.call(obj); return obj; }
var $ = document.querySelector; $(...) // Illegal invocation
原因: document.querySelector()的this是document而$()的this是不確定的.
解決:var $ = document.querySelector.bind(document);
如果某個變量(或表達式的結(jié)果)的值為undefined,null,'',0,false,則為假值,非上述值則為真值
即js的假值有多種,但!假值都是true,同理真值有無數(shù)種,但!真值都是false
空對象{}和空數(shù)組[]為真值
js的與或運算(&&,||)并不產(chǎn)生true或false,而是:
在處理或運算時,返回第一個為真的值,若全為假,則返回最后一個假值
在處理與運算時,返回第一個為假的值,若全為真,則返回最后一個真值
在處理非運算時,一定返回true或false
關(guān)于相等性的研究請參考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness
例:
var obj = createSomething() || {};// 若createSomething()返回假值,則將obj初始化為{}
例(將雙等==改成全等===的結(jié)果也是一樣的):
[undefined] == [undefined] // false,實際上無論數(shù)組里面是什么,該表達式始終返回false undefined == false // false undefined || false // false !undefined == true // true undefined && false // undefined false && undefined // false undefined || null // null '11.000' == 11 // true
和var一樣,定義變量,區(qū)別是:
1.支持塊級作用域
2.不能變量提升
定義常量,不能修改
例1:
let {name,age} = {'zhangshan',15} // 創(chuàng)建name,age兩個對象并依次賦值 let {name,age} = obj // 創(chuàng)建name,age兩個變量,并按obj中的字段名賦值 let [a,b,c] = [1,2,3] // 創(chuàng)建a,b,c三個變量,按索引號賦值
例2:
function foo({name,age}){....} // 同第二個,按字段名賦值 foo(obj)
使用``包含,并使用${name}標記變量,自動拼接。
var str = `我叫${obj.name},今年${obj.age}歲`;
let obj = { name, // 直接將外部作用于的name賦給它,下同 age, setName(name){ // 相當于function setName(name){} this.name = name } }
let a1 = ['a','b','c']; let a2 = [...a1]; let a3 = [...a2,...a1]; let o1 = {a:123,b:456}; let o2 = {...o1}
function foo(...value); // value會變成參數(shù)數(shù)組 function foo(arg1,...value); // value會變成除arg外的參數(shù)數(shù)組
...expression // 如果...后接一個可迭代對象,則此語句的作用是將該對象的所有可迭代屬性變成當前對象內(nèi)的屬性
例:
var obj = { ...func(); }
如果func()函數(shù)返回一個可迭代對象,則上述語句表示將func()返回的對象中的所有屬性變成obj的
function point(x = 0,y = 0){ };
let foo = (params)=>{
statment;
}
foo(123);
注:lambda表達式中的this為它所在作用域中的this,且不能用call()更改,而一般函數(shù)的this需要在運行時確定.
當只有一個參數(shù)和一行語句時可簡略:param => expression;
let bar = param1 =>param2 => { statment;}
表示外層的箭頭函數(shù)的返回值是一個函數(shù)對象,也就是內(nèi)層的箭頭函數(shù)
即執(zhí)行bar()會得到內(nèi)層的函數(shù)對象
for...of語句可以遍歷實現(xiàn)了Iterator接口的對象(可迭代對象)。
例:
let arr = [2,3,4,5,5,6]; for(let e of arr){ // using e; }
數(shù)組,偽數(shù)組(類數(shù)組),set,map實現(xiàn)了Iterator接口,Object沒有實現(xiàn)該接口,但可以手動實現(xiàn)。
手動實現(xiàn)Iterator的方式:需要實現(xiàn)next方法,該方法返回此格式的對象{value:dataType,done:boolean},value是元素的值,done表示是否是最后一個元素
let it = Symbol.iterator; Object.prototype[it] = function (){ let next = ()=>{ return {value:....,done:....} } return {next}; }
注:使用for...in也可以迭代Object對象,使用Object.keys(obj)可獲取對象的key數(shù)組
是一種異步操作的解決方案,假設(shè)op1()和op2()是異步操作(回調(diào)函數(shù)),op2()需要依賴op1()先執(zhí)行。則op1和op2不能順序執(zhí)行,op2應(yīng)該位于op1的函數(shù)體中,如下例:
setTimeout(function op1(){ // do something... setTimeout(function op2(){ // do something... },2000); },2000);
如果回調(diào)層數(shù)過多,則會給編程帶來很大麻煩。使用promise解決方法如下:
// 定時器1的業(yè)務(wù)代碼
function func1(){ console.log('func1 do something'); }
// 定時器2的業(yè)務(wù)代碼
function func2(){ console.log('func2 do something'); }
function timeoutTask(timeoutFunc,delay){ // 返回一個Promise對象,其初始化參數(shù)為一執(zhí)行體(函數(shù)),倆參數(shù)分別表示執(zhí)行成功和失敗 return new Promise((success,error)=>{ setTimeout(function (){ timeoutFunc(); success(); // 執(zhí)行成功 },delay); }) }
// then方法接收兩個執(zhí)行體,分別對應(yīng)上一步執(zhí)行成功和失敗的回調(diào),then方法可以鏈式調(diào)用
timeoutTask(func1,2000).then(()=>timeoutTask(func2,2000),null);
是最常用異步操作的解決方案, async是配合promise使用的。
await后可以跟一個promise對象,只有promise對象resolve了,此表達式才會向下執(zhí)行
實例:Ajax異步獲取用戶個人信息,和用戶的文件列表,而且獲取文件列表的前提是已獲取用戶信息。
(async function (){ // 使用async修飾函數(shù) let user = await getUser(123); // 只有await關(guān)鍵字后的Promise對象為resolve狀態(tài),才會向下執(zhí)行,await表達式會返回resolve()的參數(shù)(即promiseValue) let files = await getFiles(user); // use files data... })(); // 若不手動返回promise對象,async函數(shù)會強制返回一個promise對象,原本的返回值會作為promiseValue
await只能用在async塊中。
定義類
class Foo{ static msg = "Foo class"; // 靜態(tài)屬性 static getMsg = () => msg; // 靜態(tài)方法 constructor(){ // 構(gòu)造函數(shù) this.name = 'foo'; } setName(name){ // 普通函數(shù) this.name = name } }
在類定義中的普通函數(shù)會自動原型化,再也不用手動操作原型了;靜態(tài)屬性相當于直接在類對象(非實例對象)本身添加
const foo = new Foo(); foo.setName('bar'); // ok Foo.setName('bar'); // Foo.setName is not a function Foo.msg = 'abc class'; // ok foo.getMsg(); // foo.getMsg is not a function
class Bar extends Foo {
}
機制幾乎和Java一模一樣;
支持super();
淺復(fù)制:一層復(fù)制,復(fù)制單層元素的值
深復(fù)制:多層遞歸復(fù)制
注:只有進行了成員復(fù)制才能算拷貝,一般的對象間賦值只是指針的傳遞,根本不算拷貝。
例:
let a = [1,{name:"zhangshan"}]; let b = a.concat(); // concat是淺復(fù)制 b[0] = 2; b[1].name:"lishi"; console.log(a); // [1, {name: "lishi"}]
注:concat是淺復(fù)制,分別復(fù)制每一個元素的值,對于值類型的元素,復(fù)制其值,對于對象,復(fù)制其地址。
深復(fù)制的實現(xiàn):
function deepCopy(data){ let ret; if(data instanceof Array){ ret = []; } else if(data instanceof Object){ ret = {}; } else { return data; } for(let i in data){ // i為key或索引,如果是for...of,則i為值 ret[i] = deepCopy(data[i]); } return ret; }
使用JSON也可實現(xiàn)深復(fù)制