ES6新特性🥳🥳 let,const新的声明方式🧐🧐 Let
变量不能重复声明
块级作用域
不存在变量提升
不影响作用域链
暂时性死区(只要块级作用域内存在let命令,这个区域就不再受外部影响)
for中的var/let
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var arr = [];for (var i = 0 ; i < 3 ; i++) { arr[i] = function ( ) { console .log ('@' + i); } } arr[0 ](); arr[1 ](); arr[2 ](); for (var i = 0 ; i < 3 ; i++) { arr[i](); } for (let i = 0 ; i < 3 ; i++) { arr[i](); }
第一个for循环中,向arr中添加三个函数,但是函数中的i是并不会赋值的,正常人的思维习惯会误以为,函数中的i也会被赋予值,但实际上并没有。
当在外部调用数组中三个函数的时候,每个函数创建不同的AO对象,但是其寻找的i值是全局下的i,就是GO中的i。此时GO中的i已经被上文中的for循环结束导致i赋值成为3,所以执行结果为3,3,3
第二个for循环中的var对i进行重新赋值导致i又变回0,当执行arr[0]()
这个函数的时候,他会前往声明函数的所在域,此时函数寻找全局GO中的i,而i变成了0。依次执行下去。所以结果为0,1,2
第三个for循环用let声明,创建了一个一个的块,但是!常规思路会导致误认为调用函数的时候,里面的i也是块里面的i值,会认为i为0,1,2;而函数调用的时候会有一个特殊性,其在被调用的时候会返回函数声明的地方,它的i值查找的就不是for循环let创建的一个一个块里面的i,而是去查找声明函数所在域下的i值。上文中的函数声明是在全局下,它的i值被第二个函数定格在了3,所以函数输出为全3
1 2 3 4 5 6 7 8 9 function fn ( ) { console .log (a); } var a = 10 ;{ let a = 99 ; fn (); }
const
一定要赋初始值
一般常量是用大写(潜规则)
常量的值不能修改
块级作用域
对于数组和对象的元素修改,不算是对常量的修改,不会报错
变量的解构赋值
ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值这被称为结构赋值
数组的结构:
1 2 3 4 5 6 7 const f4 = ['小沈阳' , '刘能' , '赵四' , '宋小宝' ];let [xiao, liu, zhao, song] = f4;console .log (xiao);console .log (liu);console .log (zhao);console .log (song);
对象的结构:
1 2 3 4 5 6 7 8 9 let { name, age, xiaopin } = zhao; console .log (name); console .log (age); console .log (xiaopin); xiaopin ();
相当于 zhao.xiaopin(); 替换成了 xiaopin( );不用重复书写zhao.
模板字符串 不同于es5中的字符串用+拼接,es6的模板字符串可以使用``符号和${变量名}进行拼接
1 2 3 4 5 6 7 8 9 10 let str = `<ul> <li>沈腾</li> <li>玛丽</li> <li>魏翔</li> </ul>` ;let lovest = '魏翔' ;let out = `将${lovest} 替换成lovest` ;console .log (out);
对象的简化写法
ES6 允许在大括号内直接写入变量和函数 作为对象的属性和方法,这样的书写更加简洁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let name = '尚硅谷' ;let change = function ( ) { console .log ('我可以改变你' ); }; const school = { name, change, import () { console .log ('声明方法' ); } } console .log (school);
箭头函数以及声明特点🏹🏹
es6 允许使用[箭头] (=>) 定义函数.
声明一个函数:
1 2 3 4 5 6 let fn = (a, b ) => { return a + b; }; let reslut = fn (1 , 2 );console .log (reslut);
this是静态的,this始终是指向函数声明时所在作用域下的this的值(其父级作用域的this)
不能做为构造实例化对象 就是无法构造函数
不能使用arguments变量
箭头函数的简写 (1)省略小括号,当形参有且只有一个时候
1 2 3 4 let add = n => { return n + n; }; console .log (add (1 ));
(2)省略花括号,当代码只有一条语句时候,此时return必须省略
而且语句的执行结果就是函数的返回值
1 2 let pow = n => n * n;console .log (pow (9 ));
箭头函数的this指向问题 this是静态的,this始终是指向函数声明时所在作用域下的this的值(其父级作用域的this)
何为父级作用域的this,一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 function foo ( ) { setTimeout (() => { console .log ('id' , this .id ); }, 500 ); }; var id = 10 ;foo.call ({ id : 50 });
foo.call( )方法改变了foo函数的this指向,定时器中的函数this指向的是window,所以普通函数的 this.id 为window下的10。而箭头函数因为foo函数的this改变使得其this也改变成了对象**{id:50}**
函数参数的默认值和rest参数 函数参数的默认值
ES6 允许给函数参数赋值初始值
形参初始值 具有默认值的参数 一般位置要靠后(潜规则)
1 2 3 4 5 6 function add (a, b, c = 2 ) { return a + b + c; }; let result = add (1 , 2 );console .log (result);
与结构赋值结合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function connect ({ host, username, password, port } ) { console .log (host); console .log (username); console .log (password); console .log (port); } connect ({ host : '' , username : 'root' , password : 'root' , port : 3306 , });
rest参数 es6 引入 rest 参数 用于获取函数的实参 用来替代arguments
es5获取函数实参的方法是 arguments
1 2 3 4 5 function date (...args ) { console .log (args); }; date ('黑' , '白' , '灰' );
rest参数 必须 放在参数末尾
1 2 3 4 5 6 7 function fn (a, b, ...args ) { console .log (a); console .log (b); console .log (args); }; fn (1 , 2 , 3 , 4 , 5 , 6 );
扩展运算符 […] 扩展运算符能将[数组]转换为逗号分割的 [参数序列]
1 2 3 4 const colors = ['黑' , '白' , '灰' ];console .log (colors); console .log (...colors);
扩展运算符的运用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <div > </div > <div > </div > <div > </div > <script > const anSe = ['黑' , '白' , '灰' ]; const liangSe = ['红' , '黄' , '绿' ]; const heBing = [...anSe, ...liangSe]; console .log (heBing); const q1 = ['E' , 'G' , 'M' ]; const q2 = [...q1]; console .log (q2); const divs = document .querySelectorAll ('div' ); console .log (divs); const divArr = [...divs]; console .log (divArr); </script >
Symbol的介绍与创建 创建Symbol 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let s = Symbol ();console .log (s, typeof s);let s2 = Symbol ('尚硅谷' );let s3 = Symbol ('尚硅谷' );console .log (s2 === s3); let s4 = Symbol .for ('尚硅谷' );let s5 = Symbol .for ('尚硅谷' );console .log (s4 === s5);
Symbol创建对象属性 第一种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let game = {};let methods = { name : Symbol ('name' ), up : Symbol ('up' ), down : Symbol ('down' ), }; game[methods.up ] = function ( ) { console .log ('我可以改变形状' ); }; game[methods.down ] = function ( ) { console .log ('我可以快速下降' ); }; game[methods.name ] = 'A神' ; console .log (game[methods.name ]);game[methods.up ](); game[methods.down ]();
第二种:
1 2 3 4 5 6 7 8 9 10 11 12 13 let youxi = { [Symbol .for ('name' )]: '狼人杀' , [Symbol .for ('say' )]: function ( ) { console .log ('我可以发言' ); }, [Symbol .for ('zibao' )]: function ( ) { console .log ('我可以自爆' ); }, }; console .log (youxi[Symbol .for ('name' )]);youxi[Symbol .for ('say' )](); youxi[Symbol .for ('zibao' )]();
Symbol内置的一些属性 Symbol.hasInstance方法
静态成员
当此类被当作instanceof后值作为参数时触发
传递的param
为被检测的对象
1 2 3 4 5 6 7 8 9 10 class Person { static [Symbol .hasInstance ](param) { console .log (param); console .log ('我被用来检测类型了' ); } }; let o = { a : '1' }; console .log (o instanceof Person );
instanceof更详细的解释:
用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上
instanceof
可以对不同的对象实例进行判断,判断方法是根据对象的原型链依次向下查询,如果obj2的原型属性存在obj1的原型链上,obj1 instanceof obj2
值为true。
obj1是否是obj2的实例
Symbol.isConcatSpreadable方法
arr2[Symbol.isConcatSpreadable] = false;
arr2[Symbol.isConcatSpreadable] = true;
为false表示不展开合并
1 2 3 4 5 6 7 const arr = [1 , 2 , 3 ]; const arr2 = [4 , 5 , 6 ]; arr2[Symbol .isConcatSpreadable ] = false ; console .log (arr.concat (arr2));
Set集合和Map对象 set集合
没有重复值的一堆数据
即便是传入的参数有重复值他也会自动将重复值削减为1个
1 2 3 4 5 6 let s = new Set ();let s2 = new Set (['红' , '黑' , '白' , '灰' , '红' ]);console .log (s2 instanceof Object ); console .log (s2);
内置的方法:
获取元素个数 .size
console.log(s2.size); // 4
添加元素 .add
s2.add('蓝');
console.log(s2); //Set(5) {'红', '黑', '白', '灰', '蓝'}
删除元素 .delete
s2.delete('黑');
console.log(s2); //Set(4) {'红', '白', '灰', '蓝'}
检测元素是否存在 .has
console.log(s2.has('橙')); //false
清空集合 .clear
s2.clear();
console.log(s2); //Set(0) {size: 0}
集合实践:
let arr = [1, 2, 3, 4, 4, 5, 5, 6, 6, 9, 8, 7];
数组去重
let result = [...new Set(arr)];
console.log(result);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 2. 交集 `let arr2 = [2, 4, 5, 9, 2, 8, 9];` - ```js let result = [...new Set(arr)].filter(item => { let s2 = new Set(arr2); //将s2去重 //利用has方法检测arr中的在s2中是否也存在 if (s2.has(item)) { return true; } else { return false; } });
```js // 简化写法: let result = […new Set(arr)].filter(item => new Set(arr2).has(item) ); console.log(result);
1 2 3 4 5 6 3. 并集 - ```js let union = [...new Set([...arr, ...arr2])]; console.log(union);
差集
```js let result = […new Set(arr)].filter(item => !(new Set(arr2).has(item)) ); console.log(result);1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 ### map对象 map其实就是升级版对象,可以自定义key值和属性值 - 声明map`let m = new Map();` - .set方法添加元素`("key值",“属性值”)` - .size获取长度`.size` - .delete删除对应属性`.delete('属性')` - .get获取方法`.get('change')` - .clear清空`.clear()` ```js // 声明 Map // 就是升级版对象 可以自定义key值和属性值 let m = new Map(); // .set方法添加元素 ('key值','属性值') m.set('name', '123'); m.set('change', function() { console.log('456'); }); console.log(m); var abc = { name: '789' }; m.set(abc, [7, 8, 9]); console.log(m); // .size方法获取长度 console.log(m.size); // 删除方法 m.delete('name'); console.log(m); // 获取方法 console.log(m.get('change')); console.log(m.get(abc)); //[7,8,9] // 清空 // m.clear(); // console.log(m);
Generator 生成器函数
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
生成器其实就是一个特殊的函数 必须要有 * 分割
1 2 3 4 5 6 7 8 9 10 function * gen ( ) { console .log ('hello generator' ); yield '一直没有耳朵' ; console .log (222 ); yield '一直没有尾巴' ; console .log (333 ); yield '真奇怪' ; console .log (444 ); } let iterator = gen ();
1 2 3 console .log (iterator.next ());
1 2 3 4 5 6 console .log (iterator.next ());console .log (iterator.next ());
1 2 3 4 5 6 7 8 9 console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());
1 2 3 4 5 6 7 8 9 10 11 console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());
可以看出iterator.next()
的值是yield后面的值,并且是以对象形式返回。而没next()触发一次,都会执行一次yield和yield之间的代码块
next()、throw()、return() 的共同点
next()
、throw()
、return()
这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield
表达式。
next()函数参数:
next方法可以传入实参 相当于将BBB赋值给yield 111;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function * gen (arg ) { console .log (arg); let one = yield 111 ; console .log (one); let two = yield 222 ; console .log (two); let three = yield 333 ; console .log (three); } let iterator = gen ('AAA' );console .log (iterator.next ());console .log (iterator.next ('BBB' ));console .log (iterator.next ('CCC' ));console .log (iterator.next ('DDD' ));
throw()将yield表达式替换成throw语句抛出错误
throw()
是将yield
表达式替换成一个throw
语句。
生成器函数解决回调地狱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 function one ( ) { setTimeout (() => { console .log (111 ); iterator.next (); }, 1000 ); }; function two ( ) { setTimeout (() => { console .log (222 ); iterator.next (); }, 2000 ); }; function three ( ) { setTimeout (() => { console .log (333 ); iterator.next (); }, 3000 ); }; function * gen ( ) { yield one (); yield two (); yield three (); }; let iterator = gen ();iterator.next ();
Promise对象
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。
Promise的状态有 初始化(pending) 成功(fulfilled) 失败(rejected)
单纯的new Promise或者有变量接收他,也会自执行。Promise中传入一个函数,函数的两个参数分别是成功执行形参和失败执行形参
resolve(date);
成功执行函数
reject(err);
失败执行函数
另外:如果两个函数都存在则谁在上边就先执行谁,前提是调用过then方法并传参
1 2 3 4 5 6 7 8 9 const p = new Promise (function (resolve, reject ) { setTimeout (function ( ) { let date = '数据库中用户数数据' ; resolve (date); }, 1000 ); });
Promise内置的方法 Promise.all() Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
传入的p1,p2,p3都是Promise实例,只有三个状态都变成已成功,才会执行promiseAll的已成功
但是当有一个失败,就会触发catch错误执行
1 2 3 4 5 6 7 8 var promiseAll = Promise .all ([p1, p2, p3]);promiseAll.then ((data ) => { console .log (data); console .log ('圆满结束' ); }).catch ((error ) => { console .error ('失败请求' ); console .log (error); });
Promise.race() 原型上的方法 then方法
承接上文中的resolve(date)
和reject(err)
,then中可以传入两个参数(函数),第一个函数为成功执行所触发的函数resolve(date)
,第二个函数为失败执行所触发的reject(err)
1 2 3 4 5 6 7 p.then (function (value ) { console .log (value); }, function (reason ) { console .error (reason); });
catch方法
catch方法用于捕获失败的单独方法,then方法可以传入两个函数来捕获成功和失败,而catch是单独的一个专门捕获失败的方法
1 2 3 4 5 6 7 8 9 10 11 12 const p = new Promise ((resolve, reject ) => { setTimeout (() => { reject ("出错了!" ); }, 1000 ); }); p.catch (function (reason ) { console .error (reason); })
finally方法
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
例题: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 console .log ("start" );setTimeout (() => { console .log ("children2" ) Promise .resolve ().then (() => { console .log ("children3" ) }) }, 0 ) new Promise (function (resolve, reject ) { console .log ("children4" ) setTimeout (function ( ) { console .log ("children5" ) resolve ("children6" ) }, 0 ) }).then (res => { console .log ("children7" ) setTimeout (() => { console .log (res) }, 0 ) });
先将同步任务start
执行
settimeout会被放到消息队列,也可以称它为宏任务
new Promise的机制是当new时就立即执行,所以输出children4
,而后面的settimeout会再次放到消息队列进行排队
此时检查执行栈中有无同步任务,微任务队列有无任务,前往消息队列依次执行。
队列中第一个settimeout输出children2
,而下面的Promise属于成功执行立即调用后面的.then,输出children3
执行第二个settimeout中的children5
,下面的resolve(“children6”)被调用,紧跟后面的.then中的任务被放到微任务队列,而此时消息队列已经为空。
执行微任务队列中的then,输出children7
,settimeout被放到消息队列,此时微任务队列空。
最后微任务队列为空,执行消息队列中的console.log(res)输出children6
Async/Await Async
async/await是写异步代码的新方式,以前的方法有回调函数 和Promise 。
async/await是基于Promise实现的,它不能用于普通的回调函数。
async/await与Promise一样,是非阻塞的。
async/await使得异步代码看起来像同步代码,这正是它的魔力所在。
async/await
实际上是Generator
的语法糖。顾名思义,async
关键字代表后面的函数中有异步操作,await
表示等待一个异步方法执行完成。声明异步函数只需在普通函数前面加一个关键字async
即可,如:
1 async function funcA ( ) {}
async
函数返回一个Promise对象(如果指定的返回值不是Promise对象,也返回一个Promise,只不过立即 resolve
,处理方式同 then
方法),因此 async
函数通过 return
返回的值,会成为 then
方法中回调函数的参数:
async
返回的Promise函数实际上是Promise.resolve()成功回调
1 2 3 4 5 6 7 async function funcA ( ) { return 'hello!' ; } funcA ().then (value => { console .log (value); })
Await 顾名思义, await
就是异步等待,它等待的是一个Promise,因此 await
后面应该写一个Promise对象,如果不是Promise对象,那么会被转成一个立即 resolve
的Promise。 async
函数被调用后就立即执行,但是一旦遇到 await
就会先返回,等到异步操作执行完成,再接着执行函数体内后面的语句。总结一下就是:async
函数调用不会造成代码的阻塞,但是await
会引起async
函数内部代码的阻塞。看看下面这个例子:
await 必须写在async函数中
await 右侧的表达式一般为promise对象
await 返回的是promise成功的值
await 的promise失败了,就会抛出异常,需要通过try…catch捕获处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const p = new Promise ((resolve, reject ) => { resolve ('成功的值!' ); }) async function main ( ) { try { let result = await p; console .log (result); } catch (e) { console .log (e); } } main ();
例题: 01:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 async function async1 ( ) { console .log ("async1 start" ) await async2 (); console .log ("async1 end" ) }; async function async2 ( ) { console .log ("async2" ) }; console .log ("script start" );setTimeout (function ( ) { console .log ("setTimeout" ) }, 0 ); async1 ();new Promise (function (resolve ) { console .log ("promise1" ) resolve () }).then (function ( ) { console .log ("promise2" ) }); console .log ("script end" );
定时器的事件会被放置在消息队列
promise实例对象的then方法会被放置在微任务队列
等待调用栈为空时再执行微任务队列和消息队列
先执行微任务队列再执行消息队列
02:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 console .log ("start" );setTimeout (() => { console .log ("children2" ) Promise .resolve ().then (() => { console .log ("children3" ) }) }, 0 ) new Promise (function (resolve, reject ) { console .log ("children4" ) setTimeout (function ( ) { console .log ("children5" ) resolve ("children6" ) }, 0 ) }).then (res => { console .log ("children7" ) setTimeout (() => { console .log (res) }, 0 ) });
03:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 var p = new Promise (resolve => { console .log (4 ); resolve (5 ); }).then (resolve => { console .log (resolve); }); function func1 ( ) { console .log (1 ); }; function fun2 ( ) { setTimeout (() => { console .log (2 ); }); func1 (); console .log (3 ); new Promise (resolve => { resolve (); }).then (resolve => { console .log ('新的resolve' ); }) p.then (resolve => { console .log (7 ); }).then (resolve => { console .log (6 ); }); } fun2 ()
04:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 async function async1 ( ) { console .log ('async1 start' ); const result = await async2 (); console .log (result); console .log ('async1 end' ); } async function async2 ( ) { console .log ('async' ); return "testAwait" ; } console .log ('script start' );setTimeout (function ( ) { console .log ('setTimeout' ); }, 0 ); async1 ();new Promise (function (resolve ) { console .log ('promise1' ); resolve (); }).then (function ( ) { console .log ('promise2' ); }); new Promise (function (resolve ) { console .log ('promise3' ); resolve (); }).then (function ( ) { console .log ('promise4' ); }); console .log ('script end' );
iterator迭代器(遍历器) 有iterator接口 即可实现迭代器
工作原理
创建一个指针对象,指向当前数据结构的起始位置
第一次调用对象的next方法,指针自动指向数据结构的第一个成员
接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
每调用next方法返回一个包含value和done属性的对象
注:需要自定义遍历数据的时候,要想到迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const xiyou = ['唐僧' , '孙悟空' , '猪八戒' , '沙僧' ];for (let v of xiyou) { console .log (v); } let iterator = xiyou[Symbol .iterator ]();console .log (iterator);console .log (iterator.next ()); console .log (iterator.next ()); console .log (iterator.next ()); console .log (iterator.next ()); console .log (iterator.next ());
ES6-class类 es5的构造函数创建对象 1 2 3 4 5 6 7 8 9 10 11 12 13 function Phone (name, price ) { this .name = name; this .price = price; }; Phone .prototype .call = function ( ) { console .log ('我可以打电话!' ); }; let HuaWei = new Phone ('华为' , 1999 );HuaWei .call ();
继承: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function Phone (brand, price ) { this .brand = brand; this .price = price; } Phone .prototype .call = () => { console .log ('我可以打电话' ); }; function SmartPhone (brand, price, color, size ) { Phone .call (this , brand, price); this .color = color; this .size = size; } SmartPhone .prototype = new Phone ();SmartPhone .prototype .constructor = SmartPhone ;SmartPhone .prototype .photo = () => { console .log ('拍照功能' ); }; SmartPhone .prototype .playGame = () => { console .log ('玩游戏' ); } const chuizi = new SmartPhone ('锤子' , 1999 , '黑色' , '123' );console .log (chuizi);chuizi.photo (); chuizi.call ();
es6使用class类创建对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Phone2 { constructor (name, price ) { this .name = name; this .price = price; } call ( ) { console .log ('我可以打电话!' ); } } let onePlus = new Phone2 ('一加' , 1999 );onePlus.call (); console .log (onePlus);
继承: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Phone { constructor (brand, price ) { this .brand = brand; this .price = price; }; call ( ) { console .log ('打电话' ); } } class SmartPhone extends Phone { constructor (brand, price, color, size ) { super (brand, price); this .color = color; this .size = size; } photo ( ) { console .log ('拍照' ); } playGame ( ) { console .log ('玩游戏' ); } call ( ) { super .call (); console .log ('视频通话' ); } } const xiaomi = new SmartPhone ('小米' , 1999 , '黑色' , '123' );console .log (xiaomi);xiaomi.call ();
静态成员
静态成员不能被实例化的对象所调用,静态成员在谁身上,谁才可以调用
什么是静态成员,静态成员就是你所创建的构造函数不在内部添加属性或者方法,而是在外部通过调用的方式添加的属性或者方法称为静态成员,例如:
函数其实又叫函数对象,将属性或者方法打点调添加的属性或方法就叫静态成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function Phone ( ) {}; Phone .nam = '手机' ;Phone .call = () => { console .log ('我可以改变世界' ); }; let pho = new Phone ();console .log (pho); Phone .prototype .size = '123cm' ;console .log (pho.size ); Phone .call (); console .log (Phone .nam );
class中添静态成员的方法 使用static fun(){}
在这个类身上添加静态成员,实例对象无法使用
1 2 3 4 5 6 7 8 9 10 class Phone2 { static nam = '手机' ; static change ( ) { console .log ('我可以改变世界' ); } } let pho2 = new Phone2 ();console .log (pho2.nam ); console .log (Phone2 .nam );
Proxy代理器 Proxy介绍
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
Proxy 是一个构造函数可以通过var proxy = new Proxy(target, handler);
实例化对象
target
参数表示所要拦截的目标对象
handler
参数也是一个对象,用来定制拦截行为。
1 2 3 4 5 6 7 8 9 var proxy = new Proxy ({}, { get : function (target, propKey ) { return 35 ; } }); proxy.time proxy.name proxy.title
可以看到当外界想要获取target对象中的属性时,就会触发get拦截
class类中的getter和setter 与 ES5 一样, 在 Class 内部可以使用get和set关键字, 对某个属性设置存值函数和取值函数, 拦截该属性的存取行为
get:当外界读取触发
get
方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
set:当外界修改时触发
set
方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
被检测的属性不必要再放入到constructor再重新改变指向,其属性值是get 后面函数return的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Phone { constructor (age ) { this .age = age; } get name () { console .log ('名字被读取' ); return 'A神' } get price () { console .log ('价格属性被读取' ); return '123' ; } set price (newVal ) { console .log ('价格属性被修改成' + newVal); } } let s = new Phone ('18' );console .log (s);console .log (s.price ); s.price = 'abc' ;
Reflect Reflect介绍
Reflect反射
Reflect
对象与Proxy
对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect
对象的设计目的有这样几个。
静态方法 Reflect
对象一共有 13 个静态方法。
Reflect.get(target,name,receiver)
Reflect.get
方法查找并返回target
对象的name
属性,如果没有该属性,则返回undefined
。
1 2 3 4 5 6 7 `` ` **Reflect.set(target,name,value,receiver)** > ` Reflect .set `方法设置` target`对象的` name`属性等于` value`。 ` `` js
Reflect.deleteProperty(obj,name)
Reflect.deleteProperty
方法等同于delete obj[name]
,用于删除对象的属性。
1 2 3 4 5 6 7 const myObj = { foo : 'bar' };delete myObj.foo ;Reflect .deleteProperty (myObj, 'foo' );
Reflect.defineProperty(target, propertyKey, attributes)
Reflect.defineProperty
方法基本等同于Object.defineProperty
,用来为对象定义属性。未来,后者会被逐渐废除,请从现在开始就使用Reflect.defineProperty
代替它。
1 2 3 4 5 6 7 8 9 10 11 12 13 function MyDate ( ) { } Object .defineProperty (MyDate , 'now' , { value : () => Date .now () }); Reflect .defineProperty (MyDate , 'now' , { value : () => Date .now () });
如果Reflect.defineProperty
的第一个参数不是对象,就会抛出错误,比如Reflect.defineProperty(1, 'foo')
。
Object对象方法扩展 1.Object.is 判断两个值是否完全相等 1 2 3 4 console .log (Object .is (120 , 120 )); console .log (Object .is (120 , '120' )); console .log (Object .is (NaN , NaN )); console .log (NaN === NaN );
2.Object.assign对象的替换/合并
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
Object.assign(‘被替换对象’,’替换对象’) 对象的合并
如果第一个对象有第二个没有的属性,替换之后也同样保留没有的属性
将多个源对象的可枚举属性复制到自己的目标对象中,只会复制一层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const config1 = { host : 'abcdefg' , prot : 3306 , name : 'root' , pass : 'root' , test : 'text' }; const config2 = { host : 'http://' , prot : 33006 , name : 'xxx.com' , pass : 'abc' , test2 : 'text' }; console .log (Object .assign (config1, config2));
3.设置获取原型对象
Object.setPrototypeOf 设置原型对象
Object.setPrototypeOf(obj, prototype)
Object.getPrototypeOf 获取原型对象
1 2 3 4 5 6 7 8 9 10 const school = { name : 'abc' }; const cities = { shuzu : [1 , 2 , 3 ] } Object .setPrototypeOf (school, cities)console .log (school); console .log (Object .getPrototypeOf (school));
4.Object.keys()
ES5 引入了Object.keys
方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
1 2 3 var obj = { foo : 'bar' , baz : 42 };Object .keys (obj)
5.Object.defineProperty方法 参数
属性所在的对象
属性的名字(被监听的属性)
一个描述符对象
属性描述符:
configurable:是否可配置,取值为true时,该属性能够从对象删除,可以修改该属性的描述符
enumerable:是否可枚举,取值为true可枚举,可以通过for-in遍历
value:值,数据描述符有value,存取描述符没有value
witable:是否可修改,取值true,该属性取值可以修改
存取描述符:
get():在读取属性时调用的函数,默认值是undefined
set():在写入属性的时候调用的函数,默认值是undefined
模拟Vue中v-module数据双向绑定原理
通过对数据的监听,当数据改变进行拦截,触发set() 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> <meta http-equiv ="X-UA-Compatible" content ="ie=edge" /> <title > Document</title > <style > #myInput { width : 400px ; height : 50px ; font-size : 40px ; color : red; } #contain { margin-top : 20px ; width : 400px ; height : 200px ; border : 1px solid salmon; } </style > </head > <body > <input id ="myInput" type ="text" /> <div id ="contain" > </div > <script > var text; window .data = {}; var oIn = document .getElementById ("myInput" ); var oDiv = document .getElementById ("contain" ); oIn.addEventListener ("input" , function (e ) { text = e.target .value ; console .log (text); window .data .value = text; }); Object .defineProperty (window .data , "value" , { get ( ) { return "" ; }, set (v ) { oDiv.innerHTML = v; }, }); </script > </body > </html >
数值的扩展 1.Number.EPSILON
EPSILON属性的值接近于2.2204460492503130808472633361816E-16
Number.EPSILON 是JavaScript 表示的最小精度
由于js的计算机制,0.1+0.2=0.30000000000000004
封装一个判断结果的函数
1 2 3 4 5 6 7 8 9 function equal (a, b ) { if (Math .abs (a - b) < Number .EPSILON ) { return true ; } else { return false ; } } console .log (equal (0.1 + 0.2 , 0.3 ));
2.Number.isFinite
Number.isFinite 检测一个数值 是否为有限数
1 2 3 console .log (Number .isFinite (100 )); console .log (Number .isFinite (100 / 0 )); console .log (Number .isFinite (Infinity ));
3.Number.isNaN
Number.isNaN 检测一个数值是否为NaN
1 console .log (Number .isNaN (123 ));
4.Number.parseInt()/Number.parseFloat()
Number.parseInt() Number.parseFloat()字符串转整数
1 2 console .log (Number .parseInt ('521.145abx' )); console .log (Number .parseFloat ('521.145abx' ));
5.Number.isInteger()
Number.isInteger()判断一个属是否为整数
1 2 console .log (Number .isInteger (5 )); console .log (Number .isInteger (2.5 ));
6.Math.trunc()
Math.trunc()将数字的小数部分抹掉
1 console .log (Math .trunc (2.5 ));
7.Math.sign()
Math.sign() 判断一个数到底为正数 负数 零
1 2 3 console .log (Math .sign (100 )); console .log (Math .sign (-100 )); console .log (Math .sign (0 ));
模块化 暴露数据 1.分别暴露 1 2 3 4 5 6 7 export let school = 'abc' ;export function call ( ) { console .log ('123456789' ); }
2.统一暴露 1 2 3 4 5 6 7 let school = 'def' ;function call ( ) { console .log ('987654321' ); } export { school, call };
3.默认暴露 1 2 3 4 5 6 7 8 export default { school : '默认暴露' , change ( ) { console .log ('默认暴露' ); } }
引入数据 1.通用的引入方式
通用引入是对导入的文件进行赋值,当你需要调用的时候直接通过js1.
调用你想要的属性或者方法
1 2 3 4 5 6 7 8 9 <script type ="module" > import * as js1 from "./36-js模块.js" ; console .log (js1.school ); console .log (js1); import * as js2 from "./36-js模块2.js" ; js2.call (); console .log (js2); </script >
2.解构赋值形式导入或重命名
对接收的数据进行重命名,这时候你只需要使用你as后的名字即可使用导入的变量或方法
1 2 3 4 5 6 7 8 9 <script type ="module" > import {school,call} from "./36-js模块.js" ; import {school as school2,call as call2} from "./36-js模块2.js" ; console .log (school); console .log (school2); console .log (call); console .log (call2); </script >
3.默认暴露的接收
也可以不用对其重命名,那这样的话就相当于是第四个方法接收
1 2 3 4 5 6 7 8 9 <script type="module" > import {default as def} from "./36-js模块3.js" ; console .log (def.school ); console .log (def.change ); </script>
4.简便形式接收
简便形式 只针对默认暴露
1 2 3 4 <script > import m3 from "./36-js模块3.js" ; console .log (m3); </script >