時間匆匆, 轉眼畢業已經快兩年了。 最為一個前端小白的我, 現在怎麼說也是灰色的了吧? 前段時間一直有個想法: 想把自己學到的前端知識點整理下,感覺這樣有助於自己基礎的鞏固。 所以才有個今天這個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又是什麼玩意呢?代表當前作用域本身。在全域性作用域下
在函式塊級作用域下
這樣應該明白了吧
有一天看到了一個這種寫法
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 方法
主要幾點就行了:
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)
下期見。。。
總結: 文章穿插有點大 .都是筆者隨心寫的。第一次寫,如有錯誤請指正。請多多關照!!呵呵噠.
備註:文章有些是以前的學習筆記,直接拷貝上來的。侵刪!