ES6快速浏览

news/2024/7/10 23:59:57 标签: ES6, JS

ES6_0">ES6快速浏览

let和const关键字

let

  • 局部变量
  • 作用于块级作用域{},而var是函数级作用域
  • 必须先定义后才可使用,而var定义的变量会先提升成undefined
  • 不可重复声明变量,var

const

  • 常量,不可修改
  • 块级作用域
  • 对象常量,只不可修改对象整体,对象内部变量可修改,因此需要冻结对象或深度冻结

全局变量

  • var awindow.a
  • letconst不可用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(非数值,不是数字)两个值

传统方法isFiniteisNaN

新方法与传统的全局方法的区别:

  • 传统方法先调用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的特性模拟实现抽象类

  1. 抽象类不能直接实例化,必须通过子类继承才能实例化
  2. 抽象类的抽象方法不能直接调用,必须在子类中覆盖后(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(); // 调用普通方法

http://www.niftyadmin.cn/n/1503492.html

相关文章

中山大学校队选拔赛第一章题4【简单数迷Simple Kakuro】-------2015年1月28日

一&#xff1a;题意描述 本题就是给定一个迷宫&#xff0c;其中第一行和第一列都给定了数值。现在我们的任务就是需要把剩余的空格用1-9的数字把它填满&#xff0c;并且每行每列数值之和需要和行列标定的值相等。问最后是否可行&#xff0c;如果有多种方案需要输出一种方案。 二…

【mysql】采坑合集

bugOperation not allowed when innodb_forced_recovery &#xff1e; 0Operation not allowed when innodb_forced_recovery &#xff1e; 0 MySQL在转储文件或者导入数据的过程中&#xff0c;出现中断、失败或者异常&#xff0c;造成数据无法回滚&#xff0c;可以通过innodb_…

微软职位内部推荐-Sr SDE-MOD-Beijing

微软近期Open的职位:JOB TITLE: Senior Software Design EngineerDEPARTMENT: Microsoft Office Division ChinaIMMEDIATE SUPERVISOR: Development Lead/ManagerLocation: Beijing, China Is “Cloud-y” in your career forecast? Dont get me wrong, I meant Cloud computi…

用edtftpj实现Java FTP客户端工具

edtftpj是一个java FTP工具包&#xff0c;使用非常方便&#xff0c;感觉比Apache的好用&#xff0c;但Apache更灵活。edtftpj有多种版本&#xff0c;分别是java、.net和js版本。对于Java版的有一个免费版本。我用的就是免费版本。其余的均为商业版本。 为了开发&#xff0c;先下…

[SAP ABAP开发技术总结]IDoc

18.4. IDoc. 206 18.4.1. 数据段类型和数据段定义&#xff08;WE31&#xff09;... 206 18.4.2. IDoc定义&#xff08;WE30&#xff09;... 207 18.4.3. 自定义IDoc发送与接收实例... 208 18.4.3.1. 发送端800&#xff08;outbound&#xff09;配…

hdu 2561

第二小整数 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2023 Accepted Submission(s): 1266 Problem Description求n个整数中倒数第二小的数。每一个整数都独立看成一个数&#xff0c;比如&#xff0c;有三个数分…

文件系统取证分析(第12章:NTFS分析)

/* Skogkatt 开始翻译于2015-02-01&#xff0c;仅作为学习研究之用&#xff0c;谢绝转载。 2015-09-26 更新第六节全景 译注&#xff1a;我翻译这本书的这三章虽然蓄谋已久&#xff0c;但并不是一个计划好的工作。因为之前和vczh、mili、darkfall曾讨论过everything这个软件&am…

kdchxue讲解V9父栏目调用子栏目的办法

我们在做模板时有时候需要用到调用栏目的子栏目&#xff0c;下面这个文章将教大家实现目的&#xff0c;挺简单的。代码如下&#xff1a; 在二级栏目列表页调用&#xff1a; <!-- * 获取子栏目 * param $parentid 父级id * param $type 栏目类型 * param $self 是否包含本身 …