背景
ECMA - 瑞士标准协会,负责执行国际标准 JS - Netscape 和 Sun Microsystem 创建并提交,取名ECMAScript ES6 === 官方命名 - ES2015
ES6 主要API Const - 常量 以及 块级作用域 Arrow Function - 箭头函数 Class 类 Deconstruction - 解构 Array for of 数组操作 ...
const 常量标识
常量一般使用大写字母
const LIMIT = 10;
// 正反翻译机
const OBJ_MAP = {
a: 'A',
A: 'a'
}
// 5 => VI
// VI => 5
const QUEUE = [1, 2, 3, 4, 5]
1. 不允许重复声明赋值
// 变量重新赋值
var arg1 = '云隐';
arg1 = '小可';
面试:不引入polyfill 如何实现常量声明???
// 常量
// ES5
Object.defineProperty(window, 'arg2', {
value: '云隐',
writable: false
})
arg2 = '小可';
// ES6
const arg3 = '云隐';
arg3 = '小可';
// TypeError
// const 不允许重复声明
const arg3 = '云隐';
const arg3 = '小可';
// SyntaxError
2. 块级作用域
面试题:以下代码输出什么? 变量提升 + 块级作用域
if (true) {
var arg1 = '云隐'
}
console.log('arg1:' arg1)
if (true) {
const arg2 = '云隐'
}
console.log('arg2:' arg2)
3. 无变量提升
console.log(arg1);
var arg1 = '云隐';
// 相当于
var arg;
console.log(arg1);
var arg1 = '云隐';
// 无边变量提升 - 先声明再使用
console.log(arg2);
const arg2 = '云隐'; // not defined
window: 浏览器视窗,整个上下文的最外层
// 变量是否在root
var arg1 = '云隐';
console.log(window.arg1); // 云隐
面试题:const 在不在 window 中?
不在!!!不会在全局下
const arg1 = '云隐';
console.log(window.arg1); // undefined
4. dead zone
if (true) {
console.log(arg1);
var arg1 = '云隐'
}
// undefined
if (true) {
console.log(arg1);
const arg1 = '云隐'
}
// Cannot access 'arg1' before initialization
5. let or const
- 只用是引用型的 const ->
const obj = {
teacher: '云隐',
leader: '小可'
}
obj.leader = '部部';
const arr = ['云隐', '小可'];
arr[0] = 'aaa';
为什么不报错?
引用类型的原理 - 指向地址,只要地址没有改变,那么就不会报错 追问: 如果要将对象变得不可改 破局 - object.freeze()
object.freeze(obj);
const obj2 = {
teacher: '云隐',
leader: '小可',
zhuawa: ['部部', '小样']
}
object.freeze(obj2);
obj2.zhuawa[0] = '云隐';
// freeze 只能冻结根层,嵌套引用类型需要遍历递归
实现一个深冻结 // forEach 会常出现false 因此 Object.keys(obj) || [] // for in 经常会判断 Object.hasOwnProperty
function deepFreeze() {
// 2. 确定主执行步骤
Object.freeze(obj);
// 3. 逐级深入
(Object.keys(obj) || []).forEach(key => {
let innerObj = obj[key]
if (typeof innerObj === 'object') {
// 1. 递归模式确定
deepFreeze(innerObj);
}
})
}
手写lodash:clone deepClone equal deepEqual
箭头函数
// 传统函数
function text(a, b) {
return a + b;
}
const test2 = function (a, b) {
return a + b;
}
箭头函数
// ES6
const test3 = (a, b) => {
return a + b;
}
const test3 = (a, b) => a + b
const test4 = x => {
// content...
}
const test5 = () => {
// ....
}
上下文 this
// ES6 context
const bj2 = {
teacher: '云隐',
leader: '小可',
zhuawa: ['部部', '小样'],
getTeacher: function() {
return this.teacher;
},
getLeader: () => {
return this.leader;
}
}
追问:上下文形成的原因
箭头函数并不会形成独立上下文,内部this指向 window
场景
场景1: dom 操作 cb 时
const btn = document.querySelector('#btn');
btn.addEventListener('click', function() {
this.style.color = '#fff';
})
// this 指向的当前对象,指向btn
场景2:类操作
箭头函数无法成为完整构造类,无法生成实例
function Obj(teacher, leader) {
this.teacher = teacher;
this.leader = leader;
}
const Obj1 = (teacher, leader) => {
this.teacher = teacher;
this.leader = leader;
}
// 实例验证
const o1 = new Obj('云隐', '小可');
console.log(o1);
// Obj {teacher: '云隐', leader: '小可'}
const o2 = new Obj1('部部', '小可');
console.log(o2)
// Obj1 is not a constructor
追问: 箭头函数无法构造原型方法
Obj.prototype.learn = function() {
console.log(this.teacher)
}
o1.learn()
// 云隐 小可
Obj.prototype.learn = () => {
console.log(this.teacher, this.leader);
}
o1.learn()
// undefined undefined
本质:this指向 window
箭头函数的参数特性 - 无法使用arguments
const test = function (teacher) {
console.log(arguments);
}
// arguments 参数 teacher
const test = teacher => {
console.log(arguments);
}
// ReferenceError: arguments is not defined
Class
class 助力 js 更面向对象 - 类
// 传统对象 - function 面向过程
// 利用 prototype 来进行方法拓展
function Course(teacher, course) {
this.teacher = teacher;
this.course = course;
}
Course.prototype.getCourse = function () {
return `teacher is ${this.teacher}, course: ${this.course}`
}
const course = new Course('云隐', 'ES6');
course.getCourse();
// teacher is 云隐, course: ES6
// ES6 每个业务逻辑面向对象
class Course {
// init 实例会默认执行
constructor(teacher, course) {
this.teacher = teacher;
this.course = course;
}
// 拓展方法
getCourse() {
return `teacher is ${this.teacher}, course: ${this.course}`
}
}
const course = new Course('云隐', 'ES6');
course.getCourse();
追问
class 的类型是什么?
console.log(typeof Course);
// function
是function!!!!!!会被bable 翻译成 传统方式的 function
class 的原型是什么?
console.log(Course.prototype)
// 有区分,但是本质类型相同 ??
class & 函数对象 的属性 有什么不同?
console.log(course.hasOwnProperty('teacher'))
// 不论是类 还是 函数对象,属性都会挂在原型上
属性定义 构造器 & 顶层定义 两种方式
class Course {
// init 实例会默认执行
constructor(teacher, course) {
this.teacher = teacher;
this.course = course;
}
// 拓展方法
getCourse() {
return `teacher is ${this.teacher}, course: ${this.course}`
}
get teacher() {
// 留有空间
return this.teacher;
}
set teacher(val) {
// 留有空间
console.log('检测到setter')
this.teacher = val;
}
}
// 写constructor 和 static 以及set get 变量 意义何在?
// 1. js 如何建立只读变量
constructor(teacher, course) {
this._teacher = teacher;
this.course = course;
}
get teacher() {
return this._teacher;
}
// 修改只读变量,会报错吗? -- 无法改变,也不会报错
// 2. js 如何建立一个似有属性
class Course {
constructor(teacher, course) {
this.teacher = teacher;
// 在 constructor作用域内定义一个局部变量
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;
}
}
}
// 3. 封装核心 - 适配器模式
// 底层封装中台业务core
class utils {
constructor(core) {
this._main = core;
this.name = 'my-utils';
}
get name() {
return {
...this._main.name,
name: `utils is ${this._name}`
}
}
set name(val) {
// valid safety
this._name = val;
}
}
class 静态方法 - 直接挂载在类上的方法 无需实例化获取
// ES5
function Course() {
// ...
}
Course.ring = function() {
// ... about ring
}
// 执行
Course.ring();
// ES6
class Course {
constructor () {
// ...
}
static ring () {
// ...
}
}
// 执行
Course.ring();
// 通常用来解决全局变量问题,所有的实例共用一个方法
继承
// ES5 继承
function Course() {
// ...
}
Course.ring = function() {
// ... about ring
}
Course.prototype.send = function () {
// ...
}
function Child() {
Course.call(this, '云隐', 'ES6');
// 子类方法
this.run = function () {
// ...
}
}
// 继承:原型链相连
Child.prototype = Course.prototype;
// ES6
class Course {
constructor () {
// ...
}
static ring() {}
send() {}
}
// 继承 => 工厂模式:原型车被所有的其他车型作为继承进行生产
class Child extends Course {
constructor() {
super('云隐', 'ES6'); // 继承父的初始化
}
run () {}
}
解构 deconstruction
const zhaowa = {
teacher: {
name: '小可',
age: 30
},
leader: '部部',
name: 'es6'
}
// 别名
const {
teacher: {
name,
age
},
leader,
name: className
} = zhaowa;
追问:解构使用场景
形参解构
const sum = arr => {
let res = 0;
arr.forEach(each => {
res += each;
})
}
// ES6
const sum = ([a, b, c]) => {
return a + b + c;
}
结合初始值
const course = ({ teacher, leader, course = 'zhoawa'}) => {
// ...
}
course({
teacher: 'yy',
leader: '小可'
})
返回值
const getCourse = () => {
return {
teacher: '',
leader: ''
}
}
const { teacher, leader } = getCourse();
变量交换
let a = 1;
let b = 2;
[b, a] = [a, b];
json 处理
const json = '{"teacher": "yy", "leader": "xk"}';
const obj = JSON.parse(json);
const {
teacher,
leader
} = JSON.parse(json)
ajax 处理
const {
code,
data,
msg
} = response;