探索ES6类构造器中创建原型同名变量的原理

2020/4/1 11:01:35

本文主要是介绍探索ES6类构造器中创建原型同名变量的原理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

2020/03/25

我开始学习react

当看到react在创建组件时this绑定问题的时候起了疑问: ES6中class中所写的方法默认时在原型对象上的 当时我使用ES5的思维:

function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function(){
    return this.name;
};
复制代码

在ES6中使用class

class Person{
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    //1. sayName = function(){} 是创建实例方法
    //2. static sayName(){}     是创建静态方法 Person.sayName
    //3. sayName(){}            是创建原型方法
    sayName(){
        return this.name;
    }
}
复制代码

我在react创建组件绑定事件的过程中看到了如下代码,满脸问号ES6的class不只是一个语法糖么?

class Person{
    state = {};//创建state
    constructor(props){
        this.handleClick = this.handleClick.bind(this);//这是啥?我有这属性?
    }
    handleClick(e){
        //doSomething
    }
    render(){/* return (<div>...</div>) */}
}
复制代码

What? && Why?

this.handleClick = this.handleClick.bind(this);

既然问题迎面而来,那么接下来开始分析之旅吧?

1.通过Chrome的F12我了解到是原型上和实例上都是有一个同名属性

2.原型上的属性是普通的方法,实例(obj)上的属性是此方法调用Function.prototype.bind(obj)返回的bound

那么什么是bound呢? 如果你对bound了解了,那么可以尝试看一下github上我写了一个bind的实现原理

//New的实现
    function New(constructor, ...args) {
        let o = {};
        constructor = typeof constructor === 'function' ? constructor : function() {};
        o.__proto__ = constructor.prototype; //这一步骤决定最后打印出来的o这个对象的类型
        let returnVal = constructor.Apply(o, args); //至于打印出来的对象标识 取决于 原型上的constructor属性指向的函数名字,而这个name属性是无法改变的
        //这里的returnVal会一直被引用,这里只是一个概念,应该通过参数传递,这里使用之后赋值null
        return returnVal && typeof returnVal === 'object'? returnVal : o;
    }

    //Call的实现
    Function.prototype.Call = function(that) {
        //这里需要一个不会重复的键值作为{key:value}中的key
        const fn = Symbol('fn'); //如果不是用ES6中的Symbol 自己创建一个(Math.random() + new Date().getTime()).toString(32).slice(8)
        //这里将除that外的参数列表转换为数组,以为arguments中有Symbol.iterator所以可以使用of和...这两个操作
        //其实还可以使用 Array.prototype.slice.call(arguments).slice(1);
        const args = [...arguments].slice(1);
        //如果that 为 undefined/null 这时this指向window
        that = that || window;
        //将独一无二的key 配上 value:Call的调用函数
        that[fn] = this;
        //执行这个函数,并将结果在最后返回
        const res = that[fn](...args);
        //不要忘记删除这个属性
        delete that[fn];
        return res;
    };
    //Apply的实现
    Function.prototype.Apply = function(that) {
        let fn = Symbol('fn');
        let args = arguments[1];
        that = that || window; //undefined / null that ->window
        that[fn] = this;
        let res = that[fn](...args);
        delete that[fn];
        return res;
    };
    //Bind的实现依赖Apply/Call
    Function.prototype.Bind = function(that) {
        that = that && (typeof that === 'object' || typeof that === 'function') ? that : window;
        let args = Array.prototype.slice.call(arguments).slice(1);
        let self = this;

        let bound = function() {
            //判断是否通过new调用这个函数
            //如果是 new bound(),构造函数中的this被绑定到了一个被指定__proto__ == <constructor>.prototype
            //这时候使用 this instanceof pure
            return self.Apply(Instanceof(this, bound) ? this : that, args.concat(...arguments));
        }
        //箭头函数的prototype是undefined
        //这里吧bound.prototype 改变到了调用bind函数的self上
        if (self.prototype) {
            bound.prototype = self.prototype;
        }
        return bound;
    }
复制代码

最后通过babel将ES6转成ES5解决了我的疑问

这里用到的知识点

1.闭包Closure

2.变量提升function var



这篇关于探索ES6类构造器中创建原型同名变量的原理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程