1. 程式人生 > 實用技巧 >js筆記合集

js筆記合集

history:

用來控制網頁前進和後退,根據的是網頁歷史紀錄

history.back(); //後退
history.forward(); //前進

無重新整理更改URL:

history.pushState(data:json,title:string,url:string); // 會儲存在url歷史中
history.replaceState(data:json,title:string,url:string); // 不會儲存。。。
data是你要存放的資料,可以使用history.state獲取,title是標題,為空則不改變,url是新url

location:

用來控制頁面跳轉
location.replace("xx"); //跳轉


location.href = 'xxxx'; //同上
location.reload(); //重新整理頁面

定時器:

  • var id = setInterval(callback,ms); //每隔ms毫秒執行一次函式(回撥函式只寫函式名)
  • var id = setTimeout(callback,ms); //在ms毫秒後執行一次函式
  • clearInterval(timer); //清理掉setInterval定時器
  • clearTimeout(timeout); //讓setTimeout定時器失效
  • window.requestAnimationFrame(callBack); //專門為動畫設定的定時器(效果比setInterval流暢,每秒執行60次,大部分瀏覽器中,每秒執行次數和顯示器重新整理率一致)

事件繫結的方法:

直接使用元素的onclick屬性:

<button onclick="btnhandler(this)"> click me </button>

繫結一個事件:

getElementById("xx").onclick = function(e){
    xxxxxxxx;
}

事件監聽:

document.getElementById("xxx").addEventListener('click',function(e){
    xxxxxx;    
})

注意:事件名稱前不加on

區別:
使用內聯onclick屬性似乎只能傳遞自身而不能傳遞事件物件

事件監聽可以在一種事件上繫結多個方法

注意:
當元素是動態生成的時候,在元素生成前繫結的事件無效,如:

$('.btn').click(function (e){ ...... });
$(‘body’).append('<button class="btn">...</button>');    // 單擊事件不能繫結到新的元素上面

手動呼叫事件:

element.onclick() || element.onclick.call() ;
jquery方式:$(sel).click();

onchange 當表單的值改變時觸發(需滑鼠抬起,不是即時的)
oninput 當表單控制元件受到改變時觸發(即時的)

事件物件:

事件物件自動傳遞給回撥函式 element.onclick = function(e){}; // e就是事件物件
e的常見屬性:
e.target; //獲取觸發此事件的元素(不一定是繫結元素)
e.currentTarget //獲取觸發此事件的元素(一定是繫結元素)
e.offsetX ||e.offsetY ; //獲取滑鼠基於target元素內部的偏移x和y
e.clientX ||e.clientY ; //獲取滑鼠基於瀏覽器視窗的偏移x和y
e.keyCode ||e.which; //返回鍵盤上的字元的程式碼
事件回撥中的this:指向事件的觸發元素

----如果事件處理函式的繫結在元素生成之前,則此元素不能繫結事件處理函式,需重新設定


獲得和設定元素的行內樣式(只針對行內樣式):


var a = document.getElementById("xxx").style.width;
document.getElementById("xxx").style.width = '500px' //注意加上單位

獲得作用在元素身上的樣式:

原生方式:

element.currentStyle.width || getComputedStyle( element,null).width ;
前者是ie專用,後者是w3c標準,注意:用這種方法只能獲取不能設定

jquery 方式:

$(sel).css(styleName); // 注意資料型別以及單位
注:行內樣式一定是起作用的樣式,所以用js設定元素樣式都是設定行內樣式

js獲取元素位置及大小:

  • element.offsetTop; // 獲取元素相對於定位父元素(left,top屬性基於的元素)的頂部的距離
  • element.scrollTop // 元素的滾動距離(該元素需要有滾動條,如果沒有則為0)【可寫】
  • element.clientTop // 不常用

  // 使用變換(transform)不會改變上述屬性的值
  // 它們受left,top,margin等css屬性的影響,但不一定等於它們

  • element.offsetHeight // 元素的高度(包括邊框,滾動條),返回無單位的number值
  • element.clientHeight // 不包括邊框滾動條的高度,如果邊框無滾動條,則同上
  • element.scrollHeight // 如果有滾動條,則獲取滾動內容的高度,沒有滾動條則同上【可寫】

  // 建議使用offsetHeight,因為其他元素排列位置時是會考慮邊框和滾動條的

  offsetParent // 用於定位的基元素(預設為最近的設定過position的父元素)


滾動動態載入內容:

window.onscroll = function(e){    // 頁面滾動事件(一般加給window)
    // 頁面被捲起來的高度距離頂部或底部的距離
    var juan = document.documentElement.scrollTop;    // 獲取頁面被捲起來的高度,documentElement相當於html標籤
    var total = document.documentElement.scrollHeight;    // 獲取頁面總高度
    var visul = window.innerHeight;    // 獲取可見區的高度(即瀏覽器顯示出來的高度)
    var bot = total - juan - visul;    // bot就是可見區下面的高度(這是我們需要的)
    ........    // 當bot小於某值時,載入新元素
}

注:document的型別是Document,document.documentElement才是常規的Element型別的DOM元素
注:onscroll事件觸發次數比較頻繁,可以考慮節流(一段時間內只執行一次)

js實現拖動:


需要給被拖動元素和window同時新增mousemove事件,因為即使拖動時滑鼠在元素之外,也應該能實現拖動。
具體實現:
1.滑鼠按下目標元素時,儲存clientX,clientY,以及被拖動元素的引用
2.滑鼠移動時,設定被拖動元素的left為當前clientX - 預先儲存的clientX,clientY同理
3.釋放滑鼠時,清除之前儲存的資料

獲取body標籤對應的DOM :document.body


獲取html標籤對應的DOM :document.documentElement

圖片懶載入:

就是先不設定src,而是將路徑放到其他屬性中(如data-src),等到圖片處於顯示區或接近顯示區時,再設定src

HTML DOM事件在:http://www.runoob.com/jsref/dom-obj-event.html

右鍵點選事件

oncontextmenu

表單繫結事件的考慮:

  • onkeydown // 按下按鍵時立即觸發,該事件一般繫結在document/window上,因為即使被繫結的表單沒有獲得焦點,該事件也會執行
  • onkeypress // 按下按鍵時立即觸發,只有被繫結的元素獲得焦點了,才會執行事件(適用於動態search)
  • onchange // 表單值改變時執行,按下按鍵時不是立即觸發,而是等到輸入完畢時才會觸發(輸入完畢指的是按下回車或表單失去焦點)
  • oninput // 表單值改變時立即觸發

例項:

window.keydown = function(e){    //鍵盤事件一般和視窗繫結
    var ev = window.event || e;    //獲得事件物件(IE和其他瀏覽器均可用)
    var word = String.fromCharCode(ev.keyCode);    //將ascii碼轉換成字元
    alert(word);
}

動畫事件:

  事件        描述

  • animationend 該事件在 CSS 動畫結束播放時觸發
  • animationiteration 該事件在 CSS 動畫重複播放時觸發
  • animationstart 該事件在 CSS 動畫開始播放時觸發
  • transitionend 該事件在 CSS 完成過渡後觸發。

  目前,需要根據瀏覽器種類加字首

事件流:


當元素位置重疊的時候,點選重疊的位置,每個元素的事件會按照一定的順序觸發。
若只想讓第一個事件觸發,則可在那個事件的方法體中加入以下程式碼:
e.stopPropagation(); //中斷此次事件的後續操作
若顯示層級位於表層的元素沒有事件而裡層元素有繫結事件,那麼事件不會觸發,因為事件流不是一個通路。這在給父元素加滑鼠事件時很常用

事件相互影響的問題:
例如:如按空格讓視訊暫停,在文字框中輸入空格也可能會讓視訊暫停,這是因為事件冒泡到上級,只需要在文字框上的鍵盤事件上中斷事件流即可

事件委託:

例如,在一個ul中為要每個li設定點選事件,只需給ul設定點選事件即可,li的事件會冒泡至ul上,通過this|e.target獲取li的DOM物件

瀏覽器預設事件:
如:點選a標籤後,瀏覽器會預設跳轉到指定頁面;點選滑鼠右鍵,會彈出上下文選單等


阻止預設事件:

建立onclick事件方法,加入var ev=window.event; ev.preventDefault();

阻止a標籤的預設事件:

<a href="javascript:void(0)">連結</a>

在事件處理函式中this的用法:

this在事件處理函式中指向事件源物件(觸發事件的元素物件)
當元素被批量選中時,this指標對這些元素的事件處理非常有用
使用e.target也能獲取事件源物件 // e表示事件物件
e.currentTarget表示最原始的觸發這個事件的物件(根據冒泡機制,只要條件符合,子元素也會觸發父元素身上的事件)

深入理解事件機制:


事件的傳遞分成三個階段:
PHASE1.捕獲:事件從祖先元素開始(從window開始)向內傳遞
PHASE2.目標:事件已經到達目標(最內層的命中事件的元素)
PHASE3.冒泡:事件向外(祖先)冒泡至window
預設的偵聽器是偵聽冒泡階段的事件。

以點選事件為例,滑鼠點選後,會從window物件開始,檢索其子物件的區域是否包含點選點,html,body...直到某個元素的所有子元素的區域都不包含該點但自身包含該點,則此元素為目標元素,接著,從目標元素開始,依次執行點選事件回撥,直到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中時間日期是以物件的形式實現的
var time = new Date(); //獲得客戶端當前的時間,返回一堆字串,還可以用時間戳構造(注意:客戶端的時間可能是不準確的)
var time = new Date(年,月,日,時,分,秒); //建立一個具體的時間
方法:

  • getTime(); //獲得從1970年1月1日到物件時間的毫秒數(時間戳)
  • get年月日時分秒的英文(); //獲得對應的時間橙分,如getDate()獲得日數
  • Date.now() 返回毫秒級時間戳
  • toLocalDateTimeString() // 返回本地化的日期時間字串(對於北京時間,會變成12小時制)

抽取字串中的數字:

parseInt(str); // 會提取字串中的整數部分,遇到非整數會立即停止提取;適合去掉css中的單位
parseFloat(str) // 同上,可以提取小數

Number.toFixed(n) // 保留n位小數,為0則只保留整數
Number.round() // 返回最接近的整數(相當於四捨五入)
Number.floor() // 返回最接近的,小於本值的整數;如對於-5.9,返回-6,對於5.9,返回5


定義一個匿名函式,自動執行:

(function (){
    //程式碼塊
}());

或者

(function (){
    // 程式碼塊
})();


可以在前面加上 ; 提升可靠性

常用DOM操作:

  • appendChild(e) // 尾插
  • insertBefore(e,ch) // 在子節點ch前插入e
  • removeChild(e) // 刪除子節點
  • replaceChild(new,old) // 替換子節點
  • ------------------------------------------------------
  • parentNode // 父節點
  • children // 子節點集合
  • childNodes // 子節點集合
  • firstChild // 第一個子節點
  • lastChild // 最後一個子節點
  • previousSibling // 前一個兄弟節點
  • nextSibling // 下一個兄弟節點
  • -------------------------------------------------------
  • setAttribute(name,value) // 設定元素的屬性
  • getAttribute(name) // 獲取屬性
  • hasAttribute(name) // 屬性是否存在
  • -------------------------------------------------------
  • dataset // 獲取以data-開頭的屬性(返回物件,只讀)
  • classList // 獲取類名(類陣列物件,只讀)

jquery篇

—————————————————————————————————————————————————

獲取元素:

兄弟:$(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是元素,只有一個引數時,表示序號
// 程式碼
}

$.each(obj,function (i,e)){    // i是序號,e是元素,只有一個引數時,表示序號
// 程式碼
}

注:js中陣列API的回撥函式通常是function(e,i){},即序號在後

DOM節點操作:

  • append('xxxx'); // 在節點文字後新增內容,返回原來的jQuery物件,而不是新增的
  • appendTo(jQuery) // 將元素節點新增至指定jQuery物件的尾部
  • prepend('xxxx'); // 在節點文字前新增內容,返回原來的jQuery物件,而不是新增的
  • before('xxxx'); // 在節點之前新增內容(一般是新節點)
  • after('xxxx'); // 在節點之後新增內容(一般是新節點)

關於表單元素:注意table標籤內會自動加一個tbody標籤,獲取<tr>元素:$('table tbody tr');

jquery動畫:

hide(time,callback) show(...) fadeIn(...) fadeOut(...)
toggle動畫:toggle() toggleFade() toggleSlide() 指根據display來判斷做什麼動畫

animate(json,timer,[timeFunc,callback]);
obj:格式是{attrName:styleValue, ...},表示元素要達到的樣式
timer:int型別,表示動畫的毫秒數
timeFunc:速度函式,有ease,linear等
callback: 回撥函式

獲取內容:

html() // 獲取或設定innerHTML
text() // 獲取或設定innerTEXT,會自動轉義
val(); // 獲取或設定value(只能用於表單)

獲取寬高和位置:

position(); //獲取匹配元素相對父元素的偏移,返回的物件包含兩個整型屬性:top 和 left
height();
width();

返回數字,單位為px

jquery物件轉化為DOM元素物件:

jquery物件不能使用DOM物件的屬性和方法,jquery是對dom物件的封裝,使用jquery的0號元素即可獲得原來的DOM物件
$(sel)[0].setEventListener(....);

定義一個在頁面載入完成後立即執行的函式:

$(function (){
    // 函式體
});

函式中的this:

指向主調物件,或者window,其值是在執行時可知的

函式物件和例項物件:

函式物件:即Function型別的物件
例項物件:new 函式名()後生成的物件

為函式物件新增方法:

function Foo(){
    this.func = function (){    // 這是為例項新增方法:var obj = new Foo(); obj.func()
      ...
    }

    function func(){    // 沒有為任何物件新增方法,該函式僅在foo內部可用,這是錯的:foo.func() (×)
      ...
    }
}

Foo.func = function (){    // 這是為函式新增方法:foo.func()
  ...
}

js原型:

·所有函式都有prototype物件
·prototype物件是一個Object型別的物件
·該object物件中有一個constructor物件,指向該函式物件
·可以為prototype物件設定屬性,這些屬性實際是給例項物件訪問的

示例:

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型別的,該屬性和其函式原型(建構函式)的prototype屬性是一樣的,它們共享一份Object物件,即函式物件.prototype = 例項物件.__proto__,在上述例子中就是func.prototype === f.__proto__

使用物件屬性(或方法)時,先測試物件本身有無此方法,若沒有,則在__proto__中查詢,直到找到,或不存在。這種查詢鏈就是原型鏈(原型鏈使用的是__proto__而不是prototype)。

例項(接上例):
f.toString();
// func中無toString方法,其原型中也無,於是通過__proto__到Object的原型中找,在Object中找到了toString方法,。

Function和Object的原型:

·Function和Object是系統內建的函式。
·所有函式物件都是Function型別的例項(通過new Function()得到)
·Object是內建的函式物件,也是Function型別的例項物件
·Function也是函式物件,它也是Function型別的例項物件
由以上三點可知:
·所有的函式物件都有prototype和__proto__兩個屬性,有prototype是因為所有函式都有prototype物件,有__proto__是因為它是Function型別的例項
·所有函式物件的__proto__都等於Function物件的prototype,因為所有函式物件都是Function物件的例項
·Function物件的prototype和__proto__是相等的
·prototype和__proto__的型別是Object,而Object本身也有prototype和__proto__屬性,Object的__proto__屬性等於Function物件的prototype(前面說過),Object物件的prototype屬性中有很多內建的方法:

  • constructor: Object()
  • hasOwnProperty: hasOwnProperty()
  • isPrototypeOf: isPrototypeOf()
  • toString: toString()
  • valueOf: valueOf()

Object物件的prototype屬性不是Object型別的,而且該屬性的__proto__屬性為null,它是原型鏈的終點。


恆成立(假設使用者定義了一個函式函式,名為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

instanceof探幽:

L instanceof R當且僅當

L.__proto__......__proto__ === R.prototype

至少有一個__proto__

函式執行上下文:

函式(或全域性程式碼)執行前,會初始化上下文,包括:

  • 確定this
  • 變數提升:var定義的變數會在同級程式碼執行前先初始化為undefined
  • 函式提升:函式定義會在同級程式碼執行前先初始化
  • 變數提升先於函式提升

例:

function foo(){
    console.log(c)    // undefined
    var c = 1;
}

注:如果沒有定義變數,就直接使用,會在作用域鏈上查詢,而不是在自身作用域上建立。
例1(以下是全域性程式碼):

function foo(){
    username = 'zhangshan';    
}

會設定window.username為'zhangshan',與函式中的this是誰無任何關係

例2:

function foo(){
    this.username = 'zhangshan';    
}

直接呼叫foo()時,效果同上(直接呼叫某個函式時,呼叫者一定是windows)


閉包:

js支援函式的巢狀定義,內部的函式叫子函式,外部的函式叫父函式。
當子函式引用了父函式中的變數,就會在子函式中產生一個閉包,包含了被引用的變數。
來看這個例子:

function foo(){
    var msg="hello";
    return function(){
        return msg + " world";
    }
}

var a = foo();
console.log(a());    // a能否正確使用msg?

foo()執行完後,變數msg應該被釋放,但是子函式引用了msg,產生了閉包,所以msg的生命週期變長,不會被釋放,所以執行a()可以正確輸出msg的值。
產生閉包需要同時滿足:
1.存在函式巢狀
2.子函式中使用了父函式的變數
3.呼叫父函式


事件輪詢:

js是單執行緒的。
定時器回撥,DOM事件回撥,Ajax回撥都會放在回撥佇列中,待程式順序執行完畢時,才會執行。
注:並不是回撥都是非同步任務,比如Promise()的引數function會同步執行

Object物件:

Object.create(obj,[property])
//使用指定物件作為__proto__產生一個新物件。


Object.defineProperty(obj,propname,conf)
// 給obj定義屬性propname,conf為配置項。
// 該函式可以監視某個物件的改變,這是很多MVVM框架實現資料繫結的原理


Object.assign(target,source);
// 複製source的所有屬性到target並返回


詳詢:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object

Function物件:

Function.property.call(obj,param);
Function.property.apply(obj,[params]);
Function.property.bind(obj,param);

//均是 給函式指定this,其中bind不會執行,而是返回該函式。

new探幽:

使用某個建構函式new一個物件A實際上就是設定物件A的__proto__為建構函式的prototype,然後將this設定為A來執行建構函式。
手動實現new的方式:

function NEW(f){    // f為建構函式

    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);

賦值說明:

如果某個變數(或表示式的結果)的值為undefined,null,'',0,false,則為假值,非上述值則為真值
即js的假值有多種,但!假值都是true,同理真值有無數種,但!真值都是false
空物件{}和空陣列[]為真值

js的與或運算(&&,||)並不產生true或false,而是:
在處理或運算時,返回第一個為真的值,若全為假,則返回最後一個假值
在處理與運算時,返回第一個為假的值,若全為真,則返回最後一個真值
在處理非運算時,一定返回true或false
不同的假值用是不等的(用==判斷會返回false)

例:
var obj = createSomething() || {}; // 若createSomething()返回假值,則將obj初始化為{}

例(將雙等==改成全等===的結果也是一樣的):

[undefined] == [undefined]    // false,實際上無論數組裡面是什麼,該表示式始終返回false
undefined == false    // false
undefined || false    // false
!undefined == true    // true
undefined && false    // undefined
false && undefined    // false
undefined || null    // null
'11.000' == 11    // true

ES6:

let關鍵字:

和var一樣,定義變數,區別是:
1.支援塊級作用域
2.不能變數提升

const關鍵字:

定義常量,不能修改

解構賦值:(假定obj為 {name:'zhangshan',age:20})

例1:

let {name,age} = {'zhangshan',15}    // 建立name,age兩個物件並依次賦值
let {name,age} = obj    // 建立name,age兩個變數,並按obj中的欄位名賦值
let [a,b,c] = [1,2,3]    // 建立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}

可變引數(rest):

function foo(...value);    // value會變成引數陣列
function foo(arg1,...value);    // value會變成除arg外的引數陣列

...運算子:

...expression  // 如果...後接一個可迭代物件,則此語句的作用是將該物件的所有可迭代屬性變成當前物件內的屬性

例:

var obj = {

  ...func();

}

如果func()函式返回一個可迭代物件,則上述語句表示將func()返回的物件中的所有屬性變成obj的

預設引數:

function point(x = 0,y = 0){

};

箭頭函式(lambda):

let foo = (params)=>{
  statment;
}

foo(123);
注:lambda表示式中的this為它所在作用域中的this,且不能用call()更改,而一般函式的this需要在執行時確定.
當只有一個引數和一行語句時可簡略:param => expression;

雙箭頭:

let bar = param1 =>param2 => { statment;}

表示外層的箭頭函式的返回值是一個函式物件,也就是內層的箭頭函式
即執行bar()會得到內層的函式物件

iterator和for...of:

for...of語句可以遍歷實現了Iterator介面的物件(可迭代物件)。
例:

let arr = [2,3,4,5,5,6];
for(let e of arr){
// using e;
}

陣列,偽陣列(類陣列),set,map實現了Iterator介面,Object沒有實現該介面,但可以手動實現。
手動實現Iterator的方式:需要實現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陣列

promise:
是一種非同步操作的解決方案,假設op1()和op2()是非同步操作(回撥函式),op2()需要依賴op1()先執行。則op1和op2不能順序執行,op2應該位於op1的函式體中,如下例:

setTimeout(function op1(){
    // do something...
    setTimeout(function op2(){
        // do something...
    },2000);

},2000);

如果回撥層數過多,則會給程式設計帶來很大麻煩。使用promise解決方法如下:


// 定時器1的業務程式碼

function func1(){
    console.log('func1 do something');
}


// 定時器2的業務程式碼

function func2(){
    console.log('func2 do something');
}

function timeoutTask(timeoutFunc,delay){
// 返回一個Promise物件,其初始化引數為一執行體(函式),倆引數分別表示執行成功和失敗
    return new Promise((success,error)=>{

        setTimeout(function (){
            timeoutFunc();
            success();    // 執行成功
        },delay);

    })
}

// then方法接收兩個執行體,分別對應上一步執行成功和失敗的回撥,then方法可以鏈式呼叫

timeoutTask(func1,2000).then(()=>timeoutTask(func2,2000),null);

async和await關鍵字:

是最常用非同步操作的解決方案, async是配合promise使用的。
await後可以跟一個promise物件,只有promise物件resolve了,此表示式才會向下執行
例項:Ajax非同步獲取使用者個人資訊,和使用者的檔案列表,而且獲取檔案列表的前提是已獲取使用者資訊。

(async function (){    // 使用async修飾函式
    let user = await getUser(123);    // 只有await關鍵字後的Promise物件為resolve狀態,才會向下執行,await表示式會返回resolve()的引數(即promiseValue)
    let files = await getFiles(user);
    // use files data...

})();    // 若不手動返回promise物件,async函式會強制返回一個promise物件,原本的返回值會作為promiseValue

await只能用在async塊中。

class關鍵字:

定義類

class Foo{
    static msg = "Foo class";    // 靜態屬性
    static getMsg = () => msg;    // 靜態方法
    constructor(){    // 建構函式
        this.name = 'foo';
    }
    setName(name){    // 普通函式
        this.name = name
    }
}

在類定義中的普通函式會自動原型化,再也不用手動操作原型了;靜態屬性相當於直接在類物件(非例項物件)本身新增

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

extends繼承:

class Bar extends Foo {

}

機制幾乎和Java一模一樣;
支援super();

淺複製和深複製:

淺複製:一層複製,複製單層元素的值
深複製:多層遞迴複製


注:只有進行了成員複製才能算拷貝,一般的物件間賦值只是指標的傳遞,根本不算拷貝。
例:

let a = [1,{name:"zhangshan"}];
let b = a.concat();    // concat是淺複製
b[0] = 2;
b[1].name:"lishi";
console.log(a);    // [1, {name: "lishi"}]


注:concat是淺複製,分別複製每一個元素的值,對於值型別的元素,複製其值,對於物件,複製其地址。
深複製的實現:

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也可實現深複製