@GivenCui
2017-05-24T14:01:08.000000Z
字数 33685
阅读 981
ES6
参考
ECMAScript6入门--阮一峰
Babel在线编译器
vi/vim package.json // 查看package.jsonESC // 恢复到正常模式: // 进入命令模式: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.jsvar compiled = _.template("hello: <%= name %>");compiled({name: 'moe'});
1.很多第三方库源码是ES6
vue.js
Element (饿了么的UI)
D3 (图表业务和图表动画)
2.Vue,React,Angular都用到了ES6
构建工具 > 基础语法 > 实战演练 (express和mockjs)
- 是彩票业务
- 属于电商网站中较复杂的部分
1. 安装node2. 配置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 --productionimport 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'}).argvexport 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'; // webpackimport 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', // copyextname:'.min.js'})).pipe(uglify({compress : {properties : false},output : {'quote_keys' : true}})).pipe(gulp.dest('server/public/js')).pipe(gulpif(args.watch,livereload())) // 判断如果命令有watch则livereload热更新})
// 注意 yargsnpm install gulp gulp-if gulp-concat webpack webpack-stream vinyl-named gulp-livereload gulp-rename gulp-uglify gulp-util yargs --save-dev// yarnyarn 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-devyarn 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 返回Booleanvar server = liveserver.new(['--harmony','server/bin/www']); // --harmpny 在当前命令行执行后面的脚本 www.jsserver.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.jsgulp.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); // 输出undefinedvar foo = 2;// let 的情况console.log(bar); // 报错ReferenceErrorlet bar = 2;/* 4. 暂时性死区 (temporal dead zone,简称 TDZ) */// let在整个{}中都存在TDZvar tmp = 123;if (true) {tmp = 'abc'; // ReferenceErrorlet 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=undefinedconsole.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 trueconsole.log(regex.test('xyZ123'), regex2.test('xyZ123')); // true truelet 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"],nullconsole.log(a1.sticky, a2.sticky); //表示是否开启了粘连模式}// u修饰符{console.log('u修饰符',/^\uD83D/.test('\uD83D\uDC2A')); // trueconsole.log('u修饰符',/^\uD83D/u.test('\uD83D\uDC2A')); // false// 大括号表示Unicode字符,只有加上u才能识别console.log(/\u{61}/.test('a')); // falseconsole.log(/\u{61}/u.test('a')); // trueconsole.log(/\u{20BB7}/u.test('𠮷')); // true// 点(.)字符不能识别码点大于0xFFFF的Unicode字符,必须加上u修饰符。let s = '𠮷';console.log('大于0xFFFF的Unicode字符',/^.$/.test(s)); // falseconsole.log('使用u字符',/^.$/u.test(s)); // true// 使用u修饰符后,所有量词都会正确识别大于码点大于0xFFFF的Unicode字符。console.log('量词',/a{2}/.test('aa')); // trueconsole.log('量词',/a{2}/u.test('aa')); // trueconsole.log('量词',/𠮷{2}/.test('𠮷𠮷')); // falseconsole.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`); // aconsole.log('s',`\u20BB7`); // /u20BB 和7console.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)); // 55362console.log('at1',s.charCodeAt(1)); // 57271let s1='𠮷a';console.log('length',s1.length);console.log('code0',s1.codePointAt(0)); // 134071// "20bb7" 十六进制 parseInt("20bb7",16) 或 parseInt(0x20bb7)--> 134071console.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 -> binaryconsole.log(0O17); // 0o17 o -> octonary}// isFinite --> Number.isFinite// parseInt --> Number.parseInt// 'parseInt' in Number // true{console.log('15',Number.isFinite(15)); // trueconsole.log('NaN',Number.isFinite(NaN)); // falseconsole.log('1/0',Number.isFinite('true'/0)); // falseconsole.log('NaN',Number.isNaN(NaN)); // trueconsole.log('0',Number.isNaN(0)); // false}// 新增 Number.isInteger()判断整数{console.log('25',Number.isInteger(25)); // trueconsole.log('25.0',Number.isInteger(25.0)); // trueconsole.log('25.1',Number.isInteger(25.1)); // falseconsole.log('25.1',Number.isInteger('25')); // false}// (-2^53 ,2^53)期间里的为安全整数, 超出后计算不准{Math.pow(2, 53); // 9007199254740992Math.pow(2, 53) === Math.pow(2, 53) + 1; // true 说明不准了console.log(Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER); // 9007199254740991 -9007199254740991console.log('10',Number.isSafeInteger(10)); // trueconsole.log('a',Number.isSafeInteger('a')); // false}// Math.trunc() 去除一个数的小数部分,返回整数部分// 相当于 正数时, 相当于 Math.floor()// 相当于 负数时, 相当于 Math.ceil(){console.log(4.1,Math.trunc(4.1)); // 4console.log(4.9,Math.trunc(4.9)); // 4console.log(-4.1,Math.trunc(-4.1)); // -4console.log(4.1,Math.floor(4.1)); // 4console.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); // -1Math.sign(5); // +1Math.sign(0); // +0Math.sign(-0); // -0Math.sign(NaN); // NaNMath.sign('foo'); // NaNMath.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类似mapconsole.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-polyfillfor(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 worldtest('hello','kill'); // 默认值 hello world}{ // 作用域问题let x='test';function test2(x,y=x){console.log('作用域',x,y);}test2('kill'); // kill killtest2(); // 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)); // 6console.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); // falselet 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')); // trueconsole.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); // undefinedconsole.log(weakList.forEach); // undefinedlet 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); // 2console.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 // undefinedweakmap.forEach // undefinedweakmap.clear // undefined}
/* Array, Map, Set数据结构的横向对比 */{// map与arraylet 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,Objeclet 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;}},// 拦截deletedeleteProperty(target,key){if(key.indexOf('_')>-1){ // 属性中有'_'可以被删除delete target[key];return true;}else{return target[key]}},// 拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNamesownKeys(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 ObjectReflect.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]) // 1let f2 = Math.floor.apply(undefined,[1.75]) // 1// 新写法let f3 = Reflect.apply(Math.floor, undefined, [1.75]) // 1console.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)); // 只包括Symbolconsole.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, setterclass 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](){ // 111let self = this;let index = 0;let arr = self.start.concat(self.end); // [1,3,2,7,9,8]let len = arr.length;return { // 2222next(){ // 333if(index < len){return { // 444value: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() step1console.log('b阶段')/* step2 */yield 'b'; // .next() step2console.log('c阶段')/* step3 */return 'c' // .next() step3};let k=tell(); // 不运行, 返回一个指针对象(iterator)console.log('yield',k, typeof k); // Generator{} objectconsole.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, Clet 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{@readonlytime(){ // 相当于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';}@typenameclass 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);// 只要Aimport {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);