JavaScript 进阶 33 - 用ES5的代码模拟ES6的类的特性

es6类的特性

  • 不能直接调用类,必须通过new 来调用,不使用new调用会报错的
  • 有些属性是不可枚举的 ,es5的原型上的方法或属性是可以枚举的
  • 使用Object.defineProperty()方法给 原型 添加自定义的属性或方法(给原型上加属性方法)

构造函数

简单来说呢,构造函数就是:通过 new 的方式调用一个函数,该函数就是构造函数。

构造:就是---- 新创建一个具体的对象

自定义构造函数

//自定义构造函数
function Person(){
    //函数体
}

let per = new Person();

console.log(per)//Person

一个方法 通过new 来实例化,被实例化的的函数就是构造函数 ,就是 通过new来实例出一个对象,就是构造函数

实例和对象的区别,从定义上来讲:

    1、实例是类的具象化产品,

    2、而对象是一个具有多种属性的内容结构。

    实例都是对象,而对象不全是实例。比如:动物 --就是- 对象。 狗 --就是- 实例(具体对象)。

this的指向问题

//this的指向问题
function Person(name,age){
    console.log(this);//Person{} 相当于 this ==={}
    this.name = '中国人';
}
let per = new Person();
console.log(per);//Person{}

    new Person()做了什么事情:

  1. 在函数内部创建一个空对象,
  2. 让this指向此空对象
  3. 把函数执行完, 
  4. 返回这个对象
function Person(name,age){
    this.name = name;
    this.age = age;
}
let per = new Person('lily','33');
console.log(per);//Person { name: 'lily', age: '33' }

【例 3 】模拟es6类的特性 必须用new才能调用类

//【例 3 】模拟es6类的特性 必须用new才能调用类
let Person =(function(){
    function Person(){
        if(!(this instanceof Person)){
            //如果 this不是person的实例对象,就抛出错误
            throw new Error('can not call class as a function');
        }
        this.name = 'zhongguoren';
    }
    return Person;
})();
//Person();//can not call class as a function   不能将class作为函数直接调用 
let per = new Person();
console.log(per);//Person { name: 'zhongguoren' }

再来看一个例子

【 例4 】

function Person(){
    this.name = 'zhongguoren';
    this.eat = function(){
        console.log('eating');
    }
}
let p = new Person();
p.eat();//eating

let p2 = new Person();
p2.eat();//eating

console.log(p.eat === p2.eat);//false 每个对象都有自己的成员,

上面代码中的 第7行 p 和第 10行的 p2 两家是一模一样的,那就把这种公共的方法定义在原型上,看下面例子 

【例5】

function Person(){
    this.name = 'zhongguoren';
}
Person.a = 10;//静态属性,直接给类增加的属性、方法等等 ,不用通过new来实例的
console.log(Person.a);//通过构造函数直接访问 就像 Math.max(),max()方法是构造函数Math的静态方法

//原型的属性或方法是公用的,解决了代码复用的问题,这样写 es5的原型是暴露的,且能修改
Person.prototype.eat = function(){
    console.log('eat')
}
//在原型上定义,可以上每个实例都能使用
let p = new Person();
let p2 = new Person();
console.log(p.eat === p2.eat)//true

上面的【例4】【例5】 两个例子 属性都是在原型上直接定义的,可以被迭代的。直接加载原型上那就把原型暴露了。说明一个问题:es5的原型是暴露的。为了防止此问题,我们可以使用defineProperty() 如下代码:

因为es6的原型是不允许被访问的,所以,我们可以使用defineProperty() 自定义属性,给原型设置属性或方法。如下代码:

【例6】用es5的代码模拟es6的类的特性 (特性:只能通过new来调用) 用自定义属性的方法在原型上定义属性和方法。

function defineProperty(Constructor,protoProperties){
    //判断一下是不是数组
    if(Array.isArray(protoProperties)){
        for(let i = 0; i < protoProperties.length; i++){
            let protoProperty = protoProperties[i];
            //代码的核心就是Object.defineProperty()这个方法,构造函数的原型
            Object.defineProperty(Constructor.prototype,protoProperty.key,{
                configurable:true,
                enumerable:false,
                ...protoProperty
            })
        }
    }
}
let Person =(function(){
    function Person(){
        //通过new 和不通过new 函数里的this的指向是不同的,所以通过判断this到底是new还是直接去引用的
        if(!(this instanceof Person)){
            //如果 this不是 构造函数person 的实例对象,就抛出错误(如果不是实例对象,说明是直接调用的,就抛个错误
            throw new Error('can not call class as a function');
        }
        this.name = 'zhongguoren'; //如果是实例,就给这个实例 加一个属性name
    }
    //定义原型属性,在原型上定义属性 都放在数组中。
    // 第一个参数:给谁定义,给构造函数的原型
    // 第二个参数:公共参数,公共的方法或者属性 这里是个数组,里面有许多的属性和属性名
    defineProperty(Person,[
        {
            key:'eat',
            value:function(){
                console.log('eating')
            }
        },{
            key:'sing',
            value:function(){
                console.log('singing')
            }
        }
    ])

    return Person;
})();
let per = new Person();
console.log(Person.prototype);//Person {} 不能被访问时是空的,但是可以被调用
per.eat();//eating
per.sing();//singing

【例 7 】es6类的特性 给类增加静态属性  如:Person.aa 。就像我们常用到的 函数 Math.max()。.max()可以说是构造函数Math的静态属性

//定义属性  target:给谁定义,props:加哪些属性
function _definepts(target,props){
    for(let i = 0; i < props.length; i++){
        let protoProperty = props[i];
        //代码的核心就是Object.defineProperty()这个方法,构造函数的原型
        Object.defineProperty(target,protoProperty.key,{
            configurable:true,
            enumerable:false,
            ...protoProperty
        })
    }
}
function defineProperty(Constructor,protoProperties,staticProperties){
    //判断一下数组 是不是 在原型上定义的 方法
    if(Array.isArray(protoProperties)){
        _definepts(Constructor.prototype,protoProperties)
    }
    //判断数组 是不是 在类上直接添加的 静态属性
    if(Array.isArray(staticProperties)){
        _definepts(Constructor,staticProperties)
    }

}
let Person =(function(){
    function Person(){
        //通过new 和不通过new 函数里的this的指向是不同的,所以通过判断this到底是new还是直接去引用的
        if(!(this instanceof Person)){
            //如果 this不是 构造函数person 的实例对象,就抛出错误(如果不是实例对象,说明是直接调用的,就抛个错误
            throw new Error('can not call class as a function');
        }
        this.name = 'zhongguoren'; //如果是实例,就给这个实例 加一个属性name
    }
    //定义原型属性,在原型上定义属性 都放在数组中。
    // 第一个参数:给谁定义,给构造函数的原型
    // 第二个参数:公共参数,公共的方法或者属性 这里是个数组,里面有许多的属性和属性名
    // 第三个参数:静态属性或者方法(私有的  )
    defineProperty(Person,[
        {
            key:'eat',
            value:function(){
                console.log('eating')
            }
        },{
            key:'sing',
            value:function(){
                console.log('singing')
            }
        }
    ],[
        {
            key:'aa',
            value:100
        }
    ])

    return Person;
})();
let per = new Person();
console.log(Person.aa)//100
per.eat();//eating
per.sing();//singing

 

 

 

 

好啦 这一篇先到这里。我的文章都是学习过程中的总结,如果发现错误,欢迎留言指出,我及时更正

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值