欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-19 12:39:19
es6类的特性1、不能直接调用类,必须通过new来调用,不使用new调用会报错的2、有些属性是不可枚举的,es5的原型上的方法或属性是可以枚举的3、使用Object.defineProperty()方法给原型添加自定义的属性或方法(给原型上加属性方法)构造函数:简单理解就是: 通过 new 的方式调用一个函数,该函数就是构造函数,...

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

 

 

 

 

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

 

 

 

本文地址:https://blog.csdn.net/dingdinglaila/article/details/112546725

相关标签: JavaScript 前端