javascript 總結筆記

sky_Bosco發表於2018-01-16

時間匆匆, 轉眼畢業已經快兩年了。 最為一個前端小白的我, 現在怎麼說也是灰色的了吧? 前段時間一直有個想法: 想把自己學到的前端知識點整理下,感覺這樣有助於自己基礎的鞏固。 所以才有個今天這個js知識點回顧文章。 好吧,廢話少說了, 先從javascript開席吧!

前言:

畢業的時候本來不是做前端的。 作為一個後臺培訓出來的應屆生。 匆匆培訓java、html、 css、 java2ee 、ssh等一大堆東西。第一家公司是來上海做後臺。 記得有一次專案中老大說讓我整理個頁面, 當時懵逼了都。 什麼? 讓我寫個ajax ? !雖然培訓過前端方面的知識, 但是也只是匆匆而過,有個大概的瞭解。 沒辦法只能硬著頭皮上網上找資料啊。 心想如果做不出來,會不會被開除啊?( 我想每個實習期的童鞋都有這樣的經歷吧 哈哈...)這就算是我第一次真正接觸前端。 嗯 ,沒毛病。

正文:

1、 JavaScript簡介

JavaScript 和java 沒個卵的關係。 ECMAScript就是對實現該標準規定的各個方面內容的語言的描述。

JavaScript實現了ECMAScript。 所以什麼es5 es6 es7 是ECMAScript5 6 7 的簡寫。 至於其它概念,大概看下就行。(只是本人自己的看法)!

JavaScript在當前五個主要瀏覽器( IE、 Firefox、 Chrome、 Safari和 Opera) 中 都得到了不同程度的支援。 所以才有了相容性的問題。 但人類的腳步是向前的。 比如babel 編譯器解決了這種差異。統一編譯成es5。呵呵噠!


2、 推薦在javaScript 中使用 "use strict" 。why ? 讓我就舉個栗子:

"use strict"; //在正常模式中,如果一個變數沒有宣告就賦值,預設是全域性變數。嚴格模式禁止這種用法,全域性變數必須顯式宣告。

v = 1; // 報錯,v未宣告

for (i = 0; i < 2; i++) { // 報錯,i未宣告
}
//因此, 嚴格模式下, 變數都必須先用var命令宣告, 然後再使用。

function f() { 
return this; //非嚴格模式下 因為"this"指向全域性物件,返回全域性物件
}

function f() { 
"use strict"; //這行程式碼看起來像是字串,而且也沒有賦值給任何變數,但其實它是一個編譯指示(pragma),
// 用於告訴支援的 JavaScript引擎切換到嚴格模式
return this; //嚴格模式下 this的值為undefined,
}
複製程式碼


嚴格模式下方法入參不能有重複定義的變數、 物件不能有重複的屬性。。。

而且有時候  "use strict"可以節省函式執行的時間。

推薦語句以一個分號結尾。(以前做的一個vuejs專案 eslint 不能用; 結尾 結果我們公司有個大大牛就問了 你們js為什麼語句不以分號結尾都新增上 加上!!!。。。。。好吧 結果我們把eslint關了哎。。。)

3、 js 基本資料型別

字串 string var const let

數值 Number Number()

真假 Boolean new Boolean()

陣列 Array new Array() | []

物件 Object new Object | {}

NaN isNaN(a) //轉成Number型別進行判斷 so alert(isNaN("10")); //false(可以被轉換成數值 10)


var num2 = Number(""); //0 
var num4 = Number(true); //1 
var num3 = Number("000011"); //11
複製程式碼

what you find that ?

so not use Number use parseInt()

var num2 = parseInt(""); // NaN 
var num1 = parseInt("1234blue");
// 1234    剛發現 666
var num4 = parseInt(22.5); // 22 
var num5 = parseInt("070"); // 56(八進位制數)

var num6 = parseInt("70"); // 70(十進位制數)

var num7 = parseInt("0xf"); // 15(十六進位制數)
複製程式碼


false 的8個值: ''

0、 - 0、 NaN、 false 、null、 undefined、 newBoolean()


問個問題:

[] === '' 
[]== ''
複製程式碼

結果是什麼? why?


typeof 返回的是字串, 有六種可能:

"number"、

"string"、

"boolean"、

"function"、

"undefined"、

"object": {

[],

{}

}

其中 null型別
 typeof null === "object" 
複製程式碼


so 判斷變數是陣列還是物件的時候 要過濾null。

eg:

if (isWaitting && isWaitting instanceof Array) {}
複製程式碼


舉個例子吧:

var shallowCopy = function(obj) { //一個淺拷貝方法
應該知道為什麼有這個方法吧 (陣列、物件中地址引用的鍋)
if (typeof obj != 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;

}
複製程式碼


再來個深拷貝: //記得gitHub 上有一個deep-assign npm可以install哦 有需要的大家可以搜下

var deepCopy = function(obj) {
if (typeof obj !== 'object') return; //null NaN Math
var newObj = obj instanceof Array ? [] : (!obj ? null : {});
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) :
obj[key];
}
}
return newObj;
}
複製程式碼

4、 在html引入js要注意的事項

肯定要寫在 < script > < /script>裡面嘍!沒話說的。如果瀏覽器不支援js的話 那用

<noscript ><p> 本頁面需要瀏覽器支援( 啟用) JavaScript。 </noscript>

我是至今也沒見過瀏覽器顯示這個的。 除非你把瀏覽器設定了不支援js語法。
這樣當我沒說!!


js一般引入的位置是在body後面而不是header裡面。 因為瀏覽器渲染的過程中是單執行緒的。 當js載入完成並執行後才能繼續往下載入別的。
一句話概括吧: 就是你他媽的js載入多的話,

入口頁面渲染完成所用的時間過多, 體驗不要。 嗯, 這個也引發了後面的前端優化什麼的。 有點人說盡量不再js中改變dom節點的更改、 減少http的請求、
使用img snipe、 根據路由懶載入依賴的檔案、 外部引用js檔案的時候加defer( 這個屬性的用途是表明指令碼在執行時不會影響頁 面的構造。也就是說, 指令碼會被延遲到整個頁面都解析完畢後再執行。 因此, 在 < script > 元素中設定 defer 屬性, 相當於告訴瀏覽器立即下載,但延遲執行。

<script type = "text/javascript" defer = "defer"  src = "example1.js" > 
</script> )
複製程式碼


擴充:

同樣與 defer 類似,async 只適用於外部指令碼檔案, 並告訴瀏覽器立即下載檔案。 但與 defer 不同的是, 標記為 async 的指令碼並不保證按照指定它們的先後順序執行。 例如:

<script type = "text/javascript"  async src = "example1.js" >
< /script> 
<script type = "text/javascript"  async src = "example2.js" >
< /script> 
複製程式碼


在以上程式碼中, 第二個指令碼檔案可能會在第一個指令碼檔案之前執行。 因此, 確保兩者之間互不依賴 非常重要。

指定 async 屬性的目的是不讓頁面等待兩個指令碼下載和執行, 從而非同步載入頁面其他內容。

為此, 建議非同步指令碼不要在載入期間修改 DOM。 非同步指令碼一定會在頁面的 load 事件前執行,但可能會在 DOMContentLoaded 事件觸發之前或之 後執行。
支援非同步指令碼的瀏覽器有Firefox 3.6、 Safari5 和 Chrome


5、 來個運算子吧

var a = 0;
a++;
++a;
複製程式碼

老生常談了


var a = 0;
a + +1 輸出什麼?
//1 why?

'' + 1
1 + ''
'1' + '2'
1 + true

輸出什麼?

var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2; // 輸出什麼?

0.1 + 0.2 // 輸出什麼? 應該怎麼做才能不丟失精度呢?考慮
複製程式碼

6、 Boolean


以前常常看到人們判斷一個變數是否為空 是否是沒定義

if( a != null && typeof a != 'undefined') {}
複製程式碼

忘了上面的‘ false 的8個值’ 了嘛? 今天以後就可以這樣寫

if(a) {} // a 不能是 0 -0 NaN '' false null undefined
複製程式碼

特殊情況特殊對待


!!!a //強制轉化a的型別為Boolean型別
複製程式碼


追加:


a && b //短路運算 a如果為false 那麼b不用判斷了 懶!

var c = a || 'fd' //預設賦值 a如果為false 那麼c賦值為‘fd’ 

Infinity //瞭解下就行 我都沒遇到過 無窮大
-
-Infinity //無窮大的負數 你說是什麼呢?

for (;;) { // 無限迴圈 doSomething(); } 
for (true) { // 無限迴圈 doSomething(); } 

continue; break; //只能使用在迴圈遍歷中 // foreach() map()裡能用不 ?思考下
 
複製程式碼

7、 函式function

函式中的結束: return;

函式中沒有java的過載。 只有替換

函式中沒有類但能模擬類


//es5

var superClass = function() {
this.name = null;
}

superClass.prototype.setName = function(name) {
this.name = name;
}
superClass.prototype.getName = function() {
return this.name;
}
var superC = new superClass();
superC.setName('fd');
superC.getName();

var subClass = superClass.prototype; //繼承
subClass.prototype.mackSound = function() {
this.name = '電風扇';
}
複製程式碼


//es6

class superClass2 {
constructor() {
this.name = name;
}
getName() {
return this.name;
}

setName(name) {
this.name = name;
}
show() {
console.log(this.name);
}
}
var as = new superClass2();

as.setName('fdsf');

as.show();
class superClass3 extends superClass2 { //繼承
}
var sdf = new superClass3();
複製程式碼


8、閉包

(function sd() { console.log(12) })()//自動執行函式 
複製程式碼


function adds() {
var something = "ssh";
var another = [1, 2, 3];

function doSomething() {
alert(something);
}

function doAnother() {
alert(another.join(","));
}
return {
 doSomething: doSomething,
doAnother: doAnother
};
}
var fn = adds();
fn.doSomething(); // cool
fn.doAnother(); // 1 ! 2 ! 3
//私有資料變數something和another,以及doSomething()
和doAnother() 兩個內部函式,
//它們的語法作用域(而這就是閉包)也就是foo() 的內部作用域。
複製程式碼


var a = (function() {
var i = 0;
return function() {
return i++;
}
})()
複製程式碼


var b = (function(name) {
var i = 0;

function identify() {
alert(name.toUpperCase());
}

function add() {
return i++;
}
return{
identify:identify,
add:add
}
})('ssh')
複製程式碼



9、上下文執行環境 作用域

var a = 10;
function sum(b) {
return a + b;
}

function add() {
var a = 20;
var c = sum(30);
return c;
}

add(); //40
複製程式碼


why?為什麼不是50呢?我的理解是:當函式沒有執行前,函式中的變數就已經確定指向。比如sum()中 當沒有呼叫add()方法的時候,sum()方法裡的a已經的指標已經指向了全域性作用域下定義的var a = 10;而非呼叫時候的var a = 20;


總結:函式建立的時候 就已經確定了作用域 . 要到建立這個函式的那個作用域中取值——是“建立”,而不是“呼叫”,切記切記.

上面描述的只是跨一步作用域去尋找。如果跨了一步,還沒找到呢?——接著跨!——一直跨到全域性作用域為止。要是在全域性作用域中都沒有找到,那就是真的沒有了。

這個一步一步“跨”的路線,我們稱之為——作用域鏈。

我們拿文字總結一下取自由變數時的這個“作用域鏈”過程:(假設a是自由量)

第一步,現在當前作用域查詢a,如果有則獲取並結束。如果沒有則繼續;

第二步,如果當前作用域是全域性作用域,則證明a未定義,結束;否則繼續;

第三步,(不是全域性作用域,那就是函式作用域)將建立該函式的作用域作為當前作用域;

第四步,跳轉到第一步。


var x = {
name:'ssh',
age:18
}

var fn = function(){
console.log(this);
console.log(this.name);
}
fn.call(x);//fn.apply(x)
複製程式碼


//當一個函式被call和apply呼叫時,this的值就取傳入的物件的值


既然說到了call apply 那就再繼續說點吧

用call()apply() 可以強制指定this的指向。那麼this又是什麼玩意呢?代表當前作用域本身。在全域性作用域下

javascript 總結筆記


在函式塊級作用域下

javascript 總結筆記

這樣應該明白了吧


有一天看到了一個這種寫法

var arr = [1,2,4,5,7,34]
Math.max.call(null,1,2,4,5,7,34) //34
Math.max.apply(null,arr) //34
複製程式碼

從上面可以看出兩個東西:

1、call apply 接收引數的格式不同 怎麼不同? 自己看

2、重新定義了Math.max函式的this指向

Es6:Math.max(...arr) //瞭解下就行。



上下文執行環境:就是當前函式執行時候所處的環境 環境應該都知道 java開發前都要先配置jre jdk開發環境 我們們的javascript中 函式執行時候也要有自己的環境。當一個函式執行完成後
當前上下文執行環境就會被銷燬。至於涉及到堆疊方面的知識,技術有限,說不出來。。。。。想知道這樣面的知識可以自行google。


10.地址引用遇到的坑

地址引用的資料型別有:array object


Var arr = [1,2];

自己理解:變數arr 中其實放的不是資料 1,2 。寄存的只是一個hash地址。該地址指向存放[1,2]的資料池。如果重新給arr賦值 arr = [1,3] 則arr指標重新指向了另一個資料池[1,3] 。

但是:當 var arr= [1,2] ;arr.push(4); 這時候指標是不變化的。變化的是指標定向的資料池裡的資料。這點要注意。

記得有個面試題如下:

var arr = [1, 2, 3];
var c = arr;
arr = [1, 2];
console.log(c); //[1, 2, 3]
arr.push(4);
console.log(arr); //[1, 2,4]
console.log(c); //[1, 2, 3]

var arr2= [1, 2, 3];
var c2 = arr2;
arr2.push(4);
console.log(arr2); //[1, 2, 3,4]
console.log(c); //[1, 2, 3]

var obj = [{ name: 'ssh' }, { name: 'ssh2'
}]
var obj2 = [];
obj.forEach((item, index) => {
item.name == 'ssh' ? obj2.push(item) : false;
})
obj2[0].name = 'ssh2222';
console.log(obj) // 有變化嘛如果有 why?
複製程式碼



11、Math

常用的幾種:

Math.abs() //返回數的絕對值。

Math.floor() //對數進行下舍入。 地板 懂?

Math.ceil() //對數進行上舍入。 天花板 懂?

Math. max(x,y) //返回 x 和 y 中的最高值。

Math.min(x,y)// 返回x 和 y 中的最低值。

Math. pow(x,y) //返回 x 的 y 次冪。

Math. random()// 返回 0 ~ 1 之間的隨機數。

Math. round(x) //把數四捨五入為最接近的整數。

Math.trunc(2.643) //2 擷取小數點前面的數字


12、Date

Var a = new Date();
本地全部時間格式:
toLocaleString(); // "2017/11/16 下午4:27:09"
a.toLocaleDateString() //"2017/11/16"
toLocaleTimeString() // "下午4:27:09"
getFullYear() //2017
getTime()
getTime() 返回從 1970 年 1 月 1 日至今的毫秒數。//+new Date()
getDay()
//如何使用 getDay() 和陣列來顯示星期幾,而不僅僅是數字  //週日 =>0 ?
複製程式碼


13、 Array 方法

javascript 總結筆記

主要幾點就行了:

push pop 都是從陣列後面開始處理資料的 (棧)
shift unshift 從頭開始 (佇列)
slice() 相當於重新定義了個陣列 即不同的指標
splice() //從陣列中移除一個或多個陣列,並用新的item代替他們 返回被替換的陣列

var a = ['a','b','c','d'];
var b = a.splice(1,3,'f');
a// ["a", "f"]
b// ["b", "c","d"]
複製程式碼



14、陣列去重的正確編寫姿勢(面試很愛問!)

//使用陣列的indexOf()方法可以很簡單的達到目的。
Array.prototype.unique = function() {
// 建立一個新的臨時陣列,用於儲存輸出結果
var n = []; 
// 遍歷當前陣列
for (var i = 0; i < this.length; i++) {
// 如果當前陣列的第i個元素已經儲存進了臨時陣列,那麼跳過,否則把當前項push到臨時陣列裡面
if (n.indexOf(this[i]) == -1) n.push(this[i]);
// if (!n.includes(this[i]))
n.push(this[i]);
}
return n;
}
複製程式碼


最快姿勢

//把已經出現過的元素通過下標的形式存入一個Object內。下標的引用的實現原理利用的是雜湊演算法,要比用indexOf()搜尋陣列快的多。
Array.prototype.unique = function() {
// n為hash表,r為臨時陣列
var n = {}, r = [];
for (var i = 0; i < this.length; i++) {
// 如果hash表中沒有當前項
if (!n[this[i]]) {
// 存入hash表
n[this[i]] = true;
// 把當前陣列的當前項push到臨時陣列裡面
r.push(this[i]); 
}
}
return r;
}
複製程式碼

但從耗時的角度來講,這是最優的一種解決方式。但是從記憶體佔用角度來說,這並不是最優的,因為多了一個hash表。這就是所謂的空間換時間(世間安得雙全法?)。

中庸姿勢 //推薦

Array.prototype.unique = function() {
this.sort();
var re = [this[0]];
for (var i = 1; i < this.length; i++) {
if (this[i] !== re[re.length - 1]) {
re.push(this[i]);
}
}
return re;
}
複製程式碼


// [...new Set([1,1,3,4,3,56,6])] 最簡單的方法
複製程式碼


既然提到了new Set() 那麼就延伸點別的吧

刪除陣列中指定的項:

Array.prototype.$remove = function (v) {
const index = this.indexOf(v) //this代表陣列本身
if (index !== -1) this.splice(index, 1)
return this;
}

const arr = [1, 2, 3]
arr.$remove(2) //下標從1開始
複製程式碼



Array.prototype.$delete = function (v){
var set = new Set(this);// this 代表該陣列本身
set .delete(v);
return [...set]
}
var arr = [1, 2, 3]
arr.remove(2)// [1, 3]
複製程式碼



es6 新新增了很多方法

如foreach、map 、filter 、reduce 、objectAssign 、copy、 includes 、indexof 、Array.isArray() 、Array.from() new Set() new Map() 等 。

在此不做詳細的說明,想了解的可以自行google。


15、單執行緒中的非同步


我們還經常遇到setTimeout(fn,0)這樣的程式碼,0秒後執行又是什麼意思呢?是不是可以立即執行呢?

答案是不會的,setTimeout(fn,0)的含義是,指定某個任務在主執行緒最早可得的空閒時間執行,意思就是不用再等多少秒了,只要主執行緒執行棧內的同步任務全部執行完成,棧為空就馬上執行。

舉例說明:

console.log('先執行這裡');
setTimeout(() => {
console.log('執行啦1')
},10);
setTimeout(() => {
console.log('執行啦2')
},0);

console.log('最後執行這裡');
複製程式碼


16、非同步(重點 面試還是愛問!)


Promise:非同步

下面是一個Promise物件的簡單例子。

function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}

timeout(100).then((value) => {
console.log(value); //'done'
});
複製程式碼

上面程式碼中,timeout方法返回一個Promise例項,表示一段時間以後才會發生的結果。過了指定的時間(ms引數)以後,Promise例項的狀態變為resolve,就會觸發then方法繫結的回撥函式。


Promise 例項

const wait = function(val) {
    // 定義一個 promise 物件
    const promise = new Promise((resolve, reject) => {
            // 將之前的非同步操作,包括到這個 new Promise 函式之內
            const task = function() {
                if (val) {
                    console.log('執行完成');
                    resolve(true)
                } else { console.log('執行失敗');
                    reject(false) }
                // callback 中去執行 resolve 或者 reject
            }
            setTimeout(task, 2000)
        })
        // 返回 promise 物件
    return promise
}

const w = wait (1);
var a = 0;
w.then((val) => {
    console.log('ok 1' + val);
    a = 231;
    console.log(a);
    return w;
}, (val) => {
    console.log('err 1' + val)
}).then((val) => {
    console.log('ok 2' + val);
    var b = a + 2;
    console.log(b)
}, (val) => {
    console.log('err 2' + val)
})
複製程式碼



async :非同步

依次讀取兩個檔案:

var asyncReadFile = async function () {
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};//注意 封裝的readFile ()方法要返回promise物件

async function getStockPriceByName(name) {
var symbol = await getStockSymbol(name);
var stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result) {
console.log(result);
});
複製程式碼

使用注意點:

第一點,前面已經說過,await命令後面的Promise物件,執行結果可能是rejected,所以最好把await命令放在try...catch程式碼塊中。

async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}

// 另一種寫法 (推薦)
async function myFunction() {
await somethingThatReturnsAPromise().catch(function
(err) {
console.log(err);
});}
複製程式碼



因為以下所有的程式碼都會用到Promise,因此乾脆在所有介紹之前,先封裝一個Promise,封裝一次,為下面多次應用。

const fs = require('fs')
const path = require('path')  // 後面獲取檔案路徑時候會用到
const readFilePromise = function (fileName) {
    return new Promise((resolve, reject) => {
        fs.readFile(fileName, (err, data) => {
            if (err) {
                reject(err)  // 注意,這裡執行 reject 是傳遞了引數,後面會有地方接收到這個引數
            } else {
                resolve(data.toString())  // 注意,這裡執行 resolve 時傳遞了引數,後面會有地方接收到這個引數
            }
        })
    })
}
複製程式碼



那麼前面步驟return的值會被當做引數傳遞給後面步驟的函式,如下面程式碼中的a就接收到了return JSON.parse(data).a的值

const fullFileName = path.resolve(__dirname, '../data/data2.json')
const result = readFilePromise(fullFileName)
result.then(data => {
    // 第一步操作
    console.log(data)
    return JSON.parse(data).a  // 這裡將 a 屬性的值 return
}).then(a => {
    // 第二步操作
    console.log(a)  // 這裡可以獲取上一步 return 過來的值

})
複製程式碼

我們知道then會接收兩個引數(函式),第一個引數會在執行resolve之後觸發(還能傳遞引數),第二個引數會在執行reject之後觸發(其實也可以傳遞引數,和resolve傳遞引數一樣),但是上面的例子中,我們沒有用到then的第二個引數。這是為何呢 ———— 因為不建議這麼用。

對於Promise中的異常處理,我們建議用catch方法,而不是then的第二個引數。請看下面的程式碼,以及註釋。

const fullFileName = path.resolve(__dirname, '../data/data2.json')
const result = readFilePromise(fullFileName)
result.then(data => {
    console.log(data)
    return JSON.parse(data).a;
}).then(a => {
    console.log(a)
}).catch(err => {
    console.log(err.stack)  // 這裡的 catch 就能捕獲 readFilePromise 中觸發的 reject ,而且能接收 reject 傳遞的引數
})
複製程式碼



讀取兩個檔案data1.json和data2.json,現在我需要一起讀取這兩個檔案,等待它們全部都被讀取完,再做下一步的操作。此時需要用到Promise.all。(以前記得做個列印報表的功能。當時報表資料差不多幾萬條。所以想到了分頁列印。比如分5頁,每頁200條。那麼列印的時候肯定要按順序列印。即1-200,201-300以此列印。那麼就寫了個方法,迴圈遍歷整個資料,每200條放到一個方法中,結果把5個方法再push到一個陣列中。記得當時用的是$q 裡面的all()方法來執行的。)

// Promise.all 接收一個包含多個 promise 物件的陣列

Promise.all([result1, result2]).then(datas => {
    // 接收到的 datas 是一個陣列,result1, result2 依次執行,返回包含了多個 promise的內容
    console.log(datas[0])
    console.log(datas[1])
})

  var funcA = function(){
                console.log("funcA");
                return "hello,funA";
            }
            var funcB = function(){
                console.log("funcB");
                return "hello,funB";
            }
          var a = [funcA ,funcB];
          Promise.all([funcA , funcB]).then(datas => {   //  Promise.all(a).then()
    // 接收到的 datas 是一個陣列,依次包含了多個 promise 返回的內容
    console.log(datas[0])
    console.log(datas[1])
})
複製程式碼

讀取兩個檔案data1.json和data2.json,現在我需要一起讀取這兩個檔案,但是隻要有一個已經讀取了,就可以進行下一步的操作。此時需要用到Promise.race

// Promise.race 接收一個包含多個 promise 物件的陣列
Promise.race([result1, result2]).then(data => {
// data 即最先執行完成的 promise 的返回值
console.log(data)
})
複製程式碼


栗子:

var uploadQuestion = function(questions) {
var promises = [];
angular.forEach(questions, function(question) {
var promise = $http({ //
url: 'upload/question',
method: 'POST',
data: question
});
promises.push(promise);

});
return $q.all(promises);
}
複製程式碼



$q:非同步


var iWantResolve = true;

function es6promise() {
    return $q(function(resolve, reject) {
        $timeout(function() {
            if (iWantResolve) {
                resolve("es6promise resolved");
            } else {
                reject("es6promise reject");
            }
        }, 1000)
    })
}

es6promise()
    .then(function(data) {
        console.log(data);
    })
    .catch(function(err) {
        console.log(err);
    });

// if(iWantResolve == true) output: es6promise resolved
// if(iWantResolve = false) output: es6promise reject
複製程式碼




function commonJsPromise() {
    var deferred = $q.defer();
    $timeout(function() { //可以沒有
        deferred.notify("commonJS notify");
        if (iWantResolve) {
            deferred.resolve("commonJS resolved");
        } else {
            deferred.reject("commonJS reject");
        }

    }, 500);
    return deferred.promise;
}

commonJsPromise()
    .then(function /** success callback**/ (data) {
        console.log(data);

    }, function /** error callback **/ (err) {
        console.log(err);
    }, function /** progress callback **/ (update) {
        console.log(update);
    });

// if(iWantResolve == true) output: commonJS notify commonJS resolved
// if(iWantResolve = false) output: commonJS notify commonJS reject
複製程式碼



$q.all([es6promise(), commonJsPromise()])
    .then(function (dataArr) {
        console.log("$q.all: ", dataArr);
    }, function (err) {
        console.log("$q.all: ", err)
    }, function /** unnecessary **/ (update) {
        console.log("$q.all", update);
    });
// if(iWantResolve == true) output: $q.all:  ["es6promise resolved", "commonJS resolved"]
// if(iWantResolve = false) output: $q.all:  es6promise reject
複製程式碼



大家都是到 jquery v1.5 之後$.ajax()返回的是一個deferred物件,而這個deferred物件和我們現在正在學習的Promise物件已經很接近了,但是還不一樣。那麼 ———— deferred物件能否轉換成 ES6 的Promise物件來使用??

答案是能!需要使用Promise.resolve來實現這一功能,請看以下程式碼:

// 在瀏覽器環境下執行,而非node 環境

const jsPromise =
Promise.resolve($.ajax('/whatever.json'))
jsPromise.then(data => {
// ...
})//ajax 非同步執行方法
複製程式碼

其實,在我們的日常開發中,這種將thenable轉換為Promise的需求並不多。真正需要的是,將一些非同步操作函式(如fs.readFile)轉換為Promise(就像文章一開始readFilePromise做的那樣)而且then必須返回一個promise,同一個 promise 的then可以呼叫多次(鏈式)” ——— 這兩句話說明了一個意思 ——— then肯定要再返回一個promise,要不然then後面怎麼能再鏈式的跟一個then呢?


const w9991 = wait(1)
w9991.then((val) => {
console.log('ok 1'+val);// ok 1true ok 2true //2不是undefined
return w9991;
}).then((val) => {
console.log('ok 2'+val); 
})
複製程式碼


eg:

function readFilePromise() {
    return new Promise((resolve, reject) => {
        resolve(setTimeout(() => {
            console.log(1111);
        }));
    })
};

const readFileAsync = async function() {
        const f1 = await readFilePromise();
        const f2 = await readFilePromise();
        return 'done' // 先忽略,後面會講到
    }
    // 執行
const result = readFileAsync() //1111 1111
result.then(data => {
    console.log(data) // done
})
複製程式碼


使用async-await的不同和好處

第一,await後面不能再跟thunk函式,而必須跟一個Promise物件(因此,Promise才是非同步的終極解決方案和未來)。跟其他型別的資料也OK,但是會直接同步執行,而不是非同步。

第二,執行const result = readFileAsync()返回的是個Promise物件,而且上面程式碼中的return 'done'會直接被下面的then函式接收到

result.then(data => {
console.log(data) // done
})
複製程式碼

第三,從程式碼的易讀性來將,async-await更加易讀簡介,也更加符合程式碼的語意。而且還不用引用第三方庫,也無需學習Generator那一堆東西,使用成本非常低。


總結:

非同步操作 : promise \generator yield \async-await\$q


17、正則

這個我只會簡單的就不在此賣弄了。。。呵呵噠


18、window 物件(html5 api)

下期見。。。

19、模組化封裝方法(exports、 module exports 、export default)

下期見。。。


總結: 文章穿插有點大 .都是筆者隨心寫的。第一次寫,如有錯誤請指正。請多多關照!!呵呵噠.

備註:文章有些是以前的學習筆記,直接拷貝上來的。侵刪!


相關文章