ES6

135 阅读4分钟
作用域:
1、对于作用域链我们直接通过创建态来定位作用域链
2、手动取消全局,使用块级作用域
if(true){
    let e = 111;
    console.log(e);
}

this 上下文:
    是执行时动态读取上下文决定的,而不是创建时
this指向
1、函数直接调用中,this指向的是window(包含函数表达式,匿名函数,嵌套函数,setTimeout)
2、隐式绑定 this的指向是调用堆栈的上一级
    1)在执行函数时,函数被上一级调用,上下文指向上一级
    2)或直接成公共函数,则指向window
3、显示绑定 call/apply/bind 改变this指向
4、dom监听函数中指的是dom元素实例
5、箭头函数
。。。。
**this指向的是new之后得到的实例**

如何突破作用域限制->闭包
闭包:能够读取其他函数中变量的函数;(一个函数和他周围状态的引用捆绑在一起的组合)
1、函数作为返回值
2、函数作为参数
3、函数嵌套
#### 拆分执行
function createIncrement() {
        let count = 0;
        
        function increment() {
            count++;
        }

        let message = `count is ${count}`;

        function log() {
            console.log(message);
        }

        return [increment, log];
    }
    const [increment, log] = createIncrement();// 解构

    increment();
    increment();
    increment();
    log();
    #### 实现私有变量
    function createStack() {
        const items = [];
        return {
            push(item) {
                items.push(item);
            }
        }
    }
    
new原理(构建原型链)
1.创建一个对象,并返回对象实例
2.将返回的对象的原型指向构造函数的prototype对象
3.将当前对象实例赋给this
4.执行构造函数初始化,假如构造函数执行返回对象则返回这个对象,否则返回对象实例
function myNew(fnName){
    var obj = {};
    obj.__proto__ = fnName.prototype;
    var result = fnName.call(obj);
    return typeof result === 'object' ? result: obj;
}

//call/apply 函数实现原理
Function.prototype.myCall(context){
    var context = context || window;
    context.fn = this;
    var result = context.fn(context.splice(0,1));
    delete context.fn;
    return result
}
//bind 函数实现原理
Function.prototype.myBind(context){
    var _this = this;
    return function(){
        return _this.mycall(context);
    }
}

原型链图: 原型链.png

OOP面向对象
对象是对于单个物体的抽象,是一个容器,封装了属性(对象的状态)和方法(对象的行为);
类是对象模版即表征了一类物体的共同特征,从而生成对象;
js其实本质上并不是基于类,而是基于构造函数 + 原型链(constructor + prototype)
eg:
function Course(teacher){
    this.teacher = theacher;
    this.leader = 'bubu';
    this.startCourse = function(name){
        return `上课-${name}`;
    }
}
Course本质就是构造函数(调用执行,或new 执行)不初始化无法使用
1、函数体内使用的this,指向所要生成的实例
2、生成对象用new来进行实例化
3、可以做初始化传参
function Course(theacher){ //防止call/bind/apply 强改this
    let _isClass = this instanceof Course;
    if(!_isClass){
        return new Course();
    }
    
    this.teacher = theacher;
    this.leader = 'bubu';
    this.startCourse = function(name){
        return `上课-${name}`;
    }
}
const course = new Course('yy');
启发:如果编写底层的api代码时,尽量做到不需要让外部感知内部类型

构造函数:用来初始化创建对象的函数-Course
**会自动给构造函数赋予一个属性prototype,该属性等于实例对象的原型对象;
**不足:构造函数中的方法是会存在在每个实例中,重复挂载会导致资源浪费。

原型对象 Course.prototype

实例对象:course是实例对象,根据原型对象创建出来的实例
**每个实例对象中都有一个__proto__
**每个实例对象中都有一个constructor
constructor:每个对象在创建时,会自动拥有一个构造函数属性constructor,其继承自原型对象,是指向了构造函数的引用。

继承:
原型链继承:
function Parent(){
    this.name ='name';
    this.age = 10;
    this.eat= function(){
        console.log(`${this.name} eat 10 mantou`);
    }
}
function Child(){
    
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const c = new Child();
c.eat();
// 缺点: 无法传参
构造函数继承:
function Parent(name){
    this.name =name;
    this.age = 10;
    this.eat= function(){
        console.log(`${this.name} eat 10 mantou`);
    }
}
function Child(name){
    Parent.call(this, name);
}
const c = new Child();
c.eat();
// 缺点:方法会在每一个实例中创建一份,资源浪费
组合继承:
function Parent(name){
    this.name =name;
    this.age = 10;
}
Parent.prototype.eat= function(){
    console.log(`${this.name} eat 10 mantou`);
}
    
function Child(name){
    Parent.call(this, name);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const c = new Child();
c.eat();
// 初始化了两遍父类函数,资源浪费
寄生组合继承:
function Parent(name){
    this.name =name;
    this.age = 10;
}
Parent.prototype.eat= function(){
    console.log(`${this.name} eat 10 mantou`);
}
    
function Child(name){
    Parent.call(this, name);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const c = new Child();
c.eat();
多继承:
function Parent(name){
    this.name =name;
    this.age = 10;
}
Parent.prototype.eat= function(){
    console.log(`${this.name} eat 10 mantou`);
}

function Parent2(name){
    this.shop =name;
}
Parent2.prototype.speaking= function(){
    console.log(`${this.name} eat 10 mantou`);
}
    
function Child(arg){
    Parent.call(this, arg);
    Parent2.call(this, arg);
}
Child.prototype = Object.create(Parent.prototype);
Object.assign(Child.prototype, Parent2.prototype);
Child.prototype.constructor = Child;
const c = new Child();
c.eat();
c.speaking();


es6:
1)const/let
    1、不允许重复声明,const不允许重复赋值
    2、无变量提升
    3、块级作用域
    4、dead zone 暂时性死区(在声明前使用)
    5、引用型const,变量型let
2)class
    1class的类型是function
    2class的prototype  有区分但本质相同
    3class&函数对象 属性 
    4、course.hasOwnProperty('teacher');
    5、set/get
    6extends / super
    7、静态方法 Course.ring
    8、私有属性三种方式
        1)construtor中变量名与参数名不同 _teacher
        2)在constructor外部建立变量
    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) {
            // 留有空间
            this.teacher = val;
        }
    }
3)箭头函数
    1、 箭头函数并不会形成独立上下文,内部this指向了window或箭头函数的上一级
    2、箭头函数无法成为完整构造类
    3、无法构造原型对象的方法
    4、无法使用arguments参数
4)解构
    1、形参解构
    2、变量初始值
    3、json
    4、ajax返回值
    5、函数返回值
    6、变量交换  [b, a] = [a, b];
    const zhaowa = {
        teacher: {
            name: '',
            age: 30
        },
        leader: '',
        name: 'es6'
    }

    // 别名
    const {
        teacher: {
            name,
            age
        },
        leader,
        name: className
    } = zhaowa;



函数式编程
/**
 * 函数式编程
 * 1.vue3 react16.8以上 全面化函数式推动
 * 2.函数式编程可以使得代码单元相对更加独立 - tree shaking过程更顺畅,更方便做UT
 * 3.减少了对this的依赖,减轻了开放人员对指向问题的困惑
 * 4. js天生友好函数式: ramda、loadsh
 * 概念:
 * 1.一种抽象运算过程
 * 2.函数式的函数并非对于过程运算,函数的映射
 * 3.幂等- 相同的输入始终得到相同的输出
 * 纯函数 
 */
let arr = [1, 2, 3, 4, 5];
arr.slice(0, 3);// 1, 2, 3
arr.slice(0, 3); // 1, 2, 3
arr.splice(0, 3); // 1, 2, 3
arr.splice(0, 3); // 4, 5
/**
 * 对于系统的改造
 */
// 不纯函数(依赖外部变量)=》换了环境就不对了, 面向过程
let min = 18;
let limit = age =>age > min;

// 纯纯的
let limit2 = age => age > 18;
/**
 * 对于大型系统,对于外部状态的依赖,会大大的提高系统的复杂性
 * 问题:
 * 18被硬编码到了函数内部,造成了功能拓展的局限
 * 高阶函数HOC
 * 定义:
 * 1.函数作为参数被传递到了另一个函数中,
 * 2.函数作为返回值被另一个函数返回
 */
let fn = arg =>{
    let outer = "outer";
    let innerFn = ()=>{
        console.log(outer);
        console.log(arg);
    }

    return innerFn;
}
let closure = fn(18);
// 闭包

/**
 * 函数柯里化
 * 传递给函数一部分参数用于功能调用, 让他返回一个函数去处理剩下的参数
 */
let add = (x, y) => x +y;
let add2 = x=> (y => x+y);
// 柯里化
console.log(add2(2));//[Function (anonymous)]
console.log(add2(200));// [Function (anonymous)]

let limit3 = y => ( x => x > y )
let limit18 = limit3(18);
console.log(limit18(20));
// 柯里化 是一种预加载方式
/**
 * 问题:包心菜产生
 */
//  h(g(y(x)))
/**
 * 组合
 * 通过更优雅的方式实现纯函数的解耦
 */
let compose = (f, g) => ( x => f(g(x)));
let add1 = x => x+1;
let mul5 = y => y*5;
console.log(compose(add1, mul5)(2));// 11
console.log(compose(mul5, add1)(2));// 15
// 面试题- 数组长度未知,拿到最后一项
let first = arr => arr[0];
let reverse = arr => arr.reverse();
let last = compose(first, reverse);
console.log(last([1, 2, 3, 4])); // 4

es6:Object常用放过
Object.freeze();// 只冻结对象的根元素 
Object.create();//
Object.keys();
Object.assign();
Object.values();?
Object.is();