1. 作用域變量
作用域就是變量的作用范圍奸汇。也就是你聲明一個(gè)變量以后,這個(gè)變量可以在什么場(chǎng)合下使用往声。以前的JavaScript只有全局作用域擂找,和函數(shù)作用域。
1.1 var的問(wèn)題
1.var 沒(méi)有塊級(jí)作用域浩销,定義后在當(dāng)前包中都都可以訪問(wèn)贯涎,如果變量名重復(fù),就會(huì)覆蓋前面定義的變量慢洋,并且很可以被他人修改塘雳。
if(true){
var a = "a"; //期望a是某一個(gè)值
}
console.log(a);
<br />2.var 在for循環(huán)標(biāo)記變量共享,一般在循環(huán)中會(huì)使用的i會(huì)被共享普筹,其本質(zhì)也是由于沒(méi)有塊級(jí)作用域造成的<br />
for (var i = 0; i < 3; i++) {
setTimeout(function () {
alert(i);
}, 0);
}
// 結(jié)果就是 彈窗三次 3
for ( i = 0; i < 3; i++) {
setTimeout(function () {
alert(i);
}, 0);
}
// 結(jié)果就是 彈窗三次 0-2
1.2 塊級(jí)作用域
在用var定義變量的時(shí)候败明,變量通過(guò)閉包進(jìn)行隔離的,現(xiàn)在用了let斑芜,不僅僅可以通過(guò)閉包隔離肩刃,還可以增加了一些塊級(jí)作用域隔離。塊級(jí)作用用一組大括號(hào)定義一個(gè)快,使用let定義的變量字啊大括號(hào)的外面是訪問(wèn)不到的盈包。
1.2.1 實(shí)現(xiàn)會(huì)計(jì)作用域
if(ture){
let name = 'wjh'
}
consloe.log('name'); // ReferenceError: name is not defined
1.2.2 不會(huì)污染全局對(duì)象
if(ture){
let name = 'wjh'
}
console.log(window.name); // undefined
1.2.3 for 循環(huán)中也可以使用i
// 嵌套循環(huán)不會(huì)相互影響
for (let i = 0; i < 3; i++) {
console.log("out", i);
for (let i = 0; i < 2; i++) {
console.log("in", i);
}
}
// 結(jié)果 out 0 in 0 in 1 out 1 in 0 in 1 out 2 in 0 in 1
1.2.4 重復(fù)定義會(huì)報(bào)錯(cuò)
if(ture){
let a = 1;
let a = 2; //Identifier 'a'
}
1.2.5 不存在變量的預(yù)解釋
for(let i = 0;i<2;i++){
console.log('inner',i);
let i =100;
}
// 結(jié)果 i is not defined
1.2.6 閉包的新寫法
;(function(){
})();
現(xiàn)在
{
}
2. 常量
使用 const 我們可以聲明一個(gè)常量沸呐,一旦聲明之后,就不可以更改呢燥。
2.1 常量不能重新賦值
const MY_NAME = 'wjw';
MY_NAME = 'wjw2';//Assignment to constant variable
2.2 變量可以改變
注意const限制的是不能給變量重新賦值崭添,而變量的值本身是可以改變的,下面的操作是可以的
const names = ['wjw1'];
names.push('wjw2');
console.log(names);
2.3 不同的塊級(jí)作用域可以多次定義
const A = "0";
{
const A = "A";
console.log(A)
}
{
const A = "B";
console.log(A)
}
console.log(A)
// 結(jié)果 A B 0
3. 解構(gòu)
3.1 解構(gòu)數(shù)組
解構(gòu)意思就是分解一個(gè)東西的結(jié)構(gòu),可以用一種類似數(shù)組的方式定義N個(gè)變量,可以將一個(gè)數(shù)組中的值按照規(guī)則賦值過(guò)去叛氨。<br />
var [name,age]=['wjh',8];
console.log(name,age);
3.2 嵌套賦值
let [x,[y],z]=[1,[2.1]];
console.log(x,y,z);
let [x,[y,z]] = [1,[2.1,2.2]];
console.log(x,y,z);
let [json,arr,num] = [{name:'wjw'},[1,2],3];
console.log(json,arr,num);
// 1 2.1 undefined 1 2.1 2.2 { name: 'wjw' } [ 1, 2 ] 3
3.3 省略賦值
let [,,x]=[1,2,3]
console.log(x);
3.4 解構(gòu)對(duì)象
對(duì)象也可以被解構(gòu)
var obj = {name:'wjw',age:8};
//對(duì)象里的name屬性的值會(huì)交給name這個(gè)變量呼渣,age的值會(huì)交給age這個(gè)變量
var {name,age} = obj
//對(duì)象里的name屬性的值會(huì)交給myname這個(gè)變量,age的值會(huì)交給myage這個(gè)變量
let {name: myname, age: myage} = obj;
console.log(name,age,myname,myage);
3.5 默認(rèn)值
在賦值和傳參的時(shí)候可以使用默認(rèn)值
let [a='a',b='b',c=new Error('C必須指定')] = [1, , 3];
console.log(a,b,c);
function ajax(options){
var method = options.method || "get";
var data = options.data || {};
}
function ajax(method='get',data){
console.log(arguments);
}
ajax({
method:'post',
data:{'name':'wjh'}
})
4. 字符串
4.1 模板字符串
模板字符串用反應(yīng)號(hào)(數(shù)字1左邊的那個(gè)建)包含寞埠,用${}
括起來(lái)<br /><br />
var name = 'wjw',age = 8;
let desc = `${name} is ${age} old!`;
console.log(desc);
//所有模板字符串的空格和換行屁置,都是被保留的
var str = `<ul>
<li>a</li>
<li>b</li>
</ul>`;
console.log(str);
其中的變量會(huì)用變量的值替換掉
function replace(desc){
return desc.replace(/\$\{([^}]+)\}/g,function(matched,key){
return eval(key);
});
}
4.2 帶標(biāo)簽的模板字符串
可以在模板字符串的前面添加一個(gè)標(biāo)簽,這個(gè)標(biāo)簽可以去處理模板字符串 標(biāo)簽其實(shí)就是一個(gè)函數(shù),函數(shù)可以接收兩個(gè)參數(shù),一個(gè)是 strings 就是模板字符串里的每個(gè)部分的字符 還有一個(gè)參數(shù)可以使用rest的形式values,這個(gè)參數(shù)里面是模板字符串里的值仁连。
var name = 'wjh',age = 8;
function desc(strings,...values){
console.log(strings,values);
}
desc`${name} is ${age} old!`;
字符串新方法
- includes():返回布爾值蓝角,表示是否找到了參數(shù)字符串。
- startsWith():返回布爾值饭冬,表示參數(shù)字符串是否在源字符串的頭部使鹅。
- endsWith():返回布爾值,表示參數(shù)字符串是否在源字符串的尾部昌抠。
var s = 'wjh';
s.startsWith('w') // true
s.endsWith('h') // true
s.includes('j') // true
第二個(gè)參數(shù)患朱,表示開(kāi)始搜索的位置
var s = 'wjh';
console.log(s.startsWith('j',2)); // true
console.log(s.endsWith('j',2)); // true
console.log(s.includes('j',2)); // false
endsWith的行為與其他其他方法有所不同。它針對(duì)前n個(gè)字符炊苫,而其他方法是從第幾位開(kāi)始到字符串結(jié)束
4.4 repeat
repeat 方法返回一個(gè)新字符串裁厅,表示將原字符串重復(fù)n次。<br /><br />
'x'.repeat(3);
'x'.repeat(0);
5. 函數(shù)
5.1 默認(rèn)參數(shù)
可以給定義的函數(shù)接收的參數(shù)設(shè)置默認(rèn)的值 在執(zhí)行這個(gè)函數(shù)的時(shí)候劝评,如果不指定函數(shù)的參數(shù)的值姐直,就會(huì)使用參數(shù)的這些默認(rèn)的值。
function ajax(url,method='GET',dataType="json"){
console.log(url);
console.log(method);
console.log(dataType);
}
5.2 展開(kāi)操作符
把...放在數(shù)組前面可以把一個(gè)數(shù)組進(jìn)行展開(kāi)蒋畜,可以把一個(gè)函數(shù)而不需要使用apply
<br /><br />
//傳入?yún)?shù)
let print = function(a,b,c){
console.log(a,b,c);
}
print([1,2,3]);
print(...[1,2,3]);
// 可以替代apply
var m1 = Math.max.apply(null, [8, 9, 4, 1]);
var m2 = Math.max(...[8, 9, 4, 1]);
// 可以替代concat
var arr1 = [1, 3];
var arr2 = [3, 5];
var arr3 = arr1.concat(arr2);
var arr4 = [...arr1, ...arr2];
console.log(arr3,arr4);
//類數(shù)組的轉(zhuǎn)數(shù)組
function max(a,b,c) {
console.log(Math.max(...arguments));
}
max(1, 3, 4);
5.3 剩余操作符
剩余操作符可以把其余參數(shù)的值放在一個(gè)叫做b的數(shù)組里
let rest = function(a,...rest){
console.log(a,rest);
}
rest(1,2,3);
5.4 解構(gòu)參數(shù)
let destruct = function({name,age}){
console.log(name,age);
}
destruct({name:'wjh',age:10})
5.6 箭頭函數(shù)
箭頭函數(shù)簡(jiǎn)化了函數(shù)的定義方式
[1,2,3].forEach(val=>console.log(val));
輸入?yún)?shù)如果多于一個(gè)要用()包含声畏,函數(shù)體如果有多條語(yǔ)句需要用{}包起來(lái)
箭頭函數(shù)根本沒(méi)有自己的this,導(dǎo)致內(nèi)部的this就是外層代碼塊的this姻成。 正是因?yàn)樗鼪](méi)有this插龄,從而避免了this指向的問(wèn)題。
var person = {
name:'wjh',
getName:function(){
- setTimeout(function(){console.log(this);},1000); //在瀏覽器執(zhí)行的話this指向window
+ setTimeout(() => console.log(this),1000);//在瀏覽器執(zhí)行的話this指向person
}
}
person.getName();
6. 數(shù)組的新方法
// 相同的陣列
var people = [
{
name : 'Casper' ,
like : '鍋燒意面' ,
age : 18
},
{
name : 'Wang' ,
like : '炒面' ,
age : 24
},
{
name : 'Bobo' ,
like : '蘿卜泥' ,
age : 1
},
{
name : '鹵蛋' ,
like : '蘿卜泥' ,
age : 3
}
];
6.1 Array.prototype.filter()
filter() 會(huì)回傳一個(gè)陣列科展,其條件是return 后方為true 的物件均牢,很適合用在搜尋符合條件的資料。
var filterEmpty = people.filter( function ( item, index, array ) {
});
console.log(filterEmpty); //沒(méi)有條件才睹,會(huì)是一個(gè)空陣列
var filterAgeThan5 = people.filter( function ( item, index, array ) {
return item.age > 5 ; //取得大于五歲的 如果這邊符合條件 只要為ture即可
});
console .log(filterAgeThan5); // Casper, Wang這兩個(gè)物件
6.2 Array.prototype.find()
find()與filter()很像徘跪,但find() 只會(huì)回傳一次值甘邀,且是第一次為true的值。
var findEmpty = people.find( function ( item, index, array ) {
});
console .log(findEmpty); //沒(méi)有條件垮庐,會(huì)是undefined
var findAgeThan5 = people.find( function ( item, index, array ) {
return item.age > 5 ; //取得大于五歲的
});
console .log(findAgeThan5); //雖然答案有兩個(gè)松邪,但只會(huì)回傳Casper這一個(gè)物件
var findLike = people.find( function ( item, index, array ) {
return item.like === '蘿卜泥' ; //取得陣列l(wèi)ike === '蘿卜泥'
});
console .log(findLike); //雖然答案有兩個(gè),但只會(huì)回傳第一個(gè)Bobo物件
6.3 Array.prototype.forEach()
forEach 是這幾個(gè)陣列函式最單純的一個(gè)哨查,不會(huì)額外回傳值逗抑,只單純執(zhí)行每個(gè)陣列內(nèi)的物件或值。
var forEachIt = people.forEach( function ( item, index, array ) {
console .log(item, index, array); //物件,索引,全部陣列
return item; // forEach沒(méi)在return的寒亥,所以這邊寫了也沒(méi)用
});
console .log(forEachIt); // undefined
people.forEach( function ( item, index, array ) {
item.age = item.age + 1 ; // forEach就如同for邮府,不過(guò)寫法更容易
});
console .log(people); //全部age + 1
6.4 Array.prototype.map()
使用map() 時(shí)他需要回傳一個(gè)值,他會(huì)透過(guò)函式內(nèi)所回傳的值組合成一個(gè)陣列溉奕。
如果不回傳則是 undefined
回傳數(shù)量等于原始陣列的長(zhǎng)度
這很適合將原始的變數(shù)運(yùn)算后重新組合一個(gè)新的陣列褂傀。
var mapEmpty = people.map( function ( item, index, array ) {
});
console .log(mapEmpty); // [undefined, undefined, undefined, undefined]
var mapAgeThan5 = people.map( function ( item, index, array ) {
return item.age > 5 ; //比較大于五歲的
});
console .log(mapAgeThan5); // [true, true, false, false]
var mapAgeThan5_2 = people.map( function ( item, index, array ) {
// 錯(cuò)誤示范
if (item.age > 5 ) {
return item; //回傳大于五歲的
}
return false ; //別以為空的或是false就不會(huì)回傳
});
console .log(mapAgeThan5_2); // [{name: 'Casper'...}, {name: 'Wang'...}, false, false]
var mapEat = people.map( function ( item, index, array ) {
if (item.like !== '蘿卜泥' ) {
return ` ${item.like}好吃` ;
} else {
return ` ${item.like}不好吃` ;
}
});
console .log(mapEat); // ["鍋燒意面好吃", "炒面好吃", "蘿卜泥不好吃", "蘿卜泥不好吃"]
6.5 Array.prototype.every()
every()可以檢查所有的陣列是否符合條件,這僅會(huì)回傳一個(gè)值trueor false腐宋,可以用來(lái)檢查陣列中的內(nèi)容是否符合特定條件紊服。
var ans = array.every( function ( item, index, array ) {
console .log(item, index, array); //物件,索引,全部陣列
return item.age > 10 //當(dāng)全部age大于10才能回傳true
});
console .log(ans); // false:只要有部分不符合檀轨,則為false
var ans2 = array.every( function ( item, index, array ) {
return item.age < 25
});
console .log(ans2); // true:全部age都小于25
6.6 Array.prototype.some()
some() 與every() 非常接近胸竞,都是回傳true or false,差異僅在every() 需完全符合参萄,some() 僅需要部分符合卫枝。
var ans = people.some( function ( item, index, array ) {
return item.age > 10 //當(dāng)全部age大于10才能回傳true
});
console .log(ans); // true:只要有部分符合,則為true
var ans2 = people.some( function ( item, index, array ) {
return item.age < 25
});
console .log(ans2); // true:只要有部分符合讹挎,則為true
var ans2 = people.some( function ( item, index, array ) {
return item.age > 25
});
console .log(ans2); // false:全部都不符合則為false
6.7 Array.prototype.reduce()
reduce() 和其他幾個(gè)差異就很大了校赤,他可以與前一個(gè)回傳的值再次作運(yùn)算,參數(shù)包含以下:
accumulator: 前一個(gè)參數(shù)筒溃,如果是第一個(gè)陣列的話马篮,值是以另外傳入或初始化的值
currentValue: 當(dāng)前變數(shù)
currentIndex: 當(dāng)前索引
array: 全部陣列
var reduceEmpty = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
});
console .log(reduceEmpty); //沒(méi)有條件,會(huì)是undefined
var reducePlus = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
// 分別為前一個(gè)回傳值, 目前值, 當(dāng)前索引值
console .log(accumulator, currentValue, currentIndex);
return accumulator + currentValue.age; //與前一個(gè)值相加
}, 0 ); //傳入初始化值為0
console .log(reducePlus); //總和為46
var reducePlus = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
console .log( 'reduce' , accumulator, currentValue, currentIndex)
return Math .max( accumulator, currentValue.age ); //與前一個(gè)值比較哪個(gè)大
}, 0 );
console .log(reducePlus); //最大值為24
7. 對(duì)象
7.1 對(duì)象字面量
如果你想在對(duì)象里添加跟變量名一樣的屬性怜奖,并且屬性的值就是變量表示的值就可以直接在對(duì)象里加上這些屬性
let name = 'wjh';
let age = 8;
let getName = function(){
console.log(this.name)
}
let person ={
name,
age,
getName
}
person.getName();
7.2 Object.is
對(duì)比兩個(gè)值是否相等
console.log(Object.is(NaN,NaN));
7.3 Object.assign
把多個(gè)對(duì)象的屬性復(fù)制到一個(gè)對(duì)象中浑测,第一個(gè)參數(shù)是復(fù)制的對(duì)象,從第二個(gè)參數(shù)開(kāi)始往后歪玲,都是復(fù)制的源對(duì)象
var nameObj = {name:'wjh'}
var ageObj = {age:8};
var obj = {};
Object.assign(obj,nameObj,ageObj);
console.log(obj);
//克隆對(duì)象
function clone(obj){
return Object.assgin({},obj);
}
7.4 Object.setPrototypeOf
將一個(gè)指定的對(duì)象原型設(shè)置為另一個(gè)對(duì)象或者null
var obj1 = {name:'wjh1'};
var obj2 = {name:'wjh2'};
var obj = {};
Object.setPrototypeOf(obj,obj1);
console.log(obj.name);
console.log(Object.getPrototypeOf(obj));
Object.setProtoypeOF(obj,obj2);
console.log(obj.name);
console.log(Object.getPrototypeOf(obj));
7.5 proto
直接對(duì)象表達(dá)式中設(shè)置prototype
var obj1 = {name:'wjh'};
var obj3 = {
_proto_:obj1
}
console.log(obj3.name);
console.log(Object.getPrototypeOf(obj3));
7.6 super
通過(guò)super可以調(diào)用protype上的屬性或方法
let person ={
eat(){
return 'milk';
}
}
let student = {
_proto_:person,
eat(){
return super.eat()+'bead'
}
}
console.log(student.eat());
8. 類
8.1 class
使用 class 這個(gè)關(guān)鍵詞定義一個(gè)類迁央,基于這個(gè)創(chuàng)建實(shí)例以后就會(huì)創(chuàng)建 constructor 方法,此方法可以用來(lái)初始化
class Person{
constructor(name){
this.name = name;
}
getName(){
console.log(this.name)
}
}
let person = new Person('wjh');
person.getName();
8.2 get與set
getter 可以用來(lái)獲取屬性,setter 可以去設(shè)置屬性
class Person {
constructor(){
this.hobbies = [];
}
set hobby(hobby){
this.hobbies.push(hobby);
}
get hobby(){
return this.hobbies;
}
}
let person = new Person();
person.hobby = 'aa';
person.hobby = 'bb';
console.log(person.hobby)
8.3 靜態(tài)方法-static
在類里面添加靜態(tài)的方法可以使用static 這個(gè)關(guān)鍵詞滥崩,靜態(tài)方法就是不需要實(shí)例化類就能使用的方法
class Person{
static add(a,b){
return a+b;
}
}
console.log(Person.add(1,x));
8.4 繼承extends
一個(gè)類可以繼承其他的類里的東西
class Person{
constructor(name){
this.name = name;
}
}
class Teacher extends Person{
constructor(name,age){
super(name);
this.age = age;
}
}
var teacher = Teacher('wjh',8);
console.log(teacher.name,teacher.age)
9. 生成器(Generator)與迭代器(Iterator)
Generator 是一個(gè)特殊的函數(shù)岖圈,執(zhí)行它會(huì)返回一個(gè)Iterator對(duì)象。通過(guò)遍歷迭代器钙皮,Generator函數(shù)運(yùn)行后悔返回遍歷器對(duì)象蜂科,而不是函數(shù)的返回值顽决。
9.1 Iterators模擬
迭代器有一個(gè)next方法,每次執(zhí)行的時(shí)候會(huì)返回一個(gè)對(duì)象 對(duì)象里面有兩個(gè)函數(shù)导匣,一個(gè)是value表示返回的值擎值,還有就是布爾值done,表示是迭代完成
function buy(books){
let i = 0;
return{
next(){
let done = i ===books.length;
let value = !done ? books[i++]:undefined;
return {
value:value,
done:done
}
}
}
}
let iterators = buy(['js','html']);
var curr;
do{
curr = iterators.next();
console.log(curr);
}while(!curr.done);
9.3 Generators
生成器用于創(chuàng)建迭代器
function* buy(boos){
for(var i=0;i<boos.length;i++){
yield books[i];
}
}
let buying = buy(['js','html]);
var curr;
do {
curr = buying.next();
console.log(curr);
}while(!curr.done);
10. 集合
10.1 Set
一個(gè)Set是一堆東西的集合逐抑,Set 有點(diǎn)像數(shù)組鸠儿,不過(guò)跟數(shù)組不一樣的是,Set里面不能有重復(fù)的內(nèi)容
var books = new Set();
books.add('js');
books.add('js');//添加重復(fù)元素的集合元素個(gè)數(shù)不會(huì)變化
books.add('html');
books.forEach(function(book){ // 循環(huán)集合
console.log(book);
})
console.log(book.size);//集合中元數(shù)的個(gè)數(shù)
console.log(books.has('js'));//判斷集合是否有此元素
books.delete('js');
console.log(books.size);
console.log(books.has('js'));
books.clear();//清空set
console.log(books.size);
10.2 Map
可以使用Map來(lái)組織這個(gè)名值對(duì)的數(shù)據(jù)
var books = new Map();
books.set('js',{name:'js'});//向map中添加元素
books.set('html',{name:'html'});
console.log(books.size);//查看集合中的元素
console.log(books.get('js'));//通過(guò)key獲取值
books.delete('js');//執(zhí)行key刪除元素
console.log(books.has('js'));//判斷map中有沒(méi)有key
book.forEach((value,key)=>{
console.log(key+'='+value);
})
books.clear();//清空map
11. 模塊
可以根據(jù)應(yīng)用的需求吧代碼分成不同的模塊厕氨,每個(gè)模塊里可以導(dǎo)出它需要讓其他模塊使用的東西进每,在其他模塊里面可以導(dǎo)入這些模塊,導(dǎo)出的東西命斧。
11.1 模塊
在瀏覽器中使用模塊需要借助 導(dǎo)出
export var name = 'wjh';
export var age = 8;
導(dǎo)入
//import {name,age} from './school.js';
import * as school from './school.js';
console.log(school.name,school.age);
在頁(yè)面中引用
<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/bin/BrowserSystem.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
<script type="module" src="index.js"></script>
11.2 重命名
導(dǎo)出時(shí)重命名
function say(){
console.log('say');
}
export {say as say2};
導(dǎo)入時(shí)重命名
import {say2 as say3} from './school.js'
11.3 默認(rèn)導(dǎo)出
每個(gè)模塊都可以有一個(gè)默認(rèn)要導(dǎo)出的東西
export default function say(){
console.log('say')
}
導(dǎo)入
import say from './school.js'
11.4 深度克隆
var parent = {
age: 5,
hobby: [1, 2, 3],
home: {city: '北京'},
};
var child = extendDeep(parent);
child.age = 6;
child.hobby.push('4');
child.home.city = '廣東';
console.log('child ', child); //[1, 2, 3, 4]
console.log('parent ', parent);
function extend(parent) {
let child;
if (Object.prototype.toString.call(parent) == '[object Object]') {
child = {};
for (let key in parent) {
child[key] = extend(parent[key])
}
} else if (Object.prototype.toString.call(parent) == '[object Array]') {
child = parent.map(item => extend(item));
} else {
return parent;
}
return child;
}
function extendDeep(parent, child) {
child = child || {};
for (var key in parent) {
if (typeof parent[key] === "object") {
child[key] = (Object.prototype.toString.call(parent[key]) === "[object Array]") ? [] : {};
extendDeep(parent[key], child[key]);
} else {
child[key] = parent[key];
}
}
return child;
}
12.JavaScript(ES6) 中條件語(yǔ)句
12.1 使用 Array.includes 來(lái)處理多個(gè)條件
function test(fruit) {
if (fruit == 'apple' || fruit == 'strawberry') {
console.log('red');
}
}
優(yōu)化變成 ->>
function test(fruit) {
// 條件提取到數(shù)組中
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
if (redFruits.includes(fruit)) {
console.log('red');
}
}
12.2 減少嵌套田晚,提前使用 return 語(yǔ)句
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// 條件 1:fruit 必須有值
if (fruit) {
// 條件 2:必須為紅色
if (redFruits.includes(fruit)) {
console.log('red');
// 條件 3:數(shù)量必須大于 10
if (quantity > 10) {
console.log('big quantity');
}
}
} else {
throw new Error('No fruit!');
}
}
// 測(cè)試結(jié)果
test(null); // 拋出錯(cuò)誤:No fruits
test('apple'); // 打印:red
test('apple', 20); // 打庸帷:red贤徒,big quantity
優(yōu)化
/* 在發(fā)現(xiàn)無(wú)效條件時(shí)提前 return */
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// 條件 1:提前拋出錯(cuò)誤
if (!fruit) throw new Error('No fruit!');
// 條件2:必須為紅色
if (redFruits.includes(fruit)) {
console.log('red');
// 條件 3:數(shù)量必須大于 10
if (quantity > 10) {
console.log('big quantity');
}
}
}
為了減少一個(gè)嵌套層級(jí),優(yōu)化編碼風(fēng)格
/* 在發(fā)現(xiàn)無(wú)效條件時(shí)提前 return */
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
if (!fruit) throw new Error('No fruit!'); // 條件 1:提前拋出錯(cuò)誤
if (!redFruits.includes(fruit)) return; // 條件 2:當(dāng) fruit 不是紅色的時(shí)候汇四,提前 return
console.log('red');
// 條件 3:必須是大量存在
if (quantity > 10) {
console.log('big quantity');
}
}
12.3.使用函數(shù)的默認(rèn)參數(shù) 和 解構(gòu)
function test(fruit, quantity) {
if (!fruit) return;
const q = quantity || 1; // 如果沒(méi)有提供 quantity 參數(shù)接奈,則默認(rèn)為 1
console.log(`We have ${q} ${fruit}!`);
}
// 測(cè)試結(jié)果
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
但是q在這邊不直觀所有優(yōu)化
function test(fruit, quantity = 1) { // i如果沒(méi)有提供 quantity 參數(shù),則默認(rèn)為 1
if (!fruit) return;
console.log(`We have ${quantity} ${fruit}!`);
}
// 測(cè)試結(jié)果
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
但是這邊 也可能是個(gè)對(duì)象
// 解構(gòu) —— 只獲得 name 屬性
// 參數(shù)默認(rèn)分配空對(duì)象 {}
function test({name} = {}) {
console.log (name || 'unknown');
}
//測(cè)試結(jié)果
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
12.4 選擇 Map / Object 字面量通孽,而不是Switch語(yǔ)句
function test(color) {
// 使用 switch case 語(yǔ)句序宦,根據(jù)顏色找出對(duì)應(yīng)的水果
switch (color) {
case 'red':
return ['apple', 'strawberry'];
case 'yellow':
return ['banana', 'pineapple'];
case 'purple':
return ['grape', 'plum'];
default:
return [];
}
}
//測(cè)試結(jié)果
test(null); // []
test('yellow'); // ['banana', 'pineapple']
這邊建議使用對(duì)象,更加清晰
// 使用對(duì)象字面量背苦,根據(jù)顏色找出對(duì)應(yīng)的水果
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple'],
purple: ['grape', 'plum']
};
function test(color) {
return fruitColor[color] || [];
}
但是這邊是很有可能為網(wǎng)絡(luò)數(shù)據(jù)互捌,無(wú)法判斷red這樣的變量,那么就用arry.filter 來(lái)過(guò)濾
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'strawberry', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'pineapple', color: 'yellow' },
{ name: 'grape', color: 'purple' },
{ name: 'plum', color: 'purple' }
];
function test(color) {
// 使用 Array filter 行剂,根據(jù)顏色找出對(duì)應(yīng)的水果
return fruits.filter(f => f.color == color);
}
12.5 使用 Array.every 和 Array.some 來(lái)處理全部/部分滿足條件
我們想檢查所有水果是否都是紅色的
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
let isAllRed = true;
// 條件:所有的水果都必須是紅色
for (let f of fruits) {
if (!isAllRed) break;
isAllRed = (f.color == 'red');
}
console.log(isAllRed); // false
}
使用 arry.every來(lái)過(guò)濾
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
// 條件:簡(jiǎn)短方式秕噪,所有的水果都必須是紅色
const isAllRed = fruits.every(f => f.color == 'red');
console.log(isAllRed); // false
}
如果我們想要檢查是否有至少一個(gè)水果是紅色的,我們可以使用 Array.some 僅用一行代碼就實(shí)現(xiàn)出來(lái)
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
// 條件:是否存在紅色的水果
const isAnyRed = fruits.some(f => f.color == 'red');
console.log(isAnyRed); // true
}
13 promise
13.1 異步回調(diào)
13.1.1 回調(diào)地獄
在需要多個(gè)操作的時(shí)間厚宰,會(huì)導(dǎo)致多個(gè)回調(diào)函數(shù)嵌套腌巾,導(dǎo)致代碼不夠直觀,就常說(shuō)的回調(diào)地獄
13.1.2 并行結(jié)果
如果幾個(gè)異步操作之間并沒(méi)有前后順序之分固阁,但需要等多個(gè)異步完成操作完成后才能執(zhí)行后續(xù)的任務(wù)壤躲,無(wú)法實(shí)現(xiàn)并行節(jié)約時(shí)間
13.2 Promise
promise本意是承諾,在程序中的意思就是承諾我過(guò)一段時(shí)間后會(huì)給你一個(gè)結(jié)果备燃。什么時(shí)間會(huì)用到過(guò)一段時(shí)間碉克?答案是異步操作,異步是指可能比較長(zhǎng)時(shí)間才有結(jié)果的才做并齐,例如網(wǎng)絡(luò)請(qǐng)求漏麦、讀取本地文件等
13.3 Promise的三種狀態(tài)
- Pending Promise對(duì)象勢(shì)力創(chuàng)建時(shí)候的初始化狀態(tài)
- Fulfilled 可以理解為成功的狀態(tài)
- Rejected 可以理解為失敗的狀態(tài)
then方法就是用來(lái)指定Promise 對(duì)象的狀態(tài)改變時(shí)確定執(zhí)行的操作客税,resolve時(shí)執(zhí)行第一個(gè)函數(shù)(onFulfilled),reject時(shí)執(zhí)行第二函數(shù)(onRejected)
13.4 構(gòu)造一個(gè)Promise
13.4.1 使用Promise
let promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
if(Math.random()>0.5)
resolve('This is resolve!')
else
reject('This is reject')
},1000);
});
promise.then(Fulfilled,Rejected)
- 構(gòu)造一個(gè)Promise實(shí)例需要給Promise構(gòu)造函數(shù)傳入一個(gè)函數(shù)
- 傳入的函數(shù)需要有兩個(gè)形參,兩個(gè)形參都是function類型的參數(shù)撕贞。
- 第一個(gè)形參運(yùn)行后會(huì)讓Promise實(shí)例處于resolve狀態(tài)更耻,所以我們一般給第一個(gè)形參命名為resolve,使 Promise對(duì)象的狀態(tài)改變成成功捏膨,同時(shí)傳遞一個(gè)參數(shù)用于后續(xù)成功后的操作
- 第一個(gè)形參運(yùn)行后悔讓Promise實(shí)例處于reject狀態(tài)秧均,所以我們一般給第一個(gè)形參命名為reject,將Promise對(duì)象的狀態(tài)改變?yōu)槭『叛模聦㈠e(cuò)誤的信息傳遞到后續(xù)錯(cuò)誤處理的操作
13.4.2 es5模擬Promise
function Promise(fn){
this.success(data);
},(error)=>{
this.error();
}
Promise.prtotype.resolve = function (data){
this.success(data);
}
Promise.prototype.then = function (success,error){
this.success = success;
this.error = error;
}
13.4.3 es5模擬Promise
class Promise{
constructor(fn){
fn((data)=>{
this.success(data);
},(error)=>{
this.error();
})
}
resolve(data){
this.success(data);
}
reject(error){
this.error(error);
}
then(success,error){
this.success = success;
this.error = error;
console.log(this);
}
}
13.5 promise 做為函數(shù)的返回值
function ajaxPromise(queryUrl){
return new Promise((resolve,reject)=>{
xhr.open('GET',queryUrl,ture);
xhr.send(null);
xhr.onreadystatechange = () =>{
if(xhr.readyState === 4 ){
if(xhr.status === 200){
resolve(xhr.responseText);
}else{
reject(xhr.responseText);
}
}
}
})
}
ajaxPromise('http://www.baidu.com')
.then((value)=>{
console.log(value);
})
.catch((err)=>{
console.error(err);
});
13.6 promise的鏈?zhǔn)秸{(diào)用
- 每次調(diào)用返回的都是一個(gè)新的Promise實(shí)例
- 鏈?zhǔn)秸{(diào)用的參數(shù)通過(guò)返回值傳遞
then 可以使用鏈?zhǔn)秸{(diào)用的寫法原因在于目胡,每一次執(zhí)行該方法時(shí)總是會(huì)返回一個(gè) Promise 對(duì)象
readFile('1.txt').then(function(data){
console.log(data);
}).then(function (data){
console.log(data);
return readFile(data);
}).then(function (data){
console.log(data);
}).catch(function (err){
console.log(err);
})
13.7 promise API
13.7.1 Promise.all
- 參數(shù):接受一個(gè)數(shù)組,數(shù)組內(nèi)都是Promise實(shí)例
- 返回值: 返回一個(gè) promise 實(shí)例链快,這個(gè)promise 實(shí)例的狀態(tài)轉(zhuǎn)移取決于參數(shù)的 promise實(shí)例的狀態(tài)變化誉己。當(dāng)參數(shù)處于resolve狀態(tài)時(shí),返回resolve狀態(tài)。如果參數(shù)中任意一個(gè)實(shí)例處于reject狀態(tài),返回的promise實(shí)例變?yōu)閞eject狀態(tài)浓领。
Promise.all([p1,p2]).then(function(result){
console.log(result); //[ '2.txt', '2' ]
})
不管兩個(gè)promise誰(shuí)先完成,Promise.all 方法會(huì)按照數(shù)組里面的順序?qū)⒔Y(jié)果返回
13.7.2 Promise.race
- 參數(shù):接受一個(gè)數(shù)組蚣旱,數(shù)組內(nèi)都是Promise實(shí)例
- 返回值: 返回一個(gè) promise 實(shí)例,這個(gè)promise 實(shí)例的狀態(tài)轉(zhuǎn)移取決于參數(shù)的 promise實(shí)例的狀態(tài)變化。當(dāng)參數(shù)處于resolve狀態(tài)時(shí),返回resolve狀態(tài)疼阔。如果參數(shù)中任意一個(gè)實(shí)例處于reject狀態(tài),返回的promise實(shí)例變?yōu)閞eject狀態(tài)半夷。
Promise.race([p1,p2]).then(function(result){
console.log(result); //[ '2.txt', '2' ]
})
13.7.3 Promise.resolve
返回一個(gè)Promise 實(shí)例,這個(gè)實(shí)例處于resolve狀態(tài)迅细。<br />根據(jù)傳入的參數(shù)不同有不同的功能:
- 值(對(duì)象巫橄、數(shù)組、字符串等):作為resolve傳遞出去的值
- Promise 實(shí)例 : 原封不動(dòng)返回
Promise.reject
返回一個(gè)Promise實(shí)例茵典,這個(gè)實(shí)例處于reject狀態(tài)
- 參數(shù)一般就是拋出的錯(cuò)誤信息湘换。
13.8 q
Q是一個(gè)在Javascrip中實(shí)現(xiàn)promise的模塊
13.8.1 q的基本用法
var Q = require('q');
var fs = require('fs');
function read(filename){
var deferred = Q.defer();
fs.readFile(filename,'utf8',function)(err,data){
if(err){
deferred.reject(err);
}else{
deferred.resolve(data);
}
});
}
read('1.txt1').then(function(data){
console.log(data);
},funtcion(error){
console.error(error);
})
13.8.2 q的簡(jiǎn)單實(shí)現(xiàn)
module.exports = {
defer(){
var _success,_error;
return {
resolve(data){
_success(data);
},
reject(err){
_error(err);
},
promise:{
then(success,error){
_success = success;
_error = error;
}
}
}
}
}
13.8.3 q的實(shí)現(xiàn)
var defer = function () {
var pending = [], value;
return {
resolve: function (_value) {
if (pending) {
value = _value;
for (var i = 0, ii = pending.length; i < ii; i++) {
var callback = pending[i];
callback(value);
}
pending = undefined;
}
},
promise: {
then: function (callback) {
if (pending) {
pending.push(callback);
} else {
callback(value);
}
}
}
};
};
13.9 bluebird
實(shí)現(xiàn) promise 標(biāo)準(zhǔn)的庫(kù)是功能最全,速度最快的一個(gè)庫(kù)
13.9.1 bluebird經(jīng)典使用
var Promise = require('./bluebird');
var readFile = Promise.promisify(require("fs").readFile);
readFile("1.txt", "utf8").then(function(contents) {
console.log(contents);
})
var fs = Promise.promisifyAll(require("fs"));
fs.readFileAsync("1.txt", "utf8").then(function (contents) {
console.log(contents);
})
13.9.2 bluebird簡(jiǎn)單實(shí)現(xiàn)
module.exports = {
promisify(fn){
return function () {
var args = Array.from(arguments);
return new Promise(function (resolve, reject) {
fn.apply(null, args.concat(function (err) {
if (err) {
reject(err);
} else {
resolve(arguments[1])
}
}));
})
}
},
promisifyAll(obj){
for(var attr in obj){
if(obj.hasOwnProperty(attr) && typeof obj[attr] =='function'){
obj[attr+'Async'] = this.promisify(obj[attr]);
}
}
return obj;
}
}
13.10 動(dòng)畫
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>move</title>
<style>
.square{
width:40px;
height:40px;
border-radius: 50%;
}
.square1{
background-color: red;
}
.square2{
background-color: yellow;
}
.square3{
background-color: blue;
}
</style>
</head>
<body>
<div class="square square1" style="margin-left: 0"></div>
<div class="square square2" style="margin-left: 0"></div>
<div class="square square3" style="margin-left: 0"></div>
</body>
<script>
var square1 = document.querySelector('.square1');
var square2 = document.querySelector('.square2');
var square3 = document.querySelector('.square3');
/*function move(element,target,resolve){
let timer = setInterval(function(){
var marginLeft = parseInt(element.style.marginLeft, 10);
if(marginLeft == target){
resolve();
}else{
element.style.marginLeft = ++marginLeft+'px';
}
},13);
}*/
function move(element,target,resolve){
let current = 0;
let timer = setInterval(function(){
element.style.transform=`translateX(${++current}px)`;
if(current>target){
clearInterval(timer);
resolve();
};
},13);
}
function animate(element,target){
return new Promise(function(resolve,reject){
move(element,target,resolve);
});
}
animate(square1,100)
.then(function(){
return animate(square2,100);
})
.then(function(){
return animate(square3,100);
});
</script>
</html>
13.11. co
13.11.1 co初體驗(yàn)
let fs = require('fs');
function getNumber(){
return new Promise(function (resolve,reject) {
setTimeout(function(){
let number = Math.random();
if(number >.5){
resolve(number);
}else{
reject('數(shù)字太小');
}
},1000);
});
}
function *read(){
let a = yield getNumber();
console.log(a);
let b = yield 'b';
console.log(b);
let c = yield getNumber();
console.log(c);
}
function co(gen){
return new Promise(function(resolve,reject){
let g = gen();
function next(lastValue){
let {done,value} = g.next(lastValue);
if(done){
resolve(lastValue);
}else{
if(value instanceof Promise){
value.then(next,function(val){
reject(val);
});
}else{
next(value);
}
}
}
next();
});
}
co(read).then(function(data){
console.log(data);
},function(reason){
console.log(reason);
});
13.11.2 co連續(xù)讀文件
let fs = require('fs');
function readFile(filename){
return new Promise(function (resolve,reject) {
fs.readFile(filename,'utf8',function(err,data){
if(err)
reject(err);
else
resolve(data);
})
});
}
function *read(){
let a = yield readFile('./1.txt');
console.log(a);
let b = yield readFile('./2.txt');
console.log(b);
}
function co(gen){
let g = gen();
function next(val){
let {done,value} = g.next(val);
if(!done){
value.then(next);
}
}
next();
}
13.12 Promise/A+完整實(shí)現(xiàn)
function Promise(executor) {
let self = this;
self.status = "pending";
self.value = undefined;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(function () { // 異步執(zhí)行所有的回調(diào)函數(shù)
if (self.status == 'pending') {
self.value = value;
self.status = 'resolved';
self.onResolvedCallbacks.forEach(item => item(value));
}
});
}
function reject(value) {
setTimeout(function () {
if (self.status == 'pending') {
self.value = value;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(item => item(value));
}
});
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循環(huán)引用'));
}
let then, called;
if (x != null && ((typeof x == 'object' || typeof x == 'function'))) {
try {
then = x.then;
if (typeof then == 'function') {
then.call(x, function (y) {
if (called)return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, function (r) {
if (called)return;
called = true;
reject(r);
});
} else {
resolve(x);
}
} catch (e) {
if (called)return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
let self = this;
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : function (value) {
return value
};
onRejected = typeof onRejected == 'function' ? onRejected : function (value) {
throw value
};
let promise2;
if (self.status == 'resolved') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
if (self.status == 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRejected(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
if (self.status == 'pending') {
promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallbacks.push(function (value) {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
self.onRejectedCallbacks.push(function (value) {
try {
let x = onRejected(value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
return promise2;
}
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
}
Promise.all = function (promises) {
return new Promise(function (resolve, reject) {
let result = [];
let count = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
result[i] = data;
if (++count == promises.length) {
resolve(result);
}
}, function (err) {
reject(err);
});
}
});
}
Promise.deferred = Promise.defer = function () {
var defer = {};
defer.promise = new Promise(function (resolve, reject) {
defer.resolve = resolve;
defer.reject = reject;
})
return defer;
}
/**
* npm i -g promises-aplus-tests
* promises-aplus-tests Promise.js
*/
try {
module.exports = Promise
} catch (e) {
}