[es6] 积累
杂
将异步控制为顺序执行
使用promise的写法一
function Say(name) {
return new Promise(function (resolve, reject) {
switch (name) {
case 'a':
setTimeout(()=> {
console.log(name);
resolve();
}, 1000);
break;
case 'b':
setTimeout(()=> {
console.log(name);
resolve();
}, 2000);
break;
case 'c':
setTimeout(()=> {
console.log(name);
resolve();
}, 3000);
break;
case 'd':
console.log(name);
resolve();
break;
}
});
}
Say('a')
.then(()=> { return Say('b') })
.then(()=> { return Say('c') })
.then(()=> { return Say('d') })
使用promise的写法二
function Say() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('a');
resolve();
}, 1000);
});
}
Say().then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log('b'));
}, 2000);
});
}).then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log('c'));
}, 3000);
});
}).then(() => {
console.log('d');
})
使用async的写法
function A() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('a');
resolve();
}, 1000);
})
}
function B() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('b');
resolve();
}, 2000);
})
}
function C() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('c');
resolve();
}, 3000);
})
}
async function Say() {
await A();
await B();
await C();
console.log('d');
};
Say();
如果不需要顺序执行,但需要保证所有异步都执行完毕,可以使用Promise.all 。
但是需要注意这里的Promise.all只是检测是否全部执行成功,以及检测是否抛错,并不能阻止运行,但是能让后续不再resolve。
let A = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行了A')
resolve('a');
}, 1000);
})
}
let B = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行了B')
//reject('boom')
resolve('b');
}, 2000);
})
}
let C = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行了C')
resolve('c');
}, 3000);
})
}
console.time();
Promise.all([A(), B(), C()]).then((value) => {
console.log(value);// ["a", "b", "c"]
console.timeEnd();//约3s
}, (err) => {
console.error(err)//boom
console.timeEnd();//约2s
});
私有化方法
-
symbol 是用来处理属性名的冲突。但是一个 symbol 属性作为键名时是不能被 Object.keys() 、 Object.getOwnPropertyNames() 获取,所以就造成了一种非私有的内部方法效果。symbol 是不能达到闭包那么好的效果的,因为通过 Symbol.for() 是可以获取同一个 Symbol 值的,获得以后一样是能够访问值。
-
在 class 中实现私有方法的办法是将方法声明在类外,然后在类中使用 .call() 进行引用。虽然通过这个类对象是不能访问这个方法,但是这个方法其实还是暴露在其他更大的作用域当中的,或者就是使用 symbol 作为方法名。
-
常规的私有化方法是使用闭包,只对外开发非私有化的部分。
var Student = (function () {
var m_staticMaxAge = 120;//定义一个私有的静态属性
function ageIsValid(age) { //定义一个私有的静态方法
if (age > m_staticMaxAge) {
throw Error("the property age must be less than " + m_staticMaxAge);
}
}
//返回构造函数,设置一些属性
return function (name, age) {
var m_name, m_age;//把私有属性都放到这里定义
//私有属性的 setter 和 getter 都放到这里
this.setName = function (name) {
Student.nameIsValid(name);
m_name = name;
};
this.setAge = function (age) {
ageIsValid(age);
m_age = age;
};
this.getName = function () {
return m_name;
};
this.getAge = function () {
return m_age;
};
this.setName(name);
this.setAge(age);
};
})();
//定义公有的方法
Student.prototype = {
show: function () {
console.log("showcall name:" + this.getName() + " age:" + this.getAge());
}
};
//定义静态的公有方法
Student.nameIsValid = function (name) {
if (typeof name != 'string') {
throw Error("property name must me a string value");
}
};
//正常调用
var stu = new Student('皮卡丘', 23);
console.log("name:" + stu.getName() + " age:" + stu.getAge());//name:皮卡丘 age:23
stu.show();//showcall name:皮卡丘 age:23
//报name异常的调用
//var stu2 = new Student(1212, 23);//Uncaught Error: property name must me a string value
//console.log("name:" + stu2.getName() + " age:" + stu2.getAge());
//stu.show();
//报age异常的调用
//var stu3 = new Student("皮卡丘", 121);//Uncaught Error: the property age must be less than 120
//console.log("name:" + stu3.getName() + " age:" + stu3.getAge());
//stu.show();
代替匿名函数的简便写法
{
let a = 123;
console.log(a)//123
}
//console.log(a)//a is not defined
//等价
(function () {
let a = 321;
console.log(a)//321
})()
//console.log(a)//a is not defined
Set
Set可存储不重复值,但是其功能也可被对象代替(hash)。
可用于数组去重,如:
let test = [4, 5, 4, 1, 2, 3, 2];
let uniqueArray = (arr) => [...new Set(arr)];
let s = new Set(test);
console.log(s);//Set(5) {4, 5, 1, 2, 3}
console.log(...s);//4 5 1 2 3
console.log(uniqueArray(test));//[4, 5, 1, 2, 3]
Map
普通对象不可以将dom存储为键值,但是Map可以。
类似 Object.create(null)
,没有原型(prototype)。
for of
可以遍历数组,字符串,Set,Map (总之很吊就是了)
Object.getOwnPropertyNames()
Object.keys()
也只能遍历对象属性名,暂时还没有可以直接遍历对象属性值的方法
for of 并不能遍历对象
{
//let oo = {x: 'a', y: 'b', z: 'c'};
//let oo = {1: 'a', 2: 'b', 3: 'c'};
let oo = ['a', 'b', 'c'];
for (let v of oo) {
console.log(v);
}
}
{
let oo = {x: 'a', y: 'b', z: 'c'};
for (let i in oo) {
console.log(i);
//console.log(oo.i);//undefined
console.log(oo[i]);
}
}
const
- const 定义的 Array 中间元素能被修改,const 定义的变量只是持有 Array 的地址,这个变量只是本身不能修改,而对于存在于堆内存中的 Array 本身是可以修改的。
- 对于 const 声明,只是它的赋值操作被冻结了,而值不会因为 const 而不变。主要是预防在coding过程中的coder因为疏忽对变量的意外修改。
describe('complex types are NOT fully read-only', () => {
it('array', () => {
const arr = [40, 23];
arr[0] = 42;
assert.equal(arr[0], 42);
});
it('object', () => {
const obj = {x: 1};
obj.x = 3;
assert.equal(obj.x, 3);
});
});
let
在函数里对同名参数进行赋值会报错
let a = 1;
(function xx(xx) {
let a = 2;
let xx = 'xx';//error
console.log(a)//2
console.log(xx)
})()
块作用域 {}
es6的块级作用域只是针对let 和const声明的变量或函数。
{
foo(2, 3);
function foo(a, b) {
console.log(a, b);
}
}
foo(1, 2);
// 2 3
// 1 2
//函数作用域提升了
destructuring
describe('destructuring arrays makes shorter code', () => {
it('extract value from array, e.g. extract 0 into x like so `let [x] = [0];`', () => {
let [firstValue] = [1];
assert.strictEqual(firstValue, 1);
});
it('swap two variables, in one operation', () => {
let [x, y] = ['ax', 'why'];
[x, y] = [y, x];
assert.deepEqual([x, y], ['why', 'ax']);
});
it('leading commas', () => {
const all = ['ax', 'why', 'zet'];
const [,,z] = all;
assert.equal(z, 'zet');
});
it('extract from nested arrays', () => {
const user = [...['Some', 'One'], 23];
const [firstName, surname, age] = user;
const expected = 'Some One = 23 years';
assert.equal(`${firstName} ${surname} = ${age} years`, expected);
});
it('chained assignments', () => {
let c, d;
let [a, b] = [c, d] = [1, 2];
assert.deepEqual([a, b, c, d], [1, 2, 1, 2]);
});
it('in for-of loop', () => {
for (let [,a, b] of [[0, 1, 2]]) {
assert.deepEqual([a, b], [1, 2]);
}
});
});
describe('destructuring objects', () => {
it('is simple', () => {
const {x:x} = {x: 1};
assert.equal(x, 1);
});
describe('nested', () => {
it('multiple objects', () => {
const magic = {first: 23, second: 42};
const {magic: second} = {magic:magic['second']};
assert.equal(second, 42);
});
it('object and array', () => {
const {z:[,x]} = {z: [23, 42]};
assert.equal(x, 42);
});
it('array and object', () => {
const [,[{lang}]] = [null, [{env: 'browser', lang: 'ES6'}]];
assert.equal(lang, 'ES6');
});
});
describe('interesting', () => {
it('missing refs become undefined', () => {
const {z} = {x: 1, y: 2};
assert.equal(z, void 0);
});
it('destructure from builtins (string)', () => {
const {substr} = '1';
assert.equal(substr, String.prototype.substr);
});
});
});
describe('destructuring also works on strings', () => {
it('destructure every character', () => {
let [a, b, c] = 'abc';
assert.deepEqual([a, b, c], ['a', 'b', 'c']);
});
it('missing characters are undefined', () => {
const [a, ,c] = 'ab';
assert.equal(c, void 0);
});
it('unicode character work too', () => {
const [,space, coffee] = 'a ☕';
assert.equal(coffee, '\u{2615}');
});
});
object literal
describe('The object literal allows for new shorthands', () => {
const x = 1;
const y = 2;
describe('with variables', () => {
it('the short version for `{x: x}` is {x}', () => {
const short = {y};
assert.deepEqual(short, {y: y});
});
it('works with multiple variables too', () => {
const short = {x, y};
assert.deepEqual(short, {x: x, y: y});
});
});
describe('with methods', () => {
const func = () => func;
it('using the name only uses it as key', () => {
const short = {func};
assert.deepEqual(short, {func: func});
});
it('a different key must be given explicitly, just like before ES6', () => {
const short = {otherKey:func};
assert.deepEqual(short, {otherKey: func});
});
it('inline functions, can written as `obj={func(){}}` instead of `obj={func:function(){}}`', () => {
const short = {
inlineFunc(){return 'I am inline'}
};
assert.deepEqual(short.inlineFunc(), 'I am inline');
});
});
});
const short = {
Func(){return 'I am inline'},
Func1(){return 'I am inline1'}
};
console.log(short.Func())//'I am inline'
console.log(short.Func1())//'I am inline1'
let x = 1;
let y = 2;
let z = 3
let short = {x, y, zz: z};
console.log(short)//{x: 1, y: 2, zz: 3}
template
function
it('inside "${...}" can also be a function call', function() {
function getDomain(){
return document.domain;
}
var evaluated = `${ getDomain() }`;
assert.equal(evaluated, 'xx.com');
});
字符串与变量
var one = 1;
var two = 2;
var three = 3;
function firstValueOnly(strings, firstValue, secondValue) {
//strings是个数组,接收全部字符串
console.info(strings);//
//第二个参数开始,每个参数只代表一个变量
console.warn(firstValue);//1
console.warn(secondValue);//2
return firstValue;
}
firstValueOnly`uno${one}dos${two}`;
function valuesOnly(stringsArray, ...allValues) { // using the new ES6 rest syntax
console.log(allValues);//[ 1, 2, 3 ]
return allValues;
}
valuesOnly`uno=${one}, dos=${two}, tres=${three}`;
raw
it('the `raw` property accesses the string as it was entered', function() {
function firstChar(strings) {
return strings.raw;
}
assert.equal(firstChar`\n`, '\\n');
});
describe('`String.raw` as a static function', function(){
it('concats the raw strings', function() {
var expected = '\\n';
assert.equal(String.raw`\n`, expected);
});
it('two raw-templates-string-backslashes equal two escaped backslashes', function() {
const TWO_BACKSLASHES = '\\\\';
assert.equal(String.raw`\\`, TWO_BACKSLASHES);
});
it('works on unicodes too', function() {
var smilie = '\\u{1F600}';
var actual = String.raw`\u{1F600}`;
assert.equal(actual, smilie);
});
});
arrow function 箭头函数
- 不可以当做构造函数,也就是说,不可以使用 new 命令,否则会抛出错误。
- 不可以使用 arguments 对象,该对象在函数体内不存在。
- 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
不能用于定义字面量方法
calculator.sum 使用箭头函数来定义,但是调用的时候会抛出 TypeError,因为运行时 this.array 是未定义的,调用 calculator.sum 的时候,执行上下文里面的 this 仍然指向的是 window,原因是箭头函数把函数上下文绑定到了 window 上,this.array 等价于 window.array,显然后者是未定义的。
const calculator = {
array: [1, 2, 3],
sum: () => {
console.log(this === window); // => true
return this.array.reduce((result, item) => result + item);
}
};
console.log(this === window); // => true
// Throws "TypeError: Cannot read property 'reduce' of undefined"
calculator.sum();
解决方案
const calculator = {
array: [1, 2, 3],
sum: function () {
console.log(this === calculator); // => true
return this.array.reduce((result, item) => result + item);
}
};
console.log(calculator.sum()); // => 6
//方法简写
const calculator = {
array: [1, 2, 3],
sum() {
console.log(this === calculator); // => true
return this.array.reduce((result, item) => result + item);
}
};
console.log(calculator.sum()); // => 6
不能用于定义原型方法
同样的规则适用于原型方法(prototype method)的定义,使用箭头函数会导致运行时的执行上下文错误
function Cat(name) {
this.name = name;
}
Cat.prototype.sayCatName = () => {
console.log(this === window); // => true
return this.name;
};
const cat = new Cat('Mew');
cat.sayCatName(); // => undefined
不能用于定义事件回调函数
箭头函数在声明的时候就绑定了执行上下文,要动态改变上下文是不可能的,在需要动态上下文的时候它的弊端就凸显出来。比如在客户端编程中常见的 DOM 事件回调函数(event listenner)绑定,触发回调函数时 this 指向当前发生事件的 DOM 节点,而动态上下文这个时候就非常有用。
在全局上下文下定义的箭头函数执行时 this 会指向 window,当单击事件发生时,浏览器会尝试用 button 作为上下文来执行事件回调函数,但是箭头函数预定义的上下文是不能被修改的,这样 this.innerHTML 就等价于 window.innerHTML,而后者是没有任何意义的。
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
不能用于定义构造函数
构造函数中的 this 指向新创建的对象,当执行 new Car() 的时候,构造函数 Car 的上下文就是新创建的对象,也就是说 this instanceof Car === true。显然,箭头函数是不能用来做构造函数,会抛出异常。
const Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
const helloMessage = new Message('Hello World!');
=>
describe('arrow functions', function() {
it('are shorter to write', function() {
var func = () => {
return 'I am func'
};
assert.equal(func(), 'I am func');
});
it('a single expression, without curly braces returns too', function() {
var func = () => 'I return too';
assert.equal(func(), 'I return too');
});
it('one parameter can be written without parens', () => {
var func = param => param - 1;
assert.equal(func(25), 24);
});
it('many params require parens', () => {
var func = (param,param1) => param + param1;
assert.equal(func(23, 42), 23+42);
});
it('body needs parens to return an object', () => {
var func = () => ({iAm: 'an object'});
assert.deepEqual(func(), {iAm: 'an object'});
});
});
this
it('bound at definition time, use `=>` ', function() {
var bound = new LexicallyBound();
var fn = bound.getFunction();
assert.strictEqual(fn(), bound);
});
it('`arguments` doesnt work inside arrow functions', function() {
var bound = new LexicallyBound();
var fn = bound.getArgumentsFunction();
assert.equal(fn(1, 2).length, 0);
});
async
try...catch 两种方式
async function Say() {
try {
await A();
await B();
await C();
console.log('d');
} catch (e) {
console.info(e)
}
};
Say();
async function Say() {
await A();
await B();
await C();
console.log('d');
};
Say().catch((err) => {
console.log(err)
});
export
export { default as Affix } from './affix';
export * from xx
default会出现在{}里.
export * 可以全部导出{},包括default.
()=>() //error
()=>({})// return {}
()=>{} //return undefined
let b = {a:'aaa'};
let {a='xx'}=b; //b必须是对象否则报错
a//'aaa' 如果b没有a值,则a会设为默认值'xx'