课程目标
- ES6新增API
- 面试点
- babel
知识点
- 前身今世
- API
前身今世
ES6一般是指ES2015也是泛指下一代JavaScript语言。
- 历史
- ECMAScript1.0是1997年发布的
- 中间发过多次版本
- 2015年6月,ECMAScript6正式通过。
- 目标 使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言
ECMAScript和JavaScript的关系
ECMAScript是JavaScript的规格,JavaScript是ECMAScript的实现,在日常场合中,两者是可以互换的
ES6和ECMAScript2015的关系
ES6的第一个版本在2015年6月发布,正式名称是《ECMAScript2015标准》,新版本会在每年的6月份发布,不需要以前的版本号,只要用年份标记就可以。以此类推。ES6也是一个泛指,含义是5.1版以后的JavaScript的下一代标准,涵盖了ES2015,ES2016,ES2017等。
API
1、常量
const,常量不可修改,不可重复定义
- ES5怎么定义一个常量
Object.defineProperty(window,'arg',{
value: 'A',
writable: false
})
2、块极作用域
比如下面一段代码:
if(true){
var arg = 'a'
}
console.info('arg',arg);//a
if(true){
const arg1 = 'b'
}
console.info('arg1',arg1);//报错
arg变量能正常输入是因为var定义。相当于调用window.a,var定义的变量存在变量提升,可以随意修改,不受作用域限制。const是在if作用块里内定义的,外部是调用不到的,无变量提升--先声明再使用。const不在window中。
3、dead zone(死锁)
如果在var申明变量前调用,输出的值就是undefined,如果是const申请之前调用就报错。
if(true){
console.info('arg',arg);//undefined
var arg = 'a'
}
4、let or const
- 引用型
const
const obj = {
a : 'a',
b : 'b'
}
obj.a = 'c'//没问题不会报错
如果把引用类型改成常量,不可修改
const obj = {
a : 'a',
b : 'b'
c : ['fr','ee','ze']
}
obj.a = 'c'//没问题不会报错
//冻结
object.freeze(obj);
obj.c[0] = 'success'//也是可能修改的
//freeze只能冻结第一层,如果下级是引用类型的话,还是可以修改的。
//解决方法:嵌套引用类型需要遍历递归
function deepFreeze(){
//2.确定主执行步骤
Object.freeze(obj);
//3.逐级深入
//如果用for in 来解决要判断hasOwnProperty 两个要成双成对出现
(Object.keys(obj) || []).forEach(key => {
let innerObj = obj[key]
if(typeof innerObj === 'object'){
//1.递归模式确定
deepFreeze(innerObj)
}
});
}
//lodash: clone deepclone equal deepequal
5、箭头函数
先看一下传统函数:
function test(a,b){
return a + b;
}
const test1 = function(a,b){
return a + b
}
箭头函数:
const test3 = (a,b) => {
return a + b;
}
//简写
const test3 = (a,b) => a + b;
const test4 = x => {
//content
}
const test5 = () => {
//content
}
上下文
//es6 context
const obj = {
teacher: '小张老师',
leader: '小王老师',
getTeacher: function(){
console.info(this.teacher);
return this.teacher
},
getLeader: () => {
console.info(this.leader);
return this.leader
}
}
obj.getTeacher()//小张老师
obj.getLeader()//undefined
上下文形成的,箭头函数并不会形成独立上下文,内部this指向了window
- 场景一:dom操作callback时
const btn = document.querySelector('#btn');
btn.addEventListener('click', function(){
this.style.color = '#fff'
});
- 场景二: 类操作 箭头函数无法成为完整构造类
function obj1(teacher, leader){
this.teacher = teacher;
this.leader = leader;
}
const obj1 = (teacher, leader) => {
this.teacher = teacher;
thid.leader = leader;
}
//传统函数是能正常获取参数的
//箭头函数报错提示没有constructor
const o1 = new obj1('小王', '小张');
console.info(o1);
- 场景三: 箭头函数无法构造原型方法
function obj1(teacher, leader){
this.teacher = teacher;
this.leader = leader;
}
//传统函数和箭头函数
obj1.prototype.learn = function(){
console.info(this.teacher,this.leader);
}
obj1.prototype.learn = () => {
//this 指向window
console.info(this.teacher,this.leader);
}
const o1 = new obj1('小王', '小张');
//传统函数没问题 箭头函数undefined
console.info(o1.learn);
- 箭头函数的参数特性
const test = function(teacher){
console.info(arguments);
}
const test = teacher => {
console.info(arguments);
}
传统函数输出结果:
箭头函数输出结果:
- 箭头函数不能用call
6.class
class助力Js更面向对象。- 类
//传统对象
function Course(teacher, course){
this.teacher = teacher;
this.course = course;
}
Course.prototype.getCourse = function(){
reutrn `teacher is:${this.teacher}, course:${this.course}`
}
const course = new Course("小王老师","ES6");
course.getCourse();
//es6
class Course {
//构造方法。实例时会默认执行
constructor(teacher, course){
this.teacher = teacher;
this.course = course;
}
getCourse() {
reutrn `teacher is:${this.teacher}, course:${this.course}`
}
}
const course = new Course("小王老师","ES6");
course.getCourse();
- class 的类型是:
console.info(typeOf Course);//function
- class的prototype是什么
console.info(Course.prototype);//两种方式都挂载了prototype上。有区分,但本质类型相同
class的prototype输出:
functioin的protype输出:
- class & 函数对象 属性 :相同
console.info(course.hasOwnProperty('teacher'));//
- 属性定义: 构造函数 & 顶层定义。两种定义方式
class Course {
//构造方法。实例时会默认执行
constructor(teacher, course){
this.teacher = teacher;
this.course = course;
}
getCourse() {
reutrn `teacher is:${this.teacher}, course:${this.course}`
}
set teacher(val) {
//留有空间
this.teacher = val
}
get teacher() {
//留有空间
return `teacher is:${this.teacher}`
}
}
修改只读变量,会报错吗?--无法改变但不会报错
- js如何建立一个私有属性
//方法一
class Course {
//构造方法。实例时会默认执行
constructor(teacher, course){
this._teacher = teacher;
let _course = 'es6';
this.getCourse = () => {
return _course;
}
}
}
//方法二
class Course {
#course = 'es6'
//构造方法。实例时会默认执行
constructor(teacher, course){
this._teacher = teacher;
}
get course(){
return this.#course;
}
set course(val){
if(val){
this.#course = val;
}
}
}
- 封闭核心 - 适配器模式
class Utils {
constructor(core){
this._main = core;
this._name = 'my-utils'
}
//fullName:{firstname:'',name:''}
get name() {
return {
...this._main.fullName,
...{
name: `${this._name}`
}
}
}
set name(val){
this._name = val;
}
}
- static 静态方法 - 直接挂载在类上的方法无需实例化获取
//es5
function Course() {
//...
}
Couse.ring = function() {
//...
}
//es6
class Course {
constructor() {
//...
}
static ring() {
//...
}
}
Course.ring();//解决全局对象变量问题,所有的实例共用一个方法或属性
- 继承
//es5
function Course() {
//...
}
Couse.ring = function() {
//...
}
Course.prototype.send = function(){
//...
}
function Child() {
Course.call(this,'小王',‘小张’);
this.run = function() {
//...
}
}
Child.prototype = Course.prototype;
//es6
class Course {
constructor() {
//...
}
static ring() {
//...
}
send() {}
}
//工厂模式
class Child extends Course{
constructor() {
//初始化
super('小王',‘小张’);
}
static run() {
//...
}
}
7.解构
const msg = {
teacher: {
name:'',
age:18
},
leader:'',
name:'es6'
}
//别名
const {
teacher: {
name:'',
age:18
},
leader,
name:className
} = msg
- 解构使用场景
- 形参结构
const sum = arr => {
let res = 0;
arr.forEach(each => {
res += each;
})
}
const sum = ([a,b,c]) => {
return a + b +c;
}
- 解构初始值
const course = ({ teacher, leader, course = 'msg'}) => {
//...
}
course({
teacher: '小王',
leader: '小张'
})
- 返回值
const getCourse = () => {
return {
teacher: '',
leader:''
}
}
const {teacher, leader} = getCourse();
- 变量交换
let a = 1;
let b = 2;
[b, a] = [a, b]
- json处理
const json = '{"teacher": '小王',“leader”: '小张'}'
const obj = JSON.parse(json);
const {
teacher,
leader
} = JSON.parse(json);
- ajax
const {
code,
data,
msg
} = response;
8.Proxy
为什么需要Proxy?
- 被代理的对象不想直接被访问。
- 控制和修改被代理对象的行为(调用属性、属性赋值、方法调用等),使之可以进行访问控制和增加功能
- 参考网站
Proxy函数用于创建Proxy对象,也可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问都必须选通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。 - 语法:
new Proxy(target, handler)
- 参数:
- target:原对象,目标对象,它可以是任何类型的对象,包括原生数组、函数、甚至是另一个代 理。要为其创建代理的对象。
- handler:一个对象,是定义代理的自定义行为的对象。
handler API:
handler的作用就是用来自定义代理对象的各种可代理操作,本身一共有13种方法。
handler.getPrototypeOf();
在读取代理对象的原型时触发该操作,比如在执行Object.getPrototypeOf(proxy)时;
handler.setPrototypeOf();
在设置代理对象的原型时触发该操作,比如在执行Object.setPrototypeOf(proxy, null)时;
handler.isExtensible();
在判断一个代理对象是否是可扩展时触发该操作,比如在执行Object.isExtensible(proxy)时;
handler.preventExtensions();
在让一个代理对象不可扩展时触发该操作,比如在执行Object.preventExtensions(proxy)时;
handle.getOwnPropertyDescriptor();
在获取代理对象某个属性的属性描述时触发该操作,比如在执行 Object.getOwnPropertyDescriptor(proxy)时;
handler.defineProperty();
在定义代理对象某个属性时的属性描述时触发该操作,比如在执行 Object.defineProperty(proxy, "foo", {})时;
handler.has();
在判断代理对象的某个属性时触发该操作,比如在执行“foo” in proxy时;
handler.get();
在读取代理对象的某个属性时触发该操作,比如在执行proxy.foo时;
handler.set();
在给代理对象的某个属性赋值进触发该操作,比如在执行proxy.foo = 1时;
handler.deleteProperty();
在删除代理对象的某个属性时触发该操作,比如在执行delect proxy.foo时;
handler.ownKeys();
在获取代理对象的所有属性键时触发该操作,比如在执行Object.getOwnPropertyNames(proxy) 时;
handler.apple()
在调用一个目标对象为函数的代理对象时触发该操作,比如在执行 proxy()时;
handle.construct()
在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy()时;
应用场景
- 私有变量不能修改
let target = {
name: 'poetries',
_age: 22
}
let logHandler = {
get: function(target,key){
// startsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“开头”的,根据判
//断结果返回 true 或 false。
// 参数:
// str.startsWith(searchString [, position]);
// searchString
// 要搜索的子字符串。
// position
// 在 str 中搜索 searchString 的开始位置,默认值为 0,也就是真正的字符串开头处。
if(key.startsWith('_')){
console.log('私有变量age不能被访问')
return false
}
return target[key];
},
set: function(target, key, value) {
if(key.startsWith('_')){
console.log('私有变量age不能被修改')
return false
}
target[key] = value;
}
}
let targetWithLog = new Proxy(target, logHandler);
// 私有变量age不能被访问
targetWithLog._age;
// 私有变量age不能被修改
targetWithLog._age = 'others';
- 中断代理
Proxy支持随时取消对target的代理,这一操作常用于完全封闭对数据或接口的访问。使用Proxy.revocable方法可以用来创建一个可撤销的代理对象, Proxy.revocable语法:
Proxy.revocable(target, handler)
返回值:
返回一个包含了代码对象本身和它的撤销方法的可撤销Proxy对象,是一个对象,其结构为 {"proxy": proxy, "revoke": revoke}
proxy:表示新生成的代理对象本身,跟一般方法new Proxy创建的代理对象没什么不同,只是它可以被撤销掉。revoke:撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象。- 一旦某个代理对象被撤销,它将变得几乎完全不可调用,在它身上执行任何的可代理操作都会抛出。
TypeError异常(执行可代理操作有14种,除以外的操作不会抛出异常),一旦被撤销,这个代理对象便不可能被直接被恢复到原来的状态,同时和它关联的目标对象以及处理器对象都有可能被垃圾回收掉,再次调用撤销方法revoke()则不会有任何效果,但也不会报错。
let obj = Proxy.revocable({},{
get(target, name) {
return "[["+ name+"]]";
}
});
let proxy = obj.proxy;
proxy.foo;
obj.revoke();
console.info(proxy.foo); //TypeError
proxy.foo = 1; //TypeError
delete proxy.foo //TypeError
typeof proxy //object,因为typeof不属于可代理操作
- 有代理的情况下:
- 撤销代理后: