ES6快速浏覽
let和const關鍵字
let
- 局部變量
- 作用于塊級作用域
,而{}
是函數級作用域var
- 必須先定義後才可使用,而
定義的變量會先提升成var
undefined
- 不可重複聲明變量,
可var
const
- 常量,不可修改
- 塊級作用域
- 對象常量,隻不可修改對象整體,對象内部變量可修改,是以需要當機對象或深度當機
全局變量
-
與var a
window.a
-
與let
不可用const
window
變量的解構
數組的解構指派
let [a,b,c] = [4,5,6];//定義時
[a, b] = [20, 30];//後期
let [age, [salary, [name]]] = [20,[3000,["Bill"]]];//嵌套
let [m, n] = ['a'];//a,undefined
let [v1, v2] = [1,2,3,4,5];//1,2
let [value1, value2, ...others] = [0,1,2,3,4,5,6,7,8,9];//others-->2,3,4,5,6,7,8,9
let [t1] = 20;//報錯
let [x1, x2 = 20] = [100];//100,20 預設值
var [f1, f2 = f] = [1];//1,函數f 預設值也可以為函數
Generator函數的解構指派
// 隻要某種資料結構具有Iterator接口,都可以采用資料形式的解構指派
// 每執行一次,都會指向yield語句
// Generator函數,天生具有Iterator接口
// Generator每調用一次,會暫停
function* gen(x)
{
while(true)
{
yield x;//執行一次,傳回此時x
x = x + 2;
}
}
let [g1, g2, g3, g4] = gen(10);
console.log("g1 = " + g1);//10
console.log("g2 = " + g2);//12
console.log("g3 = " + g3);//14
console.log("g4 = " + g4);//16
對象的解構指派
var {name, age} = {age:30, name:"Bill"};
({x,y} = {x:20, y:30});//後期指派
var {value1, value2} = {value1:30};//30,undefined
var {product:a, price:b} = {product:"iPhone7", price:5000};//被指派變量為a;product隻做比對
var {product:a,price} = {product:"iPhone7 Plus", price:6000};//被指派變量為a,price
var obj = {p:["hello",{x:100}]};var {p:[s,{x:y}]} = obj;//嵌套指派
var {x,y=10} = {x:2};//預設值
var {car} = {name:"Bill"};//解構失敗,undefined
var obj = {fun1:function(x){return x*x;},fun2:function(x,y){return x+y;}};let {fun1,fun2} = obj;//可指派函數
字元串的解構指派
let [a,b,c,d,e] = 'hello';
函數參數的解構指派
function sub([x,y])
{
return x - y;
}
[[1,2],[4,6]].map(([a,b])=>{let result = a + b; console.log(result);});
應用
變量交換
var m = 20;
var n = 30;
[m,n] = [n,m];
函數傳回一個數組,将傳回結果賦給多個變量
function multiNames()
{
return ["Bill", "Mike", "John"];
}
var [name1, name2, name3] = multiNames();
參數使用對象形式,進而可以無序傳參
function sub({x,y,z})
{
return x - y - z;
}
sub({z:20, y:-15,x:10});
提取json資料
var jsonData =
{
id:18,
name:"Mike",
salary:4000
};
let {id, name, salary} = jsonData;
函數參數使用預設值
function fun(name, {flag = true, num = 100})
{
console.log(name, flag, num);
}
fun("John",{}); // {}不能省略
周遊map結構
任何部署了Interator接口的,都可以用for of周遊
var map = new Map();
map.set('id', 49);
map.set('name', "John");
map.set('age', 20);
for(let [key, value] of map)
{
console.log(key, value);
}
// 隻擷取key,無法按下面這種方式隻擷取value
for(let [key] of map)
{
console.log(key);
}
字元串的擴充
字元串變編碼
- .charCodeAt()–>Unicode編碼,不超過\uFFFF
- .codePointAt()–>Unicode2編碼,超過\uFFFF
編碼變字元串
- String.fromCharCode()
- String.fromCodePoint()
var ss = "?";
console.log(ss.length);
console.log("?:" + ss.charCodeAt(0).toString(16));//\ud83d
console.log("?:" + ss.charCodeAt(1).toString(16));//\udc12
console.log("\ud83d\udc12"); // utf-16
// 擷取unicode2編碼
console.log("?:" + ss.codePointAt(0).toString(16));//\u1f412
console.log("?:" + ss.codePointAt(1).toString(16));//\udc12
周遊
for…of
for(let c of 'world')
{
console.log(c);
}
查找子字元串
- indexOf([子字元串],[n開始查詢位置])
- includes()–>true:子字元串存在,false:不存在
- startsWith()–>是否以指定子字元串開頭
- endsWith()–>是否以指定子字元串結尾
// ES5:indexOf
console.log("abcde".indexOf("cd",0));
// ES6
console.log("hello world".includes("w"));
console.log("hello world".startsWith("hello"));
console.log("hello world".endsWith("world1"));
重複輸出
.repeat()–>
- 如果參數是浮點數,會将浮點數轉換為整數,向下取整
- 如果參數是負數或Infinity,抛出異常
- 特例:0到-1之間的浮點數,等同于0
- 如果參數是字元串,将字元串轉換為數值型
- 如果參數值無法轉換為數值,等同于0
console.log("hello".repeat(10));
console.log("h".repeat(0)); // 輸出長度為0的字元串
console.log("wow".repeat(2.9)); //wowwow 2.9 => 2
console.log("ha".repeat(-0.5));//0
console.log("ha".repeat(NaN)); //0
console.log("ok".repeat("4"));//okokokok
console.log("ok".repeat('ok'));//0
模闆字元串
增強型字元串,反單引号
- 保留字元串的格式
- 可大括号嵌入變量
- 可大括号引用表達式計算
- 可大括号引用函數
- 大括号引用字元串依然是字元串
- 大括号引用未定義變量異常
console.log(`hello
world`
);
var s = `你的名字是${name}`;
console.log(s);
var x = 20;
var y = 30;
console.log(`${x} + ${y} = ${x + y}`);
function fun()
{
return "It's beautiful";
}
console.log(`wow ${fun()}`);
console.log(`Hello ${'world'}`);
// 引用模闆字元串本身
// 方法1:
let str1 = 'return ' + '`Hello ${name}`';
let func1 = new Function('name', str1);
console.log(func1("Mike"));
// 方法2:
let str2 = '(name)=>`Hello ${name}`';
let func2 = eval.call(null, str2);
console.log(func2('Mary'));
- 标簽模闆–>fun``
function func1(s, n1, n2,n3)
{
console.log(s);
console.log(n1);
console.log(n2);
console.log(n3);
}
// 調用格式:函數名+模闆字元串
var n1 = 20;
var n2 = 30;
func1`abc${n1} xyz${n2}ok${n2 + n1}ddd`;
- .raw``方法–>直接輸出最原始的字元串
console.log(String.raw`abc \n xyz`);
console.log(String.raw`abc \\n xyz`);
數值的擴充
二進制、八進制
從ES5開始,在嚴格模式中,八進制數值就不再允許使用字首0表示,ES6進一步明确了這一點,要使用0o。
- 二進制:0b/0B
- 八進制:0o/0O
var n1 = 345;
var n2 = 0o531;
if(n1 == n2)
{
console.log("n1 == n2");
}
Number.infinite、Number.NAN
分别用來檢測Infinite(數值且有限)和NaN(非數值,不是數字)兩個值
傳統方法:
isFinite
和
isNaN
新方法與傳統的全局方法的差別:
- 傳統方法先調用
将非數值轉換為數值,再進行判斷。Number()
- 新方法隻對數值有效,對于非數值一律傳回
。false
console.log(Number.isFinite(20));//true
console.log(Number.isFinite(0.4));//true
console.log(Number.isFinite(NaN));//false
console.log(Number.isFinite());//false
console.log(Number.isFinite(Infinity));//false
console.log(Number.isFinite('hello')); // false
console.log(Number.isFinite(true)); // false
console.log("--------------");
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(15)); // false
console.log(Number.isNaN(9/NaN)); // true
console.log(Number.isNaN('false'/0)); // true;
// NaN:非數值
console.log(Number.isNaN('1'/'2')) ;// false
console.log(Number.isNaN('1'/'false')) ;// true
Number.parseInt、Number.parseFloat、Number.isInterger
// ES5的寫法
console.log(parseInt('44.66')); // 舍去取整
console.log(parseFloat('65.34'));
// ES6的寫法
console.log(Number.parseInt('44.66')); // 舍去取整
console.log(Number.parseFloat('65.34'));
console.log(Number.parseInt === parseInt); // true
console.log(Number.parseFloat === parseFloat); // true
// Number.isInteger用來判斷一個值是否為整數,20和20.0是同一個值
console.log(Number.isInteger(20)); // true
console.log(Number.isInteger(20.4)); // false
console.log(Number.isInteger(20.0)); // true
console.log(Number.isInteger("14")); // false
console.log(Number.isInteger(true)); // false
判斷浮點數相等Number.EPSILON
Number.EPSILON
極小值
if(((0.1 + 0.2) - 0.3) < Number.EPSILON)
{
console.log("等于");
}
else
{
console.log("不等于");
}
Math
-
.trunc:用于去除一個數的小數部分,傳回整數部分,正負号保留
對于非數值,Math.trunc内部會使用Number方法将其轉換為數值
對于空值和無法取整數的值,傳回NaN
-
.sign:用于判斷一個數到底是整數、負數、還是零
參數為正數:傳回1
參數為負數:傳回-1
參數為0:傳回0
參數為-0:傳回-0
其他值:傳回NaN
-
- .cbrt:用于計算一個數的立方根
-
.clz32:JavaScript的整數使用32位二進制形式表示
方法傳回一個數的32位無符号整數形式有多少個前導0
對于小數,隻考慮整數部分
如果無法轉換為數值,傳回的是32,而不是NaN
-
.imul:傳回兩個數以32位帶符号整數形式相乘的結果,傳回的也是一個32位的帶符号整數
如果兩個整數的乘積超過了2^53次方,JavaScript無法保證結果的精度
- .fround:傳回一個數的單精度浮點數形式
- .hypot:傳回所有參數的平方和的平方根
- .expm1:傳回e^x-1
- .log1p:傳回ln(1+x)
- .log10:傳回以10為底的x的對數,如果x小于0,則傳回NaN
- .log2:傳回以2為底的x的對數,如果x小于0,傳回NaN
數組的擴充
.from(對象轉換為數組)
兩類對象:
- 類似數組的對象,即任何有length屬性的對象
- 可周遊的對象(iterable),字元串,集合等
以key作為數組的索引(從0開始),如果key不是按順序給出,那麼數組目前元素值是undefined,需要使用length定義數組的長度,如果長度比數組元素個數大,後面的值都是未定義,如果小,後面的值被忽略,對象中屬性順序可以颠倒
let obj1 = {
'0':'hello',
'2':'中國',
'1':'world',
'3':'ok',
length:4
};
let array1 = Array.from(obj1);
console.log(array1);
Array.from方法還可以接受第二個參數,和map類似
console.log(Array.from([1,2,3,4],x=>x+x));
// 等同于下面的代碼
console.log(Array.from([1,2,3,4]).map(x=>x+x));
// 填充随機數數組
console.log(Array.from({length:20},()=>Math.random()));
.of方法(一組值轉換為數組)
console.log(Array.of(1,2,3,4,5));
console.log(Array.of(3));
console.log(Array.of(10).length);
.copyWithin(遷移數組元素)
target -->必選,遷移到哪兒
start -->必選,從哪兒開始遷移
end–>可選,哪兒結束遷移,預設到結尾
**[start,end)**遷移的内容,閉開區間
console.log([1,2,3,4,5,6].copyWithin(0, 3));//456456
console.log([1,2,3,4,5,6].copyWithin(1, 3,5));//145456
console.log([1,2,3,4,5,6].copyWithin(1, -3,-1));//145456
.find .findIndex(查找)
用來掃描數組,用來查找第一個滿足條件的數組元素
-
傳回數組元素find
-
傳回數組元素的索引findIndex
var n = [3,5,1,10,6].find((n)=>n>3)
console.log(n);
var m = [1,2,-4,6].find(function(value,index,arr){
return value < 0
});
console.log(m);
var p = [1,2,-4,6].find((value,index,arr) => value < 0)
console.log(p);
var index = [1,2,-4,6].findIndex((value,index,arr) => value < 0)
console.log(index)
find和findIndex,可以接收第二個參數,用來綁定回調函數的this對象
和indexOf的差別:find和findIndex可以用來查找NaN
console.log([NaN].indexOf(NaN));
console.log([NaN].findIndex((n)=>Object.is(NaN,n)));
.fill(用給定的值填充數組)
常用來初始化數組
可選第二個第三個參數
console.log(new Array(10).fill(123));//[123,123,123......]
console.log(['x','y','z'].fill("hello world"));//["hello world","hello world","hello world"]
console.log(['1','2','3','5'].fill("xyz",1,3));//['1','xyz','xyz','5']
.entries、.keys(周遊)
- .keys–>索引
- .entries–>索引+值
for(let index of['a','b','c'].keys())
{
console.log(index)//0 1 2
}
for(let [index,value] of['a','b','c'].entries())
{
console.log(index) //0 1 2
console.log(value) //a b c
}
數組的空位
沒有值
var arr1 = Array(3); // [,,,],應該是三個逗号而不是兩個
var arr2 = [,,,]
var arr3 = [undefined, undefined, undefined]
console.log(0 in arr2); // false,第0個位置是否有值
console.log(0 in arr3); // true
- ES5:大多數情況下會忽略空位
forEach、filter、every和some都會跳過空位
map會跳過空位,但會保留這個值
join和toString會将空位視為undefined
undefined和null 會被處理成空字元串
- ES6明确将空位轉為undefined
- 擴充運算符(…)也會将空位轉為undefined
function fun(a,b,c)
{
console.log(a);
console.log(b);
console.log(c);
}
var arr4 = ['x',,'y'];
fun(...arr4);//x undefined y
- for…of也會将空位轉為undefined
let arr5 = ['a',,'b']
for(let i of arr5)
{
console.log(i);//a undefined b
}
函數的擴充
函數的預設值
ES5判斷的方法
function write(x,y)
{
// 無效的值:undefined、NaN、null、''(空串)
y = y || 'JavaScript';//判斷y是否有效,若有效傳回第一個值
console.log(x,y);
}
write('Hello', 'World');
write('Hello');//Hello JavaScript
write('Hello', NaN);//Hello JavaScript
write('Hello', null);//Hello JavaScript
write('Hello', '')//Hello JavaScript
ES5其他判斷函數參數預設值的方法
function write1(x,y)
{
if(typeof y == 'undefined')
{
y = 'JavaScript'
}
console.log(x,y);
}
// 方法2
function write2(x,y)
{
if(arguments.length == 1)
{
y = 'JavaScript'
}
console.log(x, y)
}
write1('Hello')//Hello JavaScript
write1('Hello', '')//Hello
write2('Hello')//Hello JavaScript
write2('Hello', '')//Hello
ES6預設值
function write3(x,y='JavaScript')
{
console.log(x,y)
}
write3('Test');//Test JavaScript
write2('Test', '');//Test
function Product(name='iPhone', price=6000)
{
this.name = name;
this.price = price;
}
var product = new Product()
console.log(product.name);
console.log(product.price);
參數預設值與解構指派預設值結合
function fun1({x,y=5})
{
console.log(x,y)
}
//fun1();//報錯
fun1({});//undefined 5
fun1({x:10});//10 5
fun1({x:12,y:20});//12 20
解構指派預設值和函數參數預設值結合
function fun2(n, {value1='', value2=20})
{
console.log(value2);
}
//fun2(123) // error
fun2(123,{});// 20
fun2(123,{value2:100});// 100
兩種例子,有什麼差別
function f1({x = 0, y = 0} = {})
{
console.log(x,y);
}
function f2({x, y} = {x:0, y:0})
{
console.log(x,y);
}
f1()// 0 0
f2()// 0 0
f1({x:10, y:20})// 10 20
f2({x:10, y:20})// 10 20
f1({x:20}); // 20, 0
f2({x:20}); // 20, undefined
f1({}); // 0 0
f2({}); // undefined undefined
f1({p:10}); // 0 0
f2({p:20}); // undefined undefined
參數預設值的位置與調用方式
不同位置,調用方法不同
// demo1
function fun1(x,y,z=20)
{
console.log(z);
}
fun1(1,2,3);
fun1(1,2);//前兩個參數必須有
// demo2
function fun2(x, y=123,z = 200)
{
console.log(y,z);
}
fun2(20);//第一個參數必須有
fun2(20,44);
// demo3
function fun3(x, y = 345, z)
{
console.log(y);
}
fun3(20,123,100);//全部參數必須有
//fun3(20,,100);//報錯
length屬性(擷取無預設參數的個數)
length屬性從左向右檢測函數參數屬性,一旦遇到第一個有預設值的參數,就會停止檢測,并傳回第一個帶有預設值參數前面參數的個數
console.log((function(x){}).length)//1
console.log((function(x = 10){}).length)//0
console.log((function(x,y=100,z,t){}).length) // 1
console.log((function(x,y,z=100){}).length) // 2
console.log((function(x,...args){}).length) // 1
函數參數的作用域
var x = 10;
function fun1(m, y = x)
{
console.log(y);
}
fun1(100);//100
/* error
function fun2(m, y = n)
{
let n = 90;
console.log(y);//n未定義錯誤
} */
// fun2(100)
//預設值為函數
var value = 'hello';
function fun3(value = 'xyz', func = x=>value){
console.log(func());
}
fun3()//xyz
參數預設值應用
function throwIfMissing()
{
throw new Error('必須指定該參數值');
}
function fun1(x,y = throwIfMissing())
{
console.log(y);
}
fun1(10);//抛出異常-->必須指定該參數值
function fun2(x, y = undefined)
{
console.log(y)//undefined
}
rest參數
放在函數參數最後面,指任意多個參數
function add(...values)
{
let sum = 0;
for(var v of values)
{
sum += v;
}
return sum;
}
console.log(add(1,4,5,7))
function fun1(a,b,...c)
{
console.log(a,b);
for(var v of c)
{
console.log(v)
}
}
fun1(1,2,3,4)
arguments和rest參數的差別:
- arguments不是數組
- rest參數是數組
function sort1()
{
const sortedNumbers = Array.prototype.slice.call(arguments).sort();//不能直接調用sort()
return sortedNumbers;
}
console.log(sort1(3,2,1))
function sort2(...numbers)
{
const sortedNumbers = numbers.sort();//能直接調用sort()
return sortedNumbers;
}
console.log(sort2(5,4,3));
參數長度差別:
- 整個函數的參數是形參,不考慮rest參數和帶預設值的參數
- arguments考慮的是值參
function fun2(a,b,c,d,...e)
{
// arguments考慮的是值參
console.log(arguments.length) // 10
}
fun2(1,2,3,4,5,6,7,8,9,10)
console.log((function (a,b,c,d,...e)
{
console.log(arguments.length) // 10
}).length) // 4 考慮的是形參,但不包括rest參數和帶預設值的參數
擴充運算符
...
三個點表示,将數組變成單值
var values = [1,2,3,4];
console.log(...values);// 1 2 3 4
console.log(values);//[1,2,3,4]
console.log('abc','xyz',...values,30);
對于rest參數,調用時隻能傳遞多個單值,不能傳遞數組
function addNumbers(...numbers)
{
var sum = 0;
for(var n of numbers)
{
sum+=n;
}
return sum;
}
var arr = [1,4,7,12,-3];
// console.log(addNumbers(arr)) // error 對于rest參數,隻能傳遞多個單值,不能傳遞數組
console.log(addNumbers(...arr)) // ok
擴充運算符應用
合并數組
var arr1 = ['a','b']
var arr2 = ['c','d']
var arr3 = ['e','f']
// ES5
var arr4 = arr1.concat(arr2, arr3);
console.log(arr4);
// ES6
var arr5 = [...arr1, ...arr2, ...arr3]
console.log(arr5)
擴充運算符與解構指派結合
rest必須是數組的最後一個元素
const [a,...b] = [1,2,3,4]; // ...b rest參數
console.log(a);
console.log(...b);
const [first, ...rest] = [];
console.log(first); // undefined
console.log(rest); // []
const [x, ...y] = [10];
console.log(x)//10
console.log(y)//[]
//const [...k, t] = [10,20] // error rest必須是數組的最後一個元素
函數的傳回值
function fun1()
{
return [1,2,3,4]
}
function fun2(a,b,c,d)
{
console.log(a,b,c,d)
}
var values = fun1();
fun2(...values) // 可以通過擴充運算符将函數傳回的數組解析成多個值
字元串
字元串拆成數組
var str = "hello";
var strArray = [...str]; // 不能是...str
console.log(strArray);
正确識别32位Unicode
console.log('a\uD83D\uDE80b'.length); // 4,無法獲得真正的字元串長度
console.log([...'a\uD83D\uDE80b'].length);//3,真正的長度
console.log([...'a\uD83D\uDE80b'][1]);//小火箭圖示
function length(str)
{
return [...str].length
}
console.log(length([...'a\uD83D\uDE80b']));//3
var str = 'a\uD83D\uDE80b';//\uD83D\uDE80b-->小火箭
console.log(str);//a小火箭
var str1 = str.split('').reverse().join('');
console.log(str1);//翻轉失敗,buDE80\uD83D\a'
var str2 = [...str].reverse().join('');
console.log(str2);//小火箭a,正确翻轉
可以處理類似數組的對象(Iterator)
let map = new Map(
[
[1, 'one'],
[2, 'two'],
[3, 'three']
]
);
let arrKeys = [...map.keys()];
console.log(...arrKeys);//1 2 3
let arrValues = [...map.values()];
console.log(...arrValues);//one two three
name屬性
傳回該函數的屬性名
function myfun(){}
console.log(myfun.name);
var fun1 = function(){}//匿名函數
// ES5是空串,ES6是變量名
console.log(fun1.name) // fun1
var fun2 = function process(){}
console.log(fun2.name); // process
console.log((new Function).name) // anonymous
function test(){}
console.log(test.bind({}).name) // bound test,前置bound
箭頭函數
使用=>定義的函數
- =>的左側表示函數的參數,右側表示函數體
- 如果=>右側隻有一條語句,該條語句将作為return的參數
var f = x => x;
console.log(f(100))
// 相當于
var f1 = function(x)
{
return x;
}
// 如果箭頭函數的參數沒有,或有多個參數
var f2 = ()=> 123;
f2 = function()
{
return 123;
}
// 箭頭函數和變量結構結合使用
var f5 = ({first, last}) => first + '.' + last;
var person = {first:'Bill', last:'Gates'};
console.log(f5(person));
//相當于
function f6(person)
{
return person.first + '.' + person.last;
}
console.log(f6(person));
可以簡化回調函數
var arr2 = [1,2,3,4,5].map(x=>x*x)
console.log(...arr2)
var result2 = values.sort((a,b)=>a-b);
console.log(...result2)
rest參數也可以用于箭頭函數
var fun = (...nums)=>nums; // rest參數也可以用于箭頭函數
console.log(fun(1,2,3,4,5,6))
對象的擴充
屬性和方法簡潔表示
屬性定義時
// ES5
var obj1 = {name:'Bill'};
console.log(obj1.name)
// ES6
var name = 'Mike';
var obj2 = {name};
console.log(obj2.name);
傳回值時
// ES6中的傳回方式
function fun1(x,y,z)
{
return {x,y,z};
}
// ES5中的傳回方式
function fun2(x,y,z)
{
return {x:x,y:y,z:z};
}
console.log(fun1(1,2,3))
console.log(fun2(4,5,6));
方法定義時
// ES5:方法的傳統表示法
var obj3 = {
process:function()
{
return "I love you."
}
};
// ES6:方法的簡潔表示法
var obj4 = {
process()
{
return "How are you?"
}
}
console.log(obj3.process());
console.log(obj4.process());
簡潔定義示例
let price = 7000;
var product =
{
productName:'iPhone7 Plus',
price,
buy(name, currentPrice)
{
console.log('已經購買');
return {name, currentPrice,discount:price - currentPrice};
}
}
console.log(product.productName);
console.log(product.buy('John', 6500));
屬性名表達式
ES5,有三種向對象中添加屬性的方法,其中兩種是動态的,一種是靜态的
// 靜态添加屬性
var obj1 = {
name:'Bill'
};
// 動态一
obj1.age = 30;
// 動态二
obj1['salary'] = 2000;
使用表達式
obj1['hello' + 'world'] = 'hello world';
const p = 'x';
obj1[p+p] = 'xx';
console.log(obj1.name)
console.log(obj1.age)
console.log(obj1.salary)
console.log(obj1.helloworld);
console.log(obj1.xx);
ES6,定義時方括号内使用表達式
var obj2 =
{
name:'Mike',
['product' + 1]:'iPhone8'
}
console.log(obj2.product1);
var y = 'hello';
var obj3 =
{
[y]:'world' // 如果要解析變量,需要使用方括号
};
console.log(obj3.hello)
var obj4 =
{
['hello']:'hello world' // ok,如果屬性名直接是一個字元串值,可以不适用方括号
// 當然,使用方括号也可以
}
console.log(obj4.hello)
方法名也可以
let obj5={
['pro' + 'cess']()
{
console.log('process')
}
};
obj5.process();
屬性名表達式和簡潔表示法不能同時使用,否則會報錯
var name1 = 'kkk';
/* error
* var obj6 = {
[name1]
}*/
var obj6 = {
[name1]:'xxx' // 屬性名使用表達式,右側必須指定屬性值
}
console.log(obj6.kkk)
Object.is
console.log(Object.is('abc', 'abc'));
console.log(Object.is({},{}));
Object.is方法
- +0和-0不相等
- NaN等于自身
console.log(+0 === -0); // true
console.log(Object.is(+0, -0)); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
Object.assign
複制對象屬性,assign參數有兩個:
- targetObject
- …rest參數(sourceObject)
var target = {id:10};
var source1 = {name:'iPhone'};
var source2 = {price:7000};
var source3 = {location:'中國'};
Object.assign(target, source1,source2,source3);//将source1 2 3屬性添加到target
console.log(target);
若有重複屬性,後面的覆寫前面的
target = {a:1};
source1 = {a:20};
source2 = {b:30};
source3 = {b:50};
Object.assign(target, source1, source2, source3);//a 20 b 50
// Object.assign(target, source1, source3, source2);//a 20 b 30
console.log(target);
有嵌套式時,直接覆寫,隻複制自身的屬性
var target = {a:{b:'c',d:'e'}};
var source = {a:{b:'ok'}};
Object.assign(target, source);// a {b:'ok'}
console.log(target);
不可枚舉的屬性和繼承的屬性不會複制, 不可枚舉:enumerable = false
var target = {a:'a'};
var source = Object.defineProperty({b:'b'},'name',{
enumerable:false,
value:'Bill'
});
console.log(target)
console.log(source)
Object.assign(target, source)//a b
console.log(target)
可以複制數組
var targetArray = [1,2,3];
var sourceArray = [4,5];
Object.assign(targetArray,sourceArray)
console.log(targetArray); // [4,5,3]
應用
為對象添加屬性
class MyClass
{
constructor(x,y)
{
Object.assign(this, {x,y})
}
}
var my = new MyClass(10,20);
console.log('x', my.x);
console.log('y', my.y);
// 方法二
Object.assign(my, {name:'Bill'});
console.log('name', my.name);
// 方法三,類為原型添加
Object.assign(MyClass.prototype, {price:1200});
console.log('price', my.price);
為對象添加方法
Object.assign(MyClass.prototype, {
add(x,y)
{
return x + y;
}
});
console.log('add', my.add(10, 40));
克隆對象
function cloneObject(origin)
{
return Object.assign({},origin);
}
var origin = {id:20, name:'Bill'};
console.log('origin',cloneObject(origin));
合并多個對象
var target = {a:10}
var source1 = {b:20}
var source2 = {c:30}
var source3 = {d:40}
const merge = Object.assign(target, source1, source2, source3);
console.log(merge)
為屬性指定預設值
const DEFAULTS =
{
name:'Mike',
price:2000
}
function process(options)
{
var obj = {}
Object.assign(obj, DEFAULTS, options)
return obj;
}
console.log(process({a:20,price:3000}))//a 20 name Mike price 3000
Symbol
為了解決對象屬性沖突問題,第7種資料類型
//let s = Symbol(); // s就是獨一無二的
let s = Symbol("s"); // s就是獨一無二的
console.log(typeof s);//Symbol
let ss = Symbol("ss");
console.log(typeof ss);//Symbol
console.log(s);//Symbol(s)
console.log(ss);//Symbol(ss)
獨一無二
let aa = Symbol("x");
let bb = Symbol("x");
if(aa === bb)
{
console.log("aa === bb");
}
else
{
console.log("aa != bb");
}
//"aa != bb
Symbol不能與其他類型的值進行運算,否則會報錯
不能隐式轉換到字元串
可以顯式轉換為String
var sym = Symbol('Test Symbol');
//var sss = "hello" + sym; // 不能隐式轉換到字元串
var str = String(sym); // 方法1
console.log(str)
console.log(sym.toString()); // 方法2
Symbol可以轉為布爾值,但不能轉為數值
var sym1 = Symbol("abc");
console.log(Boolean(sym)); // true
應用
作為屬性名
var id = Symbol();
var name1 = Symbol();
var obj1 = {};
// 方法1
obj1[id] = 200;
obj1[name1] = 'Mike';
// 方法2
var obj2 = {
[id]:300 // 如果不加方括号,name屬性名的類型就是String
// 而不是Symbol
};
// 方法3
var obj3 = {};
Object.defineProperty(obj3, name1, {value:'John'});
console.log('obj1 id', obj1[id]);//200
- Symbol不能使用點(.)運算符
- 對象内部也需要使用方括号定義屬性
let sym = Symbol();
let obj = {
// Symbol屬性必須加方括号,否則就變成了字元串屬性了
[sym]:function(){console.log('hello world')}
};
obj[sym]();
定義常量
let CONST ={
DEBUG:Symbol('debug'),
INFO:Symbol('info'),
WARNING:Symbol('warning')
};
console.log(CONST.INFO.toString());
const COLOR_WHITE = Symbol('white');
const COLOR_RED = Symbol('red')
周遊
getOwnPropertySymbols,隻Symbol屬性
var obj = {name:'Bill'};
var a = Symbol('a');
var b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
var objectSymbols = Object.getOwnPropertySymbols(obj);
console.log(objectSymbols)
for in非Symbol屬性
for(var key in obj)
{
console.log(key);
}
Reflect.ownKeys,同時擷取Symbol和非Symbol屬性
console.log(Reflect.ownKeys(obj));
可以利用Symbol的特性為對象添加一些非私有的,但又希望 隻用于内部的方法或屬性
var size = Symbol('size');
class Collection
{
constructor()
{
this[size] = 0; // 為對象添加一個Symbol類型的名為size的屬性,初始值為0
}
add(item)
{
this[this[size]] = item;
this[size]++;
}
static sizeOf(instance)
{
return instance[size];
}
}
var cc = new Collection();
console.log(Collection.sizeOf(cc));//0
cc.add('ok');//0 ok
cc.add("xyz");//1 xyz
console.log(Collection.sizeOf(cc));//2
console.log(Object.keys(cc));//["0","1"]
console.log(Object.getOwnPropertySymbols(cc))//[Symbol('size')]
.for與.keyFor方法
Symbol.for首先會向全局環境注冊名為key的Symbol變量
第一次調用for方法,會傳回一個新的Symbol變量,如果以後再調用for方法,并且for方法的參數值(key)在全局環境中存在,那麼for方法會傳回這個已經注冊的Symbol變量
var name1 = Symbol("name")
var name2 = Symbol("name")
console.log("name1 === name2", name1 === name2);//false
var id1 = Symbol.for("id")
var id2 = Symbol.for("id")
console.log("id1 === id2", id1 === id2);//true
keyFor:傳回某個已經注冊的Symbol變量的key
console.log(Symbol.keyFor(id1));//id
console.log(Symbol.keyFor(name1));//未定義,因為name1并沒有全局環境注冊
console.log(Symbol.keyFor(id2));//id
Proxy
用于攔截對象的操作
var obj1 = {name:'Bill',
process:function()
{
//console.log('----')
console.log('process Bill')
}
};
var obj2 = {name:'Mike',
process:function()
{
//console.log('----')
console.log('process Mike');
}
};
console.log(obj1.name);
obj1.process();
console.log(obj2.name);
obj2.process();
var obj = new Proxy(target, handler);
target:要攔截的對象
handler:攔截的動作,取代原來的方法,要想以前功能依然在,使用Reflect.method
var obj = new Proxy(obj1, {
get:function(target, key,receiver)
{
console.log('----');
return Reflect.get(target, key, receiver);
}
});
console.log(obj.name);
obj.process();
用代理攔截多個對象,單獨将handler提出來
var handler = {
get:function(target, key,receiver)
{
console.log('----');
return Reflect.get(target, key, receiver);
}
}
var proxyObj1 = new Proxy(obj1, handler);
var proxyObj2 = new Proxy(obj2, handler);
console.log(proxyObj1.name);
proxyObj1.process();
console.log(proxyObj2.name);
proxyObj2.process();
讀取攔截get
攔截屬性的讀取操作
var product = {
name:'iPhone'
};
//讀取不存在的屬性抛出異常
var proxy = new Proxy(product, {
get:function(target, property)
{
if(property in target)
{
return target[property];
}
else
{
throw new ReferenceError("屬性 \"" + property + "\"不存在!");
}
}
});
console.log(proxy.name);
get代理的繼承
let proto = new Proxy({},{
get(target, propertyKey,receiver)
{
console.log('GET ' + propertyKey);
return target[propertyKey];
}
});
let obj = Object.create(proto);
obj.name;
應用:利用get代理讀取數組的負索引
var arr1 = [1,2,3];
//console.log(arr1[-1]);
// arr1[0] = arr1[-3] arr1[2] = arr1[-1]
function createSuperArray(...elements)
{
let handler = {
get(target, key, receiver)
{
let index = Number(key);
if(index < 0)
{
key = String(target.length + index)//-1-->length-1
}
return Reflect.get(target, key,receiver);
}
}
let target = [];
target.push(...elements);
return new Proxy(target, handler);
};
let arr2 = createSuperArray(1,2,3);
console.log(arr1[1]);//2
console.log(arr1[-1]);//未定義
console.log(arr2[1]);//2
console.log(arr2[-1]);//3
console.log(arr2[-2]);//2
console.log(arr2[-3]);//1
寫操作攔截set
校驗屬性值
var product = {
name:'特斯拉汽車',
price:80 // 30-180
};
let validator = {
set:function(obj, key, value)
{
// 首先判斷屬性名
if(key == 'price')
{
if(!Number.isInteger(value))
{
throw new TypeError('價格必須是整數');
}
if(value < 30 || value > 180)
{
throw new RangeError('價格必須在30到180之間');
}
}
obj[key] = value;
}
};
let product1 = new Proxy(product, validator);
// product1.price = 200;
product1.price = 170;
console.log(product1.price);
// product1.price = 'abc';
控制屬性是否可通路
對象的内部屬性 屬性名以下劃線(_)開頭
var handler = {
get(target, key)
{
invariant(key,'get');
return target[key];
},
set(target, key,value)
{
invariant(key,'set');
return true;
}
};
function invariant(key,type)
{
if(key[0] == "_")
{
throw new Error(`内部屬性不能被通路 ${type}`);
}
};
var obj = {
name:'Bill',
_value:20
};
var objProxy = new Proxy(obj, handler);
console.log(objProxy.name);
// console.log(objProxy._value);
objProxy.name = "Mike";
// objProxy._value = 40;
攔截函數的調用:call和apply
改變函數傳回值
var fun1 = function(){return '世界您好!';}
var handler = {
apply:function()
{
return 'hello world';
}
};
console.log(fun1());//世界您好!
var funProxy = new Proxy(fun1, handler);
console.log(funProxy());//hello world
修改原傳回值
function sum(num1, num2)
{
return num1 + num2;
}
console.log(sum(20,40));//60
var twice = {
apply(target, ctx, args)
{
return Reflect.apply(...arguments) * 2;
}
};
var sumProxy = new Proxy(sum, twice);
console.log(sumProxy(20,40));//120
console.log(sumProxy.call(null, 20,40));//120
console.log(sumProxy.apply(null, [30,50]));//160
攔截in操作has
隐藏屬性,隻影響in判斷
var handler = {
has(target, key)
{
//console.log("<" + key + ">");
if(key[0] == '_')
{
return false;
}
return key in target;
}
};
var obj = {name:'iPhone7', price:5800, _value:200};
var objProxy = new Proxy(obj, handler);
console.log('_value' in obj); // true
console.log('_value' in objProxy); // false
console.log('price' in obj); // true
console.log('price' in objProxy); // true
has方法并不會影響for in操作
for(key in objProxy)
{
console.log(key)
}
如果原對象不可配置或禁止擴充,那麼這時has攔截會報錯
var obj1 = {a:20};
Object.preventExtensions(obj1);
var proxy = new Proxy(obj1, {
has:function(target, key)
{
return false;
}
});
// 'a' in proxy //抛出異常
攔截new指令contrust
var handler = {
construct(target, args)
{
console.log('construct');
return new target(...args);
}
};
var proxy = new Proxy(function(){}, handler);
new proxy();//contrust
傳回的不是對象,抛出異常
var handler1 = {
construct(target, args)
{
console.log('construct');
return 20;
}
};
var proxy1 = new Proxy(function(){}, handler1);
//new proxy1(); //抛出異常
可以傳回别的對象
var handler2 = {
construct(target, args)
{
console.log('construct');
return {name:'Bill'};
}
};
var proxy2 = new Proxy(function(){}, handler2);
console.log(new proxy2().name);
攔截delete操作deleteProperty
var handler = {
deleteProperty(target, key)
{
delete target[key]; // 需要在該方法中再次删除對象屬性
console.log('删除了' + key + "屬性");
return true;
}
};
var obj = {name:'Bill', age:40};
var objProxy = new Proxy(obj, handler);
console.log(objProxy.age);
delete objProxy.age;
console.log(objProxy.age);
攔截define操作defineProperty
攔截動态添加屬性
var obj = {};
var handler = {
defineProperty(target, key, descriptor)
{
console.log("<" + key + ">")
// 要想讓被攔截的操作仍然發揮原來的作用,需要調用
// Reflect.method
// 否則defineProperty會進入遞歸
return Reflect.defineProperty(target, key, descriptor);
}
}
var proxy = new Proxy(obj, handler);
proxy.name = "Bill";
console.log('name' in proxy);
Reflect
将Object對象的一些明顯屬于語言層面的方法放到Reflect中
Object和Reflect的方法是一樣的,以後再部署語言層面的方法會隻部署在Reflect中
defineProperty、deleteProperty、has
Generator
是個狀态機,并延後執行 ,不會立刻執行
普通函數加*,一個yield表示一個狀态
function* helloworldGenerator()
{
console.log('第一次執行');
yield 'hello'; // 一個yield表示一個狀态
console.log('第二次執行');
yield 'world';
console.log('第三次執行');
return 'ending'; // 非必須,也表示一個狀态,最後一個狀态
}
var hw = helloworldGenerator(); // 傳回一個周遊器對象,狀态的集合
var obj = hw.next(); // 狀态切換,next傳回一個普通對象
console.log(obj); // value-hello done-false
console.log(obj.value); // hello
console.log('-------------------------------');
obj = hw.next(); // 狀态切換,next傳回一個普通對象
console.log(obj); // value-world done-false
console.log(obj.value);
console.log('-------------------------------');
obj = hw.next(); // 狀态切換,next傳回一個普通對象
console.log(obj); // value-ending done-true
console.log(obj.value);
console.log('-------------------------------');
obj = hw.next(); // 狀态切換,next傳回一個普通對象
console.log(obj); // value-undefined done-true
console.log(obj.value); // undefined
console.log('-------------------------------');
yield
function sum(...values)
{
var n = 0;
for(let v of values)
{
n += v;
}
console.log('sum');
return n;
}
function* gen()
{
yield 10 + 20;
yield 10 * sum(1,2,3,4);
}
var obj = gen();
console.log(obj.next());
console.log(obj.next());
Generator函數可以不用yield,Generator就變成了暫緩執行的函數
function* fun()
{
console.log('fun');
}
var obj1 = fun();
obj1.next();
yield不能用于普通函數,否則會抛出異常
function process()
{
// yield 'abc';
}
yield用在一個表達式中,必須放在圓括号裡
function* gen1()
{
// console.log('Hello' + yield 'world');
console.log('Hello' + (yield 'world'));
}
var obj2 = gen1();
obj2.next();// fun,暫停了
obj2.next();// helloundefined
yield語句用于函數參數或用于指派表達式的右邊,可以不加圓括号
function* gen2()
{
sum(yield '1', yield '2');
var input = yield 20;
}
gen2().next();
next()
function* gen()
{
var n = yield 20;
console.log(n);
}
var obj = gen();
obj.next(); //暫停
obj.next(200); // 需要給第二個next方法傳遞參數
- next方法執行時,yield後面的變量值即next方法傳回的value值,next方法本身傳回done和value兩個屬性的對象,并可以通過傳值改變這屬性value的值。
- next()傳的參數即此次yield語句的值
function* gen1()
{
for(var i = 0; true;i++)
{
var reset = yield i;
if(reset)
{
i = -1;
}
}
}
var obj1 = gen1();
console.log(obj1.next());//0 false
console.log(obj1.next());//1 false
console.log(obj1.next());//2 false
obj1.next(true);//重新開始,i=-1後加一 // 0 false
console.log(obj1.next()); // 1 false
console.log(obj1.next()); // 2 false
console.log(obj1.next()); // 3 false
for of
切換狀态,并不包含return的傳回值
function* gen()
{
yield 'a';
yield 'b';
yield 'c';
yield 'd';
return 'x';
}
// a b c d
for(let v of gen())
{
console.log(v);//a b c d
}
fibonacci數列
function* fibonacci()
{
let [prev,curr] = [0, 1];
for(;;)
{
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
/*var obj = fibonacci();
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().value);*/
for(let n of fibonacci())
{
if(n > 1000) break;
console.log(n);
}
for…of循環可以寫出周遊任何對象的方法。原生的JavaScript對象 沒有周遊接口
function* objectEntries(obj)
{
let propKeys = Reflect.ownKeys(obj);//獲得對象所有屬性
for(let propKey of propKeys)
{
yield [propKey, obj[propKey]];
}
}
let arr = {name:'Bill', age:30};
for(let [key, value] of objectEntries(arr))
{
console.log(`${key}:${value}`);
}
throw()
var g = function*()
{
while(true)
{
try
{
yield;
}
catch(e)
{
if(e != 'a') throw e; //throw異常後結束generator函數
console.log('Generator内部錯誤', e);
}
}
}
var obj = g();
obj.next();
try
{
obj.throw('a');//内部 a
obj.throw('b');//外部 b
}
catch(e)
{
console.log('外部異常', e);
}
不用try catch,throw後直接跳出循環
var gg = function*()
{
while(true)
{
yield;
console.log('内部捕獲', e);
}
};
var obj1 = gg();
obj1.next();
try
{
obj1.throw('a'); // 外部異常a
obj1.throw('b'); // 沒有執行
}
catch(e)
{
console.log('外部捕獲', e);
}
應用:提前結束狀态
function fun(s)
{
console.log(s + s);
}
function *gen()
{
try
{
var a = yield fun('a');
var b = yield fun('b');
var c = yield fun('c');
}
catch(e)
{
console.log(e);
}
}
var obj2 = gen();
obj2.next();// aa
//console.log(obj2.next());
obj2.throw('aaa');//aaa,提前結束
obj2.next();//
obj2.next();
應用:函數内部抛出異常,如果内部沒有try catch,也會被外部異常捕獲
function *gen1()
{
var x = yield 10;
var y = x.toUpperCase();
yield y;
}
var obj3 = gen1();
obj3.next();
try
{
obj3.next(33);
}
catch(e)
{
console.log(e);
}
return()
也是結束狀态,return方法傳回值與其參數相同
function *gen()
{
yield 'a';
yield 'b';
yield 'c';
}
var obj = gen();
console.log(obj.next()); // a
console.log(obj.return('hello world')); // hello world
console.log(obj.next()); // undefined
如果有finally,return 會直接跳到finally的第一個yield語句,并傳回yield後面表達式的值。如果finally裡面沒有yield,則傳回 return的參數值
function* gen1()
{
yield 'a';
try
{
yield 'b';
yield 'c'; // 不會執行
}
finally
{
yield 'd';
yield 'e';
}
yield 'f'; // 不會執行
}
var obj1 = gen1();
console.log(obj1.next()); // 'a'
console.log(obj1.next()); // 'b'
console.log(obj1.return('hello')); // d
console.log(obj1.next()); // e,finally裡的yield必須都執行完
console.log(obj1.next()); // hello,參數值也要指行完
console.log(obj1.next()); // undefined,return已提前結束狀态
yield*與遞歸generator函數
遞歸函數:在函數内部調用函數自身
遞歸Generator函數:在Generator函數内部需要調用Generator函數自身
在Generator函數内部是否可以調用另外一個Generator函數?
在一個Generator函數中直接調用另外一個Generator函數是沒有任何作用的
function* gen1()
{
console.log('x');
yield 'a';
console.log('y');
yield 'b';
}
function* gen2()
{
yield 1;
yield* gen1();//相當于把代碼複制過來
//yield gen1();
yield 2;
}
var obj = gen2();
console.log(obj.next());
console.log(obj.next());
console.log(obj.next());
console.log(obj.next());
利用Generator遞歸函數枚舉嵌套數組中的所有值
const array = ['a', [1,2], ['b', [3, [4, 'c']],'d'],["abc","xyz"]];
function* enumerateArray(array)
{
if(Array.isArray(array))
{
for(let i = 0; i < array.length;i++)
{
yield* enumerateArray(array[i]);
}
}
else
{
yield array;
}
}
for(let x of enumerateArray(array))
{
console.log(x);
}
将Generator函數作為對象屬性
//方法一
var obj1 = {name:'Bill', *gen(){yield 10}};
console.log(obj1.gen().next());// 10 false
console.log(obj1.gen().next());// 10 false
//方法二
var obj2 = {name:'Mike', gen:function*(){yield 'hello world'}};
console.log(obj2.gen().next());
this
ES6規定這個周遊器是Generator函數的執行個體,這個執行個體也繼承了Generator函數的prototype對象上的方法
function* gen()
{
}
gen.prototype.process = function()
{
return 'test process';
}
let obj = gen();
console.log(obj instanceof gen);
console.log(obj.process());
不能像普通函數的this一樣在Generator函數中使用this
function* gen1()
{
this.name = 'Bill'; // 沒有成功添加name屬性
}
let obj1 = gen1();
console.log(obj1.name);
可以采用變通的方式在Generator函數中為對象添加屬性
var genObj = {};
function* gen2()
{
yield this.name = 'Bill';
yield this.age = 30;
}
var g = gen2.bind(genObj)();
g.next();//調用三次才可
g.next();
g.next();
console.log(genObj.name);
console.log(genObj.age);
var genObj1 = {};
function* gen3()
{
this.name = 'Bill';
this.age = 30;
}
var g1 = gen3.bind(genObj1)();
g1.next();//調用一次即可
console.log(genObj1.name);
console.log(genObj1.age);
generator函數與狀态機
普通函數
var state = 1;
var stateMachine = function()
{
if(state == 1)
{
console.log('狀态1');
state++;
}
else if(state == 2)
{
console.log('狀态2');
state++;
}
else
{
console.log('狀态3');
state = 1;
}
}
stateMachine();
stateMachine();
stateMachine();
stateMachine();
stateMachine();
使用Generator函數切換狀态
var genStateMachine = function*()
{
while(true)
{
console.log('狀态1');
yield;
console.log('狀态2');
yield;
console.log('狀态3');
yield;
}
}
var gen = genStateMachine();
gen.next();
gen.next();
gen.next();
gen.next();
gen.next();
Promise
Promise是一種異步實作模式,允許Promise中的任務在另一個線程中執行
Promise是一個構造函數,構造函數的參數可以傳遞一個回調函數,回調函數就是在另一個線程中執行的任務
成功或失敗兩種結果success和fail,成功Promise.then
以前就有,es6進行了api标準化
function timeout(ms)
{
return new Promise((success,fail)=>{ //參數名随便起,回調函數
setTimeout(success,ms);
})
}
timeout(2000).then(()=>{
console.log('success');
})
function timeout1(ms)
{
return new Promise((success,fail)=>{
if(ms % 2 == 0)
{
setTimeout(success,ms);
}
else
{
setTimeout(fail, ms);
}
})
}
timeout1(2000).then(()=>{
console.log('success(2000)');
},()=>{
console.log('fail(2000)');
});
timeout1(1999).then(()=>{
console.log('success(1999)');
},()=>
{
console.log('fail(1999)');
});
異步下載下傳HTML代碼
var getHtml = function(url)
{
var promise = new Promise((success,fail)=>{
var client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = 'text';
client.setRequestHeader('Accept', "text/html");
client.send();
function handler()
{
if(this.readyState != 4)
{
return;
}
if(this.status == 200)
{
success(this.response);
}
else
{
fail(new Error(this.statusText));
}
}
})
return promise;
}
getHtml("basic.html").then((html)=>{
console.log(html);
},(error)=>
{
console.error(error);
})
then
then也可以有傳回值,是一個新的Promise對象,第二個then的參數是第一個then的傳回值
異步下載下傳HTML代碼
getHtml("basic.html").then((html)=>{
console.log(html);
return html.length;
}).then((length)=>{
console.log("html長度:" + length);
});
catch
異步下載下傳HTML代碼,統一捕獲
getHtml("basic.html").then((html)=>{
console.log(html);
return getHtml("then.html");
}).then((html)=>
{
console.log(html);//第二次調用
}).catch((errorMsg)=>
{
console.log(errorMsg);
})
異步操作與async函數
通過Generator函數與Promise對象封裝異步任務
Generator函數分段執行的,就是通過yield實作的可以在Generator函數中将大人物分解成多步,其中某些步驟 就可以交給Promise對象異步執行 這時Generator函數是暫停執行的,直到Promise對象執行完異步任務後,才會傳回Generator函數 核心問題:Generator函數與Promise對象之間的資料互動 主要的方式:Generator函數通過next方法傳回一個Promise對象,然後 Promise對象執行完異步任務後,将處理完的資料傳回給Generator函數繼續處理
demo:按txt方式下載下傳一個json文檔,然後通過Promise對象異步将該文檔轉換為json對象,再将json對象傳回給Generator函數,最後在Generator函數中輸出對象的所有屬性值
var getJSON = function(url)
{
var promise = new Promise((success,fail)=>{
var client = new XMLHttpRequest();
client.open("GET",url);
client.onreadystatechange = handler;
client.responseType = 'text';
client.send();
function handler()
{
if(this.readyState != 4)
{
return;
}
if(this.status == 200)
{
success(this.response);
}
else
{
fail(new Error(this.statusText));
}
}
})
return promise;
}
function* gen()
{
var url = "data.json";
var result = yield getJSON(url);//
console.log("name:" + result.name);
console.log("age:" + result.age);
console.log("salary:" + result.salary);
}
var g = gen();
// Generator函數 -> Promise對象
var obj = g.next();
// 通過Promise對象将json文本轉換為json對象
obj.value.then((json)=>{
var jsonObj = JSON.parse(json);
return jsonObj;
}).then((jsonObj)=>{
// 将解析完的JSON對象傳回gen函數(Promise對象 -> Generator函數傳遞資料的過程)
g.next(jsonObj)
})
async
function timeout(ms)
{
return new Promise((success,fail)=>{
if(ms % 2 == 0)
{
setTimeout(success, ms);
}
else
{
// 必須調用fail,async函數和catch方法才會捕獲異常
fail(new Error("毫秒必須是偶數"));
}
})
}
timeout(2000).then(()=>{
console.log('hello world 2000')
})
timeout(1999).catch((error)=>{
console.log(error);
})
使用async函數,要es7,es6不支援
async function asyncPrint(value, ms)
{
try
{
// await等待success方法被調用,調用後,立刻往下執行
await timeout(ms);
console.log(value);
}
catch(e)
{
console.log(e);
}
}
asyncPrint('hello world 3000', 3000);
asyncPrint('hello world 3000', 3001);
類
es5
function Product(name, price)
{
this.name = name;
this.price = price;
}
Product.prototype.toString = function()
{
return 'name:' + this.name + ', price:' + this.price;
}
var product = new Product('iPhone8 Plus', 7000);
product.printName = function()
{
console.log('商品名:' + this.name);
}
console.log(product.toString());
product.printName();
es6新方式
class NewProduct
{
constructor(name, price)
{
this.name = name;
this.price = price;
}
toString()
{
return 'name:' + this.name + ', price:' + this.price;
}
printName()
{
console.log('商品名:' + this.name);
}
}
var newProduct = new NewProduct('特斯拉電動車', 900000);
console.log(newProduct.toString());
newProduct.printName();
新的定義類的方式就是一個文法糖
console.log(typeof NewProduct); // function
枚舉類的屬性
console.log(Object.keys(NewProduct.prototype)); // [],在類中不在原型
console.log(Object.getOwnPropertyNames(NewProduct.prototype)); // ["constructor", "toString", "printName"];//方法在類原型屬性中
console.log(Object.keys(newProduct)); // ["name", "price"],屬性在執行個體對象本身
console.log(Object.getOwnPropertyNames(newProduct)); // ["name", "price"]
console.log(Object.keys(Product.prototype)); // ["toString"],es5傳統方法
console.log(Object.keys(product)); // ["name", "price", "printName"],es5傳統方法
構造方法(constructor)
類必須要有構造方法,如果未指定構造方法,系統會自動加一個沒有參數的構造方法
因為使用new指令建立對象時需要調用類中的構造方法
constructor預設會傳回當期類的執行個體對象,但也可以通過return傳回其他的對象
class MyClass
{
constructor()
{
return new NewProduct('會員卡', 200);
}
}
var p = new MyClass();
console.log(p.toString());
p.printName();
console.log(typeof p);
console.log(p instanceof NewProduct);//p屬于類NewProduct的執行個體
// name屬性
class MyClass1{
}
var my = new MyClass1();
console.log(MyClass.name);
// console.log(my.name); // undefined
class表達式定義類
函數表達式定義函數
var myFun = function()
{
console.log("myFun");
}
myFun();
var myFun1 = function hello()//hello基本無用處
{
console.log(hello.name)
console.log(typeof hello);
console.log(typeof myFun1);
console.log(myFun1.name);
}
myFun1();
console.log(myFun1.name);
// hello(); error,隻在myFun1函數内部可以使用
類表達式
//不加Me,該class的name為MyClass
var MyClass = class Me
{
getClassName()
{
console.log(Object.getOwnPropertyNames(MyClass));
console.log(Object.getOwnPropertyNames(Me));
return Me.name;
}
}
var c = new MyClass();
console.log(c.getClassName()); //Me
console.log(MyClass.name); //Me
var Person = class
{
constructor(name)
{
this.name = name;
}
printName()
{
console.log('姓名:' + this.name);
}
}
new Person('Bill').printName();
繼承
class Person
{
constructor(name, age)
{
this.name = name;
this.age = age;
}
toString()
{
return 'name:' + this.name + ' age:' + this.age;
}
}
class Teacher extends Person
{
constructor(name, age, course)
{
super(name, age);
this.course = course;
}
toString()
{
return super.toString() + ' course:' + this.course;
}
}
var teacher = new Teacher('Bill', 30,'大資料');
console.log(teacher.toString());
- 在子類的構造方法中必須調用super,否則建立類執行個體時會抛出異常
- 因為子類中沒有this,是以要通過執行super方法來獲得this對象
class Teacher1 extends Person
{
constructor(name, age, course)
{
super(name, age);//必須
//this.course = course;
}
toString()
{
return super.toString() + ' course:' + this.course;
}
}
var teacher1 = new Teacher1('Bill', 30,'大資料');
- super的位置可以在構造函數随便放(Java必須在最前面),隻有調用super後,才能使用this
class Teacher2 extends Person
{
constructor(name, age, course)
{
console.log('constructor');
super(name, age);
this.course = course;
}
toString()
{
return super.toString() + ' course:' + this.course;
}
}
var teacher2 = new Teacher2('Bill', 30,'大資料');
原生構造函數的繼承
Boolean()、Number()、String()、Array()、Date()、Function()、RegExp()、Error()、Object()
ES5 不能繼承原生構造函數
ES6可以繼承原生構造函數
class NewArray extends Array{
constructor(...args)
{
super(...args);
}
}
var arr = new NewArray(1,2);
arr[2] = 'Bill';
arr[3] = 'Mike';
console.log(arr.length); // 4
arr.length = 0;
console.log(arr[0]); // undefined
過sum方法計算數組中所有數值類型元素的和
class SumArray extends Array
{
constructor(...args)
{
super(...args);
}
sum()
{
var n = 0;
for(let i = 0; i < this.length;i++)
{
if(typeof this[i] == 'number')
{
n += this[i];
}
}
return n;
}
}
var sumArray = new SumArray(1,2,'abc',true,3,'hello', 4);
console.log(sumArray.sum());
類的getter與setter方法
getter和setter方法就是可以監控對象屬性讀寫的方法
class Person
{
constructor()
{
this._name = '';
}
get name()
{
console.log('get name()');
return '<' + this._name + '>';
}
set name(value)
{
console.log('set name(value)');
this._name = value;
}
}
var person = new Person();
person.name = 'Bill';
console.log(person.name);
Generator方法
利用Generator方法可以周遊對象中的資料
class MyClass
{
constructor(...args)
{
this.args = args;
}
// 給類添加一個預設的周遊器
*[Symbol.iterator]()
{
for(let arg of this.args)
{
yield arg;
}
}
// 普通方法
*gen()
{
for(let arg of this.args)
{
yield arg;
}
}
}
var my = new MyClass(1,2,3,4,5);
var obj = my.gen();
console.log(obj.next());//1
console.log(obj.next());//2
console.log(my[Symbol.iterator]().next());//1
// x == arg
for(let x of new MyClass('a','b','c'))
{
console.log(x);
}
類的靜态方法和靜态屬性
成員方法:必須通過類的執行個體調用
靜态方法:必須通過類本身調用
不能通過類執行個體調用,這一點和Java不同
成員方法會被繼承,而靜态方法不會被繼承
class MyClass
{
// 會被繼承
process()
{
console.log('process');
}
static delete()
{
console.log('delete');
}
}
new MyClass().process();//類執行個體
// new MyClass().delete(); 類執行個體,抛出異常
MyClass.delete();//類本身
console.log('-----------------');
靜态方法可以從super對象調用
class SubClass extends MyClass
{
static method()
{
console.log('method');
super.delete();
}
}
SubClass.method();
靜态屬性,并沒有直接支援方法,但可以處理一下
class NewClass
{
}
NewClass.name = 'Bill';
NewClass.name1 = 'Mike';
NewClass.age = 30;
console.log(NewClass.name); // NewClass,而不是Bill,name屬性本身有,無法人工改變
console.log(NewClass.name1);
console.log(NewClass.age);
new.target屬性
使用new建立的執行個體,new才有target屬性,否則target為undefined
// 寫法1
function Person1(name)
{
// 通過new指令建立對象
if(new.target != undefined)
{
this.name = name;
}
else
{
throw new Error('必須使用new執行個體化類');
}
}
// 寫法2
function Person2(name)
{
if(new.target === Person2)
{
this.name = name;
}
else
{
throw new Error('必須使用new執行個體化類');
}
}
var person1 = new Person1('李甯');
// var person11 = Person1.call(person1, 'Bill');
new.target在類中傳回目前的Class
class MyClass
{
constructor(name)
{
// console.log(new.target);
// console.log(new.target === MyClass);
this.name = name;
}
}
new MyClass('John');
子類繼承父類,new.target傳回的是子類
class SubClass extends MyClass
{
constructor(name)
{
super(name);
console.log(new.target);
console.log(new.target === MyClass); // false
console.log(new.target === SubClass); // true
}
}
new SubClass('李甯');
抽象類
可以利用new.target的特性模拟實作抽象類
- 抽象類不能直接執行個體化,必須通過子類繼承才能執行個體化
- 抽象類的抽象方法不能直接調用,必須在子類中覆寫後(override)才能調用
class AbstractPerson
{
constructor()
{
if(new.target === AbstractPerson)
{
throw new Error('抽象類不能執行個體化');
}
}
// 抽象方法
print()
{
// 内部抛出異常
throw new Error('抽象方法不能直接調用.')
}
// 普通方法
method()
{
console.log('method:普通方法');
}
}
class Teacher extends AbstractPerson
{
constructor()
{
super();
}
// override父類的抽象方法
print()
{
console.log('Teacher.method');
}
}
class Engineer extends AbstractPerson
{
constructor()
{
super();
}
}
new Teacher().print();
// new Engineer().print(); // error,調用了抽象類的print方法
// new AbstractPerson();
new Teacher().method(); // 調用普通方法