@GivenCui
2017-05-24T22:01:08.000000Z
字数 33685
阅读 835
ES6
参考
ECMAScript6入门--阮一峰
Babel在线编译器
vi/vim package.json // 查看package.json
ESC // 恢复到正常模式
: // 进入命令模式
:w // 保存 (write to disk)
:q // 不保存退出
:wq / ZZ / :x // 保存后退出
:q! / ZQ // 强制退出(不保存)
不算模板引擎, 只是方法
Eng v4.17
中 v4.16
需要运行node上 (算后端模板?)
ES6对象拷贝
Object.assign({t:1},{k:2})
ES5对象拷贝
//TODO
ES6
function hello(txt="hello world") {
console.log(txt);
}
ES5
function hello(txt) {
var txt = txt || 'hello world';
console.log(txt);
}
ES6
var name = 'moe';
var txt = `hello ${name}`; // 此处为` `
ES5
// 引入 LoDash/undersore.js
var compiled = _.template("hello: <%= name %>");
compiled({name: 'moe'});
1.很多第三方库源码是ES6
vue.js
Element (饿了么的UI)
D3 (图表业务和图表动画)
2.Vue,React,Angular都用到了ES6
构建工具 > 基础语法 > 实战演练 (express和mockjs)
- 是彩票业务
- 属于电商网站中较复杂的部分
1. 安装node
2. 配置npm的全局模块的存放路径以及cache路径
npm config set prefix "...\nodejs\node_global"
npm config set cache "...\nodejs\node_cache"
3. 安装Express 和 Express应用生成器
npm install express -g // express -V查看报错
npm install express-generator -g // express -V查看成功
4. express -h // 查看支持的命令
5. express demo1 -e . // 当前目录下创建demo1应用, 以ejs为模板
6. win系统下树形结构显示文件目录结构
tree // 查看
tree /f > ***.txt // 输出当前目录结构到***.txt中
args.js (命令行参数解析)
/* yargs包, 为了区分命令行中的option */
// option('production') => gulp --production
import yargs from 'yargs';
const args = yargs
.option('production',{
boolean : true,
default : false,
describe : 'min all scripts'
})
.option('watch',{
boolean : true,
default : false,
describe : 'watch all filse'
})
.option('verbose',{
boolean : true,
default : false,
describe : 'log'
})
.option('sourcemaps',{
describe : 'force the creation of sourcemaps'
})
.option('port',{
string : true,
default : 8080,
describe : 'server port'
})
.argv
export default args; // 模块暴露出去!!!!!
scripts.js
import gulp from 'gulp';
import gulpif from 'gulp-if'; // gulp语句中作if判断
import concat from 'gulp-concat'; // gulp字符串拼接
import webpack from 'webpack'; // webpack
import gulpWebpack from 'webpack-stream'; // 让gulp支持webpack(流操作)
import named from 'vinyl-named'; // 文件别名,标志
import livereload from 'gulp-livereload'; // 热更新
import plumber from 'gulp-plumber'; // 处理文件信息流 (水管工)
import rename from 'gulp-rename'; // 文件重命名
import uglify from 'gulp-uglify'; // js,css压缩
import {log,colors} from 'gulp-util'; // 命令行输出加强
import args from './util/args'; // 命令行参数解析 (自定义)
gulp.task('scripts',()=>{
return gulp.src(['app/js/index.js'])
.pipe(plumber({
errorHandler : function () {
}
}))
.pipe(named()) // ? vinyl-named
.pipe(gulpWebpack({
module : {
loaders : [{
test:/\.js$/,
loader:'babel'
}]
}
}), null, (err, stats) => {
log(`Finished '${colors.cyan('scripts')}'` ,stats.toString({
chunks:false
}))
})
.pipe(gulp.dest('server/public/js')) // 输出路径
.pipe(rename({
basename:'cp', // copy
extname:'.min.js'
}))
.pipe(uglify({
compress : {
properties : false
},
output : {
'quote_keys' : true
}
}))
.pipe(gulp.dest('server/public/js'))
.pipe(gulpif(args.watch,livereload())) // 判断如果命令有watch则livereload热更新
})
// 注意 yargs
npm install gulp gulp-if gulp-concat webpack webpack-stream vinyl-named gulp-livereload gulp-rename gulp-uglify gulp-util yargs --save-dev
// yarn
yarn add gulp gulp-if gulp-concat webpack webpack-stream vinyl-named gulp-livereload gulp-rename gulp-uglify gulp-util yargs --dev
/* 注意 */
yarn add package -dev/-D => npm install package --save-dev
yarn add package => npm install package --save
pages.js
import gulp from 'gulp';
import gulpif from 'gulp-if';
import livereload from 'gulp-livereload';
import args from './util/args';
gulp.task('pages',()=>{
return gulp.src('app/**/*.ejs')
.pipe(gulp.dest('server'))
.pipe(gulpif(args.watch,livereload()))
})
css.js
import gulp from 'gulp';
import gulpif from 'gulp-if';
import livereload from 'gulp-livereload';
import args from './util/args';
gulp.task('css',()=>{
return gulp.src('app/**/*.css')
.pipe(gulp.dest('server/public'))
.pipe(gulpif(args.watch,livereload()))
})
server.js
import gulp from 'gulp';
import gulpif from 'gulp-if';
import liveserver from 'gulp-live-server';
import args from './util/args';
gulp.task('server',(cb)=>{
if(!args.watch) return cb(); // args.watch 返回Boolean
var server = liveserver.new(['--harmony','server/bin/www']); // --harmpny 在当前命令行执行后面的脚本 www.js
server.start(); // 启动服务器
// 监听server目录下的js和ejs文件的变化让浏览器热更新(自动刷新)
gulp.watch(['server/public/**/*.js','server/views/**/*.ejs'],function(file){
server.notify.apply(server,[file]);
})
// 监听路由改变时重新启动服务器
gulp.watch(['server/routes/**/*.js', 'server/app.js'], function () {
server.start.bind(server)()
})
})
browser.js
import gulp from 'gulp';
import gulpif from 'gulp-if';
import livereload from 'gulp-livereload';
import gutil from 'gulp-util'; // 命令行输出加强
import args from './util/args';
gulp.task('browser',(cb)=>{
if(!args.watch) return cb();
gulp.watch('app/**/*.js',['scripts']); // 调用scripts任务, 在scripts.js
gulp.watch('app/**/*.ejs',['pages']);
gulp.watch('app/**/*.css',['css']);
})
clean.js
import gulp from 'gulp';
import del from 'del';
import args from './util/args';
gulp.task('clean',()=>{
return del(['server/public', 'server/views']) // 删除server/public和server/views目录中的内容
})
build.js
/* 按照特定的顺序串联所有的任务 */
import gulp from 'gulp';
import gulpSequence from 'gulp-sequence';
gulp.task('build',gulpSequence('clean', 'css', 'pages', 'scripts', ['browser','server'])) // 仔细理解
default.js
/* 为了偷懒 */
import gulp from 'gulp';
gulp.task('default',['build']);
gulpfile.babel.js
/* 所有的任务都导入到gulpfile.js中 */
import requireDir from 'require-dir';
requireDir('./tasks');
.babelrc
{
"presets" : ["es2015"]
}
// TODO:想要支持IE8还需要其他配置
package.json
{
"name": "es6",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.24.1",
"babel-loader": "^7.0.0",
"babel-preset-env": "^1.4.0",
"babel-preset-es2015": "^6.24.1",
"connect-livereload": "^0.6.0",
"del": "^2.2.2",
"gulp": "^3.9.1",
"gulp-concat": "^2.6.1",
"gulp-if": "^2.0.2",
"gulp-live-server": "^0.0.30",
"gulp-livereload": "^3.8.1",
"gulp-plumber": "^1.1.0",
"gulp-rename": "^1.2.2",
"gulp-sequence": "^0.4.6",
"gulp-uglify": "^2.1.2",
"gulp-util": "^3.0.8",
"require-dir": "^0.3.1",
"vinyl-named": "^1.1.0",
"webpack": "^2.5.1",
"webpack-stream": "^3.2.0",
"yargs": "^8.0.1"
}
}
gulp --watch // --watch对应args.watch 来自yargs包
修改server下的app.js
只有这样才能热更新
// line23后新添加代码
app.use(require('connect-livereload')()); // 后添加的中间件, 搜索热更新原理,用于接收热更新
const -> constant 常量;不变的
es6中很多功能都需要运行在"use strict";下
babel等默认添加严格模式语句
// ES5
全局作用域
函数作用域 function(){}
// ES6
在ES5基础上增加块作用域 {}
/* 1. let声明变量只在块作用域内有效 (for, if, {}等) */
for (let i = 0; i < 10; i++) {}
console.log(i); //ReferenceError: i is not defined
// for循环还有一个特别之处,就是循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域
for (let i = 0; i < 3; i++) { // 父作用域
let i = 'abc'; // 子作用域
console.log(i); // abc *3次
}
/* 2. let不能重复声明 */
{
let a =1;
let a =2;
}
{
let a=1;
var a=1;
}
/* 3. 不存在变量提升 */
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
/* 4. 暂时性死区 (temporal dead zone,简称 TDZ) */
// let在整个{}中都存在TDZ
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
// 报错
let x = x; // ReferenceError: x is not defined
用来定义常量, read-only(只读)
对于基本数据类型(值类型), 值不可变
对于引用类型, 值(指向堆内存的地址)不可变, 但对象的成员可以变
其他同let
// 自执行函数
(function(){
// do something
})()
// 块
{
// do something
}
// 数组结构赋值
{
let a,b,rest;
[a,b]=[1,2];
console.log(a,b);
}
// ...rest的应用
{
let a,b,rest;
[a,b,...rest]=[1,2,3,4,5,6];
console.log(a,b,rest);
}
// 默认值情况
{
let a,b,c,rest;
[a,b,c=3]=[1,2]; // 默认值可被覆盖, 若不设置c=undefined
console.log(a,b,c);
}
/* 对象结构赋值 */
{
let a,b;
({a,b}={a:1,b:2}); // 括号不能省略, 为了区分模式还是表达式
console.log(a,b);
}
{
let {a,b}={a:1,b:2}; // 方法二, 不用括号
console.log(a,b);
}
// 默认值
{
let {a=10,b=5}={a:3};
console.log(a,b);
}
{
let {a:a,b:b,c=100}={a:1,b:2}; // 实质,默认值
console.log(a,b,c);
}
{
let {a:x,b:y,c:z=1000}={a:1,b:2}; // 改变量名的情况,默认值
console.log(x,y,z);
// console.log(a,b,c); // a,b,c只是键而不是变量
}
/* 应用场景 */
// 变量互换值
{
let a=1;
let b=2;
[a,b]=[b,a];
console.log(a,b); // 2 1
}
// 对象返回数组
{
function f(){
return [1,2]
}
let a,b;
[a,b]=f();
console.log(a,b); // 1 2
}
// 空位
{
function f(){
return [1,2,3,4,5]
}
let a,b,c;
[a,,,b]=f();
console.log(a,b); // 1 4
}
// 其余,和空位
{
function f(){
return [1,2,3,4,5]
}
let a,b,c;
[a,,...b]=f();
console.log(a,b); // 1 [3,4,5]
}
// json数据
{
let metaData={
title:'abc',
test:[{
title:'test',
desc:'description'
}]
}
let {title:esTitle,test:[{title:cnTitle}]}=metaData;
console.log(esTitle,cnTitle); // abc test
}
/*
* 正则拓展
*/
/*
* 回顾: ES5中正则的用法
* 1. RegExp对象的方法 exec()和test()
* var reg = /pattern/ 或 new RegExp() reg.lastIndex 匹配位置
* reg.exec(string) 返回值为数组 [result],只有一个值, arr.index
*
reg.test(string) 返回值为true/false
*
*2. String对象的方法: search() match() repalce() split()字符串按规则拆分成数组
*
*/
/*
* ES6新增
*
* 属性: flags, sticky
*
* u修饰符 针对Unicode中大于\uffff的部分
* y修饰符 sticky粘连模式
* 后行断言 (ES5有先行断言)
*
*
*/
{
// #构造函数#
let regex = new RegExp('xyz', 'i'); //第一个参数是字符串,第二个是修饰符
let regex2 = new RegExp(/xyz/i); //第一个参数是正则表达式,不接受第二个参数,否则会报错
console.log(regex.test('xyz123'), regex2.test('xyz123')); // true true
console.log(regex.test('xyZ123'), regex2.test('xyZ123')); // true true
let regex3 = new RegExp(/abc/ig, 'i'); // ES6中改变
//原有正则对象的修饰符是ig,它会被第二个参数i覆盖
console.log(regex3.flags); // i new RegExp()中的第二个参数
}
// 字符串对象的4个使用正则表达式的方法: match(),replace(),search(),split()这四个方法全部调用RegExp的实例的方法。
{
let regex = new RegExp('xyz', 'ig');
console.log(regex.test('xyz0XYZ1xyz2'), regex.exec('xyz0XYZ1xyz2'));
}
// y修饰符
{
let s = 'bbbb_bbb_bb_b';
var a1 = /b+/g;
var a2 = /b+/y;
console.log(a1.exec(s), a2.exec(s)); // ["bbbb"],["bbbb"]
console.log(a1.exec(s), a2.exec(s)); // ["bbb"],null
console.log(a1.sticky, a2.sticky); //表示是否开启了粘连模式
}
// u修饰符
{
console.log('u修饰符',/^\uD83D/.test('\uD83D\uDC2A')); // true
console.log('u修饰符',/^\uD83D/u.test('\uD83D\uDC2A')); // false
// 大括号表示Unicode字符,只有加上u才能识别
console.log(/\u{61}/.test('a')); // false
console.log(/\u{61}/u.test('a')); // true
console.log(/\u{20BB7}/u.test('𠮷')); // true
// 点(.)字符不能识别码点大于0xFFFF的Unicode字符,必须加上u修饰符。
let s = '𠮷';
console.log('大于0xFFFF的Unicode字符',/^.$/.test(s)); // false
console.log('使用u字符',/^.$/u.test(s)); // true
// 使用u修饰符后,所有量词都会正确识别大于码点大于0xFFFF的Unicode字符。
console.log('量词',/a{2}/.test('aa')); // true
console.log('量词',/a{2}/u.test('aa')); // true
console.log('量词',/𠮷{2}/.test('𠮷𠮷')); // false
console.log('量词',/𠮷{2}/u.test('𠮷𠮷')); // true
}
{
// #正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是行终止符(line terminator character)除外
// U+000A 换行符(\n)
// U+000D 回车符(\r)
// U+2028 行分隔符(line separator)
// U+2029 段分隔符(paragraph separator)
// 只是一个提案目前还不支持
// let reg=/test.go/s;
// console.log(reg.test('test\ngo'));
// console.log(reg.test('test\ngo'));
console.log('s变通方法',/foo.bar/.test('foo\nbar'));
console.log('s变通方法',/foo[^]bar/.test('foo\nbar'));
}
/*
* 字符串的拓展
*
*Unicode > \uffff 需要{}
*/
// /u{****} 正确识别 > \uffff
{
console.log('a',`\u0061`); // a
console.log('s',`\u20BB7`); // /u20BB 和7
console.log('s',`\u{20BB7}`); // 𠮷
}
// stri.codePointAt()
{
let s='𠮷';
console.log('length',s.length); // 2 识别错误
console.log('0',s.charAt(0));
console.log('1',s.charAt(1));
console.log('at0',s.charCodeAt(0)); // 55362
console.log('at1',s.charCodeAt(1)); // 57271
let s1='𠮷a';
console.log('length',s1.length);
console.log('code0',s1.codePointAt(0)); // 134071
// "20bb7" 十六进制 parseInt("20bb7",16) 或 parseInt(0x20bb7)--> 134071
console.log('code0',s1.codePointAt(0).toString(16));
console.log('code1',s1.codePointAt(1)); // 𠮷的另一部分 (错误)
console.log('code2',s1.codePointAt(2)); // 97 -> a
}
// String.fromCodePoint()
{
console.log(String.fromCharCode("0x20bb7"));
console.log(String.fromCodePoint("0x20bb7")); // 正确
}
// for-of才能正确处理字符串的个数
{
let str='\u{20bb7}abc';
for(let i=0;i<str.length;i++){
console.log('es5',str[i]);
}
for(let code of str){
console.log('es6',code);
}
}
// str.includes()
// str.startsWith()
// str.endsWith()
{
let str="string";
console.log('includes',str.includes("c"));
console.log('start',str.startsWith('str'));
console.log('end',str.endsWith('ng'));
}
// str.repeat() 重复
{
let str="abc";
console.log(str.repeat(2));
}
// ` `和 ${} 字符串模板
{
let name="list";
let info="hello world";
let m=`i am ${name},${info}`;
console.log(m);
}
// str.padStart() 前补白 eg. 时间日期的补全 15:3:2 --> 15:03:02
// str.padEnd() 后补白
{
let [a,b] = ['1','8']
console.log(a.padStart(2,'0'));
console.log(b.padEnd(2,'0'));
}
// 标签模板 (tagged template)
// 标签模板其实不是模板,而是函数调用的一种特殊形式。
{
let user={
name:'list',
info:'hello world'
};
console.log(abc`i am ${user.name},${user.info}`);
function abc(s,v1,v2){ // s对应数组,其中s.raw指向数组本身 , v1,v2对应${}
console.log(s,v1,v2); // ['i am ', ',', ''], 'list', 'hello world'
return s+v1+v2
}
}
// String.raw`标签模板用法` '\n' --> '\\n'
{
console.log(String.raw`Hi\n${1+2}`);
console.log(`Hi\n${1+2}`);
}
/*
* 数值的拓展
* console.dir(Number) 可以查看对象里面的属性和方法
*/
// 规范了数字二进制和八进制表示法
// 大小写都可以
{
console.log('B',0B1111); // 0b1111 b -> binary
console.log(0O17); // 0o17 o -> octonary
}
// isFinite --> Number.isFinite
// parseInt --> Number.parseInt
// 'parseInt' in Number // true
{
console.log('15',Number.isFinite(15)); // true
console.log('NaN',Number.isFinite(NaN)); // false
console.log('1/0',Number.isFinite('true'/0)); // false
console.log('NaN',Number.isNaN(NaN)); // true
console.log('0',Number.isNaN(0)); // false
}
// 新增 Number.isInteger()判断整数
{
console.log('25',Number.isInteger(25)); // true
console.log('25.0',Number.isInteger(25.0)); // true
console.log('25.1',Number.isInteger(25.1)); // false
console.log('25.1',Number.isInteger('25')); // false
}
// (-2^53 ,2^53)期间里的为安全整数, 超出后计算不准
{
Math.pow(2, 53); // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1; // true 说明不准了
console.log(Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER); // 9007199254740991 -9007199254740991
console.log('10',Number.isSafeInteger(10)); // true
console.log('a',Number.isSafeInteger('a')); // false
}
// Math.trunc() 去除一个数的小数部分,返回整数部分
// 相当于 正数时, 相当于 Math.floor()
// 相当于 负数时, 相当于 Math.ceil()
{
console.log(4.1,Math.trunc(4.1)); // 4
console.log(4.9,Math.trunc(4.9)); // 4
console.log(-4.1,Math.trunc(-4.1)); // -4
console.log(4.1,Math.floor(4.1)); // 4
console.log(-4.1,Math.floor(-4.1)); // -5
// 原理
Math.trunc = Math.trunc || function(x) {
return x < 0 ? Math.ceil(x) : Math.floor(x);
};
}
// 判断正数, 负数, 还是0, 它会返回五种值。
// 参数为正数,返回+1;
// 参数为负数,返回-1;
// 参数为0,返回0;
// 参数为-0,返回-0;
// 其他值,返回NaN。
{
Math.sign(-5); // -1
Math.sign(5); // +1
Math.sign(0); // +0
Math.sign(-0); // -0
Math.sign(NaN); // NaN
Math.sign('foo'); // NaN
Math.sign(); // NaN
}
// 求立方根 cube root -> cbrt
{
console.log('-1',Math.cbrt(-1));
console.log('8',Math.cbrt(8));
}
// Array.of() 将一组值,转换为数组
// Array.of基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一
{
let arr = Array.of(3,4,7,9,11);
console.log('arr=',arr); // [3,4,7,9,11]
let empty=Array.of();
console.log('empty',empty); // []
}
// Array.from() 两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象
// 类数组 对象中有length属性的
// arrayLike = {
// '0': 'a',
// '1': 'b',
// '2': 'c',
// length: 3
// };
// dom集合
{
let p = document.querySelectorAll('p'); // 类数组
let pArr = Array.from(p); // 转为数组
pArr.forEach(function(item){ // 使用数组的forEach方法
// console.log(item.textContent); // 获取文本节点
console.log(item.innerHTML); // 获取文本节点
});
// Array.from(arg1,arg2) arg2类似map
console.log(Array.from([1,3,5],function(item){return item*2})); // 返回结果组成的数组
}
// 数组实例的fill() 填充数组
{
console.log('fill-7',[1,'a',undefined].fill(7)); // [7,7,7]
console.log('fill,pos',['a','b','c'].fill(7,1,3)); // [a,7,7]
}
// 数组实例keys()和values()和entries()
{
for(let index of ['1','c','ks'].keys()){
console.log('keys',index); // 0 1 2
}
// values未实现,需要 babel-polyfill
for(let value of ['1','c','ks'].values()){
console.log('values',value); // 1 2 ks
}
for(let [index,value] of ['1','c','ks'].entries()){
console.log('entries',index,value);
}
}
// [].copyWithin(target, start, end)
{
console.log([1,2,3,4,5].copyWithin(0,3,4)); // [4,2,3,4,5]
}
// [].find(cb), [].findIndex(cb) 返回符合回调函数中条件的项
{
console.log([1,2,3,4,5,6].find(function(item){return item>3}));
console.log([1,2,3,4,5,6].findIndex(function(item){return item>3}));
}
// [].includes() 类似indexOf(), 但能处理NaN
{
console.log('number',[1,2,NaN].includes(1));
console.log('number',[1,2,NaN].includes(NaN));
}
// 默认值
{
function test(x, y = 'world',z){ //
console.log('默认值',x,y);
}
test('hello'); // 默认值 hello world
test('hello','kill'); // 默认值 hello world
}
{ // 作用域问题
let x='test';
function test2(x,y=x){
console.log('作用域',x,y);
}
test2('kill'); // kill kill
test2(); // undefined undefined
}
// rest参数 (函数定义时)
{
function test3(...arg){ // rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中
for(let v of arg){
console.log('rest',v);
}
}
test3(1,2,3,4,'a'); // 正常传参
}
// 拓展运算符 (在函数调用时使用) 去掉数组的[]
{
console.log(...[1,2,4]);
console.log('a',...[1,2,4]);
}
// 箭头函数
{
let arrow = v => v*2;
let arrow2 = () => 5;
console.log('arrow',arrow(3)); // 6
console.log(arrow2()); // 5
}
// 尾调用 (尾调用是函数的最后一步操作) 提升性能的, 用来尾递归
{
function tail(x){
console.log('tail',x);
}
function fx(x){
return tail(x) // 尾调用
}
fx(123)
// 也是尾调用
function f(x) {
if (x > 0) {
return m(x)
}
return n(x);
}
}
// 对象
// 简洁表示法
{
// 属性
let o=1,k=2;
let es6={
o,
k
};
console.log(es6);
// 方法
let es6_method={
hello(){
console.log('hello');
}
};
console.log(es6_method.hello());
}
// 属性表达式
// [Symbol]
{
let a='b';
let es6_obj={
[a]:'c' // b:'c'
}
console.log(es6_obj); // {b:'c'}
}
// 新增API
// Object.is(a,b) 判断a,b是否相等
// Object.assign(o1,o2) 浅拷贝 o2->o1
// Object.entries() 配合iterator, 返回[key,value]
{
console.log('字符串',Object.is('abc','abc'),'abc'==='abc');
console.log('NaN',Object.is(NaN,NaN),NaN===NaN);
console.log('数组',Object.is([],[]),[]===[]);
console.log('拷贝',Object.assign({a:'a'},{b:'b'}));
let test={k:123,o:456};
for(let [key,value] of Object.entries(test)){
console.log([key,value]);
}
}
// 拓展运算符
// {...o1,...o2} <=> 相当于 Object.assign(o1,o2)
// ES7的方案
{
// 扩展运算符
// let {a,b,...c}={a:'test',b:'kill',c:'ddd',d:'ccc'};
// c={
// c:'ddd',
// d:'ccc'
// }
}
第七种原生数据类型
{
// Symbol为第七种原生数据类型
let s = Symbol(); // 不用new(), Symbol不是构造函数
typeof s // "symbol"
/* Symbol的用法 */
// Symbol.for('tag')会注册在全局作用域
// Symbol.for('a3')多次运行结果唯一
// Symbol()每次运行返回新结果
let a1=Symbol();
let a2=Symbol();
console.log(a1===a2); // false
let a3=Symbol.for('tag3');
let a4=Symbol.for('tag3');
console.log(a3===a4); // true
}
{
let a1=Symbol.for('abc');
let obj={
[a1]:'123', // 对象的属性表达式
'abc':345,
'c':456
};
console.log('obj',obj);
// 不能遍历Symbol属性
for(let [key,value] of Object.entries(obj)){
console.log('let of',key,value);
}
// Object.getOwnPropertySymbols()只获取Symbol的属性,返回值数组
console.log(Object.getOwnPropertySymbols(obj));
Object.getOwnPropertySymbols(obj).forEach(function(item){
console.log(obj[item]); // 123
})
// Reflect.ownkeys()获取包括Symbol的所有属性, 返回值为数组
console.log(Reflect.ownKeys(obj));
Reflect.ownKeys(obj).forEach(function(item){
console.log('ownkeys',item,obj[item]);
})
}
/* Set和Map */
// Set
// 声明和add添加值
// Set中的key值唯一
// size属性获取长度
{
let list = new Set();
list.add(5);
list.add(7);
console.log(list,list.size); // Set{5,7} 2
}
// 接收数组为参数初始化化
{
let arr = [1,2,3,4,5,1,2,3];
let list = new Set(arr);
console.log(list,list.size); // Set{1,2,3,4,5} 5
}
{
let list = new Set();
list.add(1);
list.add(2);
list.add(1); // 重复值可以操作, 但不生效
console.log('list',list); // Set{1,2}
let arr=[1,2,3,1,'2',NaN,NaN];
let list2=new Set(arr);
console.log('unique',list2); // Set{1,2,3,'2',NaN}
// 利用Set的key不能重复,为数组去重
function unique(arr) {
return Array.from(new Set(arr)); //等价 [...new Set(arr)]
}
}
// Set的API
// add 添加key
// delete 删除key
// has 判断key是否存在
// clear 清空所有key
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
console.log('has',list.has('add')); // true
console.log('delete',list.delete('add'),list); // true Set{'delete','clear','has'}
list.clear();
console.log('list',list); // Set{} 完全清空
}
//遍历Set中的内容
// for-of 配合keys()
// for-of 配合values()
// for-of 配合entries()
// forEach遍历
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
for(let key of list.keys()){
console.log('keys',key);
}
for(let value of list.values()){
console.log('value',value);
}
for(let [key,value] of list.entries()){
console.log('entries',key,value);
}
for(let a of list.entries()){
console.log('entries',a);
}
list.forEach(function(item){console.log(item);})
}
// WeakSet
// 成员只能是对象,而不能是其他类型的值
// WeakSet 不可遍历
// 没有size属性
// 对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用
// 没有clear方法
// API: add, has, delete
{
let weakList=new WeakSet();
let arg={};
console.log(weakList.size); // undefined
console.log(weakList.forEach); // undefined
let add = weakList.add(arg);
let has = weakList.has(arg);
let d = weakList.delete(arg);
console.log(add,has,d); // WeakSet {} true true
}
// Map
// “键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
// Object 结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现
// API:
// size属性
// set(key,value) // 而Set添加时add()
// get(key) // Set没有get, 因为只有一个值
// has(key)
// delete(key)
// clear
{
let map = new Map();
let arr=['123'];
map.set(arr,456);
console.log('map',map,map.get(arr)); // Map {["123"] => 456} 456
}
{
let map = new Map([['a',123],['b',456]]);
console.log('map args',map); // Map {"a"=>123,"b"=>456}
console.log('size',map.size); // 2
console.log('delete',map.delete('a'),map); // Map {"b"=>456}
console.log('clear',map.clear(),map); // Map{}
}
// weakMap
// key只能是对象
// 没有size属性, 没有clear方法
// 键名所指向的对象,不计入垃圾回收机制
{
let weakmap=new WeakMap();
let o={};
weakmap.set(o,123);
console.log(weakmap.get(o));
weakmap.size // undefined
weakmap.forEach // undefined
weakmap.clear // undefined
}
/* Array, Map, Set数据结构的横向对比 */
{
// map与array
let map = new Map();
let array = [];
// 增
map.set('t',1);
array.push({t:1});
console.log('(map | array 增)',map,array);
// 查
let m = map.has('t');
let a = array.find(item=>item.t); // find中的回调表示若对象存在item.t属性, 返回该对象
console.log('(map | array 查)',m,a);
// 改
map.set('t',2);
array.forEach(item=>item.t?item.t=2:''); // 先匹配后修改
console.info('(map | array 改)',map,array)
// 删
map.delete('t');
let idx = array.findIndex(item=>item.t);
array.splice(idx,1);
console.info('(map | array 删)',map,array)
}
{
// set和array的对比
let o1 = {t:1};
let o2 = {t:2};
let set = new Set();
let array = [];
// 增
set.add(o1);
array.push(o2);
console.info('(set | array 增)',set,array);
// 查
let s = set.has(o1);
let a = array.find(item=>item===o2);
console.info('(set | array 查)',s,a)
// 改
set.forEach(item=>item.t?item.t=111:'');
array.forEach(item=>item.t?item.t=222:'')
console.info('(set | array 改)',set,array)
// 删
set.forEach(item=>item.t?set.delete(item):undefined); // 间接用到set.delete()
let idx = array.findIndex(item=>item.t);
array.splice(idx,1);
console.info('(set | array 删)',set,array)
}
{
// set,map,Objec
let item = {t:1};
let map = new Map();
let set = new Set();
let obj = {};
// 增
map.set('t',1)
set.add(item)
obj.t = 1;
console.info('(map|set|obj 增)',map,set,obj);
// 查
console.info('(map|set|obj 查)',map.has('t'),set.has(item),'t' in obj)
// 改
map.set('t',3);
item.t = 4; // set中的是引用
obj.t = 2;
console.info('(map|set|obj 改)',map,set,obj);
// 删
map.delete('t');
set.delete(item);
delete obj.t;
console.info('(map|set|obj 删)',map,set,obj);
}
Proxy 对象代理, 拦截
Reflect 用来代替Object的部分方法 (Reflect.ownKeys()必会)
/* Proxy和Reflect */
{
let obj={
time:'2017-03-11',
name:'net',
_r:123
};
// monitor为Proxy实例
// monitor映射obj, 改变obj某些操作的默认行为
// monitor拦截/代理了对obj的相关操作
let monitor=new Proxy(obj,{
// 拦截对象属性的读取
get(target,key){
return target[key].replace('2017','2018')
},
// 拦截对象设置属性
// 限制处name属性外的其他属性修改
set(target,key,value){
if(key==='name'){
return target[key]=value; // 只有name属性可写
}else{
return target[key];
}
},
// 拦截key in object操作
has(target,key){
if(key==='name'){
return target[key] // 只有name被 in
}else{
return false;
}
},
// 拦截delete
deleteProperty(target,key){
if(key.indexOf('_')>-1){ // 属性中有'_'可以被删除
delete target[key];
return true;
}else{
return target[key]
}
},
// 拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
ownKeys(target){
return Object.keys(target).filter(item=>item!='time') // 返回不等time的属性, 返回值为数组
}
});
// Proxy实例get,读取相关操作
console.log('get',monitor.time,monitor); // 2017-> 2018
// Proxy实例set,写入相关操作
monitor.time='2018';
monitor.name='mukewang';
console.log('set',monitor.time,monitor,typeof monitor);
console.log(obj);
// key in object操作
console.log('has','name' in monitor,'time' in monitor); // true false
// delelte操作
// delete monitor.time;
// console.log('delete',monitor);
//
delete monitor._r;
console.log('delete',monitor);
// 拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
// Reflect.ownKeys也受影响??
console.log('keys',Object.keys(monitor));
console.log('getOwnPropertySymbols',Object.getOwnPropertySymbols(monitor));
console.log('getOwnPropertyNames',Object.getOwnPropertyNames(monitor));
console.log('ownKeys',Reflect.ownKeys(monitor)); // Reflect.ownKeys也受影响
}
// Reflect对象放置一些属于Object中属于语言内部的方法
// Object.defineProperty() <--> Reflect.defineProperty()
// 对象命令式操作
// delete obj.name --> Reflect.deleteProperty(obj,name)
// name in obj --> Reflect.has(obj,name)
{
let obj={
time:'2017-03-11',
name:'net',
_r:123
};
console.log('Reflect get',Reflect.get(obj,'time')); // 读属性
Reflect.set(obj,'new','Reflect test'); // 写
console.log('set',obj);
console.log('has',Reflect.has(obj,'name')); // [prop] in Object
Reflect.deleteProperty(obj,'new'); // delete object[prop]
console.log('deleteProperty',obj);
// Reflect.apply(target,thisArg,args)
// 一般来说,如果要绑定一个函数的this对象,
// 可以这样写fn.apply(obj, args),
// 但是如果函数定义了自己的apply方法,
// 就只能写成Function.prototype.apply.call(fn, obj, args)
// 采用Reflect对象可以简化这种操作。
// 老写法
let f1 = Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1
let f2 = Math.floor.apply(undefined,[1.75]) // 1
// 新写法
let f3 = Reflect.apply(Math.floor, undefined, [1.75]) // 1
console.log('Reflect.apply()',f1,f2,f3)
// Reflect.ownKeys(target)
let cc = Symbol('c');
let o11 = {
a : 'aa',
b : 'bb',
[cc] : 'cc'
}
Object.defineProperty(o11, 'key', {
enumerable: false,
value : 'enumerable_false'
});
console.log(Object.keys(o11)); // 能枚举的和不是Symbol的
console.log(Object.getOwnPropertyNames(o11)); // 包括不能枚举的
console.log(Object.getOwnPropertySymbols(o11)); // 只包括Symbol
console.log(Reflect.ownKeys(o11)); // 所有
}
// Proxy的应用场景
// 和数据类型解耦的校验模块
{
function validator (target, validator) {
return new Proxy(target,{
_validator: validator,
set(target,key,value,proxy){ // proxy-> receiver proxy对象本身或原型链上的
if (target.hasOwnProperty(key)) {
let va = this._validator[key];
if(!!va(value)) { // 比如执行了name(val)
return Reflect.set(target,key,value,proxy) // 正常赋值
} else {
throw Error(`不能设置${key}到${value}`)
}
} else{
throw Error(`${key} 不存在`)
}
}
})
}
// 对应handler的校验规则对象
const personValidators = {
name(val){
return typeof val === 'string'
},
age(val){
return typeof val === 'number' && val>18
}
}
class Person{
constructor(name,age){
this.name = name;
this.age=age;
return validator(this,personValidators)
}
}
const person = new Person('Lilei',30) // 是一个Proxy对象
console.log(person);
// person.name = 123; // 不是字符串, 报错
// person.age = 12; // 小于18 报错
person.name = '韩梅梅';
console.log(person.name);
}
class, constructor, extends, static(只适用静态方法)
super()
getter, setter
/*
* class类
* 关键字 : class constructor extends super() static
*/
{
// 基本定义和生成实例
class Parent {
constructor(name='mukewang') { // 构造函数
this.name =name;
}
}
let v_parent = new Parent('v');
console.log('构造函数和实例', v_parent);
}
{
/*继承*/
class Parent {
constructor(name = 'GivenCui 父类') {
this.name = name;
}
}
class Child extends Parent {
constructor(name='child'){
// super(); // 不传参数,默认使用父类参数
super(name); // 传参覆盖父类
this.type = 'child'; // 子类的参数必须放在super()后
}
}
var p = new Parent('父类 -')
var child = new Child('子类 --')
console.log('父类', p);
console.log('继承extends & super', child)
}
{
// getter, setter
class Parent{
constructor(name="父类"){
this.name = name;
}
get longName(){
return 'getter' + this.name;
}
set longName(value){
this.name = value;
}
}
let v= new Parent();
console.log('getter',v, v.longName);
v.longName = 'Kitty';
console.log('setter',v,v.longName);
}
{
// 静态方法
class Parent{
constructor(name="父类"){
this.name = name;
}
static tell(){ // 类上的方法
return '我是静态方法--通过类调用';
}
}
console.log('static静态方法',Parent.tell());
}
{
// 静态属性
class Parent{
constructor(name="父类"){
this.name = name;
}
}
Parent.type = '静态属性(暂时还没有关键字)';
console.log('静态属性',Parent.type);
}
/*
* Promise
* Promise 是异步编程的一种解决方案
* Promise对象代表一个异步操作,有三种状态:
* Pending(进行中)、
* Resolved(已完成,又称 Fulfilled)
* Rejected(已失败)
*/
{ // 普通的回调的写法
let ajax = function(callback){
console.log('callback: 执行01');
setTimeout(function(){
callback&&callback.call()
}, 1000)
console.log('callback: 执行03')
};
ajax(function () {
console.log('callback: 执行02');
})
}
{
// Promise的写法
let ajax = function () {
console.log('Promise1: 执行1');
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, 1000)
console.log('Promise1: 执行3');
})
// return 后面写代码没用
}
ajax().then(function () {
console.log('Promise1: 执行2');
}, function () {
console.log('Promise1: 执行2');
})
}
{
// Promise的多步回调 A->B->C
// then()的回调中返回new Promise即可
// new Promise(function(resolve,reject){}).then(rs,rj).then(rs,rj).then(rs,rj)
let ajax = function () {
console.log('Promise2: 执行1');
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, 1000)
})
// return 后面写代码没用
}
var step2 = ajax().then(function () {
/* resolve */
console.log('Promise2: 执行2');
return new Promise(function (resolve,reject) {
setTimeout(function () {
resolve();
},1000)
})
}, function () {
/* reject */
})
step2.then(function () {
/* resolve */
return new Promise(function (resolve,reject) {
console.log('Promise2: 执行3');
setTimeout(function () {
resolve()
},1000)
})
},function () {
/* reject */
}).then(function () {
/* resolve */
console.log('Promise2: 执行4');
},function () {
/* reject */
})
}
{ // catch()捕获错误
// catch()也接收reject的情况
let ajax=function(num){
console.log('执行4');
return new Promise(function(resolve,reject){
if(num>5){
resolve()
}else{
reject()
throw new Error('出错了')
}
})
}
ajax(6).then(function(){
console.log('log',6);
}).catch(function(err){
console.log('catch',err);
});
// ajax(3).then(function(){
// console.log('log',3);
// }).catch(function(err){
// console.log('catch',err);
// });
}
{ // Promise.all()静态方法
// 所有图片加载完再添加到页面
function loadImg(src){
return new Promise((resolve,reject)=>{
let img=document.createElement('img');
img.src=src;
img.onload=function(){
resolve(img); // 创建的img
}
img.onerror=function(err){
reject(err);
}
})
}
function showImgs(imgs){
let box = document.createElement('div');
box.className = 'imgBox_all';
imgs.forEach(function(img){
box.appendChild(img); // 插入
})
document.body.appendChild(box);
}
// Promise.all([promise对象])
// 返回值为新的promise实例
Promise.all([
loadImg('http://mvimg1.meitudata.com/5555435ce56169374.jpg'),
loadImg('http://img5.duitang.com/uploads/item/201603/03/20160303200058_nXiHv.jpeg'),
loadImg('http://mvimg1.meitudata.com/563369a719e204730.jpg')
]).then(showImgs)
}
{ // Promise.race() 静态方法
function loadImg(src){
return new Promise((resolve,reject)=>{
let img=document.createElement('img');
img.src=src;
img.onload=function(){
resolve(img);
}
img.onerror=function(err){
reject(err);
}
})
}
function showImgs(img){
document.querySelector('.imgBox_race').appendChild(img)
}
// Promise.race([promise实例数组])
// 最快的那个
Promise.race([
loadImg('http://mvimg1.meitudata.com/5555435ce56169374.jpg'),
loadImg('http://img5.duitang.com/uploads/item/201603/03/20160303200058_nXiHv.jpeg'),
loadImg('http://mvimg1.meitudata.com/563369a719e204730.jpg')
]).then(showImgs)
}
/*
* Iterator和for...of循环
遍历器的本质: 指针对象 , 指针是next()
*/
{
let arr = ['hello','world'];
let map = arr[Symbol.iterator]();
console.log(map,typeof map);
console.log('第1次',map.next()); // {value: "hello", done: false}
console.log('第2次',map.next()); // {value: "world", done: false}
console.log('第3次',map.next()); // {value: undefined, done: true}
}
{
// object默认没有iterator接口
// 自定义object的iterator接口
let obj = {
start : [1,3,2],
end : [7,9,8],
[Symbol.iterator](){ // 111
let self = this;
let index = 0;
let arr = self.start.concat(self.end); // [1,3,2,7,9,8]
let len = arr.length;
return { // 2222
next(){ // 333
if(index < len){
return { // 444
value:arr[index++],
done:false
}
}else{
return {
value:undefined,
done:true
}
}
}
}
}
}
// 用 for-of验证
for(let key of obj) {
console.log(key)
}
console.log('start' in obj);
}
Generator 函数是 ES6 提供的一种异步编程解决方案
从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。
形式上,Generator函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
/*
* Generator (返回为itorator接口)
* next函数的用法
* yield*的语法
* function* (){yield或yield*}
*/
{
// genertaor基本定义
let tell=function* (){
console.log('a阶段')
/* step1 */
yield 'a'; // .next() step1
console.log('b阶段')
/* step2 */
yield 'b'; // .next() step2
console.log('c阶段')
/* step3 */
return 'c' // .next() step3
};
let k=tell(); // 不运行, 返回一个指针对象(iterator)
console.log('yield',k, typeof k); // Generator{} object
console.log(k.next()); // {value:'a',done:false}
console.log(k.next()); // {value:'b',done:false}
console.log(k.next()); // {value:'c',done:true}
console.log(k.next()); // {value:undefined,done:true}
}
{
// 内部用yiled写可以简化
let obj={};
obj[Symbol.iterator]=function* (){
yield 1;
yield 2;
yield 3;
}
for(let value of obj){
console.log('value',value);
}
}
{ // 事物只有三种状态: A, B, C
let state=function* (){
while(1){
yield 'A';
yield 'B';
yield 'C';
}
}
let status=state();
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
}
// async语法 (generator的一个语法糖)
// 去掉*, yiled换成await
// 需要babel安装对应的插件
// {
// let state=async function (){
// while(1){
// await 'A';
// await 'B';
// await 'C';
// }
// }
// let status=state();
// console.log(status.next());
// console.log(status.next());
// console.log(status.next());
// console.log(status.next());
// console.log(status.next());
// }
/* 实例: 抽奖和长轮询 */
// 抽奖逻辑
{
let draw=function(count){
// 具体抽奖逻辑
console.info(`剩余${count}次`)
}
let residue=function* (count){
while (count>0) {
count--;
yield draw(count);
}
}
let star=residue(5);
let btn=document.createElement('button');
btn.id='start';
btn.textContent='抽奖';
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click',function(){
star.next();
},false)
}
// 长轮循
{
// 长轮询
let ajax=function* (){
yield new Promise(function(resolve,reject){
setTimeout(function () {
resolve({code:0}) // code:0只执行1次, code:一直轮循不停
}, 200);
})
}
let pull=function(){
let genertaor=ajax();
let step=genertaor.next();
step.value.then(function(d){
if(d.code!=0){
setTimeout(function () {
console.info('wait');
pull()
}, 1000);
}else{
console.info(d);
}
})
}
pull();
}
修饰器(Decorator)是一个函数,用来修改类的行为, 不能用于普通函数.
修饰器可以自定义, 也可引用第三方模块core-decorators.js.
使用decorator之前,需要install一个babel插件 babel-plugin-transform-decorators-legacy
,安装完成后配置.babelrc
// 安装插件及配置
yarn add babel-plugin-transform-decorators-legacy -D/--dev
// .babelrc中
"plugins": [
"transform-decorators-legacy" // new add
]
{
let readonly=function(target,name,descriptor){
// descriptor对象原来的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
// 定义 Object.defineProperty(obj,{}) | Object.defineProperties(obj,{})
// 查看Object.getOwnPropertyDescriptor(obj,'prop')
descriptor.writable=false;
return descriptor
};
// 针对类的方法
class Test{
@readonly
time(){ // 相当于Test.prototype.time()
return '2017-03-11'
}
}
let test=new Test();
// test.time=function(){ // 赋值报错, 只读
// console.log('reset time');
// };
console.log(test.time());
}
// 针对类
{
let typename=function(target,name,descriptor){
target.myname='hello';
}
@typename
class Test{
}
console.log('类修饰符',Test.myname);
// 第三方库修饰器的js库:core-decorators; yarn add core-decorators -D
}
// 实例
// 日志系统, 数据埋点
// 网站数据统计分析 埋点技术(数据分析第一步)
{
// 自定义修饰器--日志埋点
let log=(type)=>{
return function(target,name,descriptor){
let src_method=descriptor.value;
descriptor.value=(...arg)=>{
src_method.apply(target,arg);
console.info(`log ${type}`);
}
}
}
class AD{
@log('show') // 此处为埋点
show(){
console.info('ad is show')
}
@log('click') // 此处为埋点
click(){
console.info('ad is click');
}
}
let ad=new AD();
ad.show();
ad.click();
}
兼容CommonJs(Node),AMD(require.js)
是尽量的静态化,使得编译时就能确定模块的依赖关系
CommonJs,AMD都是运行时确定依赖关系
import 'babel-polyfill'; // 表示执行模块
import './class/lesson3_19'; // 表示执行模块
/* lesson3_19 */
export let A=123; // 导出变量
export function test(){ // 导出函数
console.log('test');
}
export class Hello{ // 导出类
test(){
console.log('class');
}
}
/* index.js */
import {A,test,Hello} from './class/lesson3_19';
console.log(A,test,Hello);
// 只要A
import {A} from './class/lesson3_19';
console.log(A);
// 设置别名
import * as lesson from './class/lesson3_19';
console.log(lesson.A,lesson.test,lesson.Hello);
/* lesson3_19 */
let A=123;
let test=function(){
console.log('test');
}
class Hello{
test(){
console.log('class');
}
}
export default { // 最后一起导出
A,
test,
Hello
}
/* index.js */
import L19 from './class/lesson3_19';
console.log(L19.A,L19.test,L19.Hello);