ES5构造函数与ES6类

ES5

es5并没有类class,我们只能用构造函数来模拟类.

构造函数

构造函数用new操作符一起使用.new具体做了以下事情.
1.new在内存中创建了一个新的空对象.
2.让this指向了这个对象.
3.执行构造函数里的代码,给这个新的对象增加属性和方法.
4.返回这个新对象,隐式return.

//构造函数
function Animal(){
    this.name='动物' //实例属性 
}
//每个创建出的实例身上都带有name属性,this指向这个实例创建的对象.
let animal=new Animal();  
animal.name='动物1'
let animal1=new Animal();
console.log(animal1.name) //动物

//如果手动返回一个引用类型this就会指向这个引用类型
function Animal(){
   this.name='动物'
   //this指向返回这个对象
   return {
       a:2
   }
}

let animal=new Animal(); //{a:2}
console.log(animal.name) //undefined

静态属性

function Animal(){
}
//静态属性
Animal.attr='12'
//静态方法
Animal.say=function(){
   return 11111
}
console.log(Animal.attr)  //12
console.log(Animal.say()) //11111

原型prototype(每个类都有prototype,是一个对象)

function Animal(){
}
//公共方法 每个实例上都会带有这方法
Animal.prototype.say=function(){
   return 'say'
}
Animal.prototype.attr='attr'

let animal=new Animal();
let animal1=new Animal();
console.log(animal.say===animal1.say) //true 所有的实例都指向这个地址
console.log(animal.say()) //say
console.log(Animal.prototype) //Animal { say: [Function], attr: 'attr' }

_ _proto _ _ (实例 _ _ proto _ _ 指向所属类的原型,用来查找属性和方法)

console.log('123'.__proto__)
console.log({}.__proto__)
let num=123
console.log(num.__proto__)
let bol=true;
console.log(bol.__proto__)
//结果
//[String: '']
//{}
//[Number: 0]
//[Boolean: false]
//null undefined error
function Animal(){

}

Animal.prototype.say=function(){
   return 'say'
}
Animal.prototype.attr='attr'

let animal=new Animal();
console.log(animal.__proto__);// Animal { say: [Function], attr: 'attr' }  指向实例所属类的原型对象
// 查找顺序先找自身 自身没有再去查找所属类的原型对象
console.log(animal.__proto__===Animal.prototype); //true

构造函数constructor(prototype里包含一个constructor属性)

function Animal(){

}
let animal=new Animal()
console.log(animal.__proto__.constructor)//[Function: Animal]
console.log(Animal.prototype.constructor) //[Function: Animal]

原理

无标题.png

Object.prototype

无标题.png
class Animal{
  
}
let animal=new Animal()
console.log(Animal.prototype.__proto__===animal.__proto__.__proto__) //true
console.log(Animal.prototype.__proto__===Object.prototype) //true
console.log(animal.__proto__.__proto__===Object.prototype) //true
console.log(Function.prototype.__proto__===Object.prototype) //true
console.log(Array.prototype.__proto__===Object.prototype) //true
console.log(Function.__proto__===Object.__proto__) //true
console.log(Object.prototype.__proto__) //null Object已经是顶层了

继承

1.实例属性继承

function Animal(name){
    this.name=name
}

function Tiger(){
    //this指向Tiger实例
    Animal.call(this,'老虎')
}

let tiger=new Tiger();
console.log(tiger.name)  //老虎

2.原型继承

function Animal(name){
    this.name=name
}
Animal.prototype.say=function(){
    return 'say'
}
function Tiger(){
    //this指向Tiger实例
    Animal.call(this,'老虎')
}
//这种方式不可采 把Tiget的指向Animal.prototype会造成子类和父类同引用一个内存地址 子类原型改变会影响父类
Tiger.prototype=Animal.prototype 

//这样不会改变子类的原型 先从子类查找 子类查找不到再去父类原型查找 不会污染父类 可以在自己子类原型增加方法
Tiger.prototype.__proto__=Animal.prototype

let tiger=new Tiger();
console.log(tiger.__proto__) //Tiger {}
console.log(tiger.say()) //say

2.1 object.create继承

function Animal(name){
    this.name=name
}
Animal.prototype.say=function(){
    return 'say'
}
function Tiger(){
    //this指向Tiger实例
    Animal.call(this,'老虎')
}
//tiger.constructor指向的是父类
Tiger.prototype=Object.create(Animal.prototype)
//设置constructor tiger.constructor指向自己
Tiger.prototype=Object.create(Animal.prototype,{constructor:{value:Tiger}})
let tiger=new Tiger();
console.log(tiger.__proto__.constructor)
console.log(tiger.say())

2.2 模拟object.create

function Animal(name){
    this.name=name
}
Animal.prototype.say=function(){
    return 'say'
}
function Tiger(){
    //this指向Tiger实例
    Animal.call(this,'老虎')
}
function create(prototypes,constr){
    function Fn(){

    }
    Fn.prototype=prototypes;
    if(constr&&constr.constructor.value){
       Fn.prototype.constructor=constr.constructor.value
    }
    return new Fn()
}

Tiger.prototype=create(Animal.prototype,{constructor:{value:Tiger}});
Tiger.prototype.say=function(){
    return 'Tiger say'
}
let tiger=new Tiger();
console.log(tiger.__proto__.constructor) //[Function: Tiger]
console.log(tiger.say()) // Tiger say
console.log(Animal.prototype.say()) //say

object.create实现原理

无标题.png

es6类(class)

class Animal{

}
let animal=new Animal();
console.log(animal)

静态属性(通过类自身调用)

class Animal{
   static attrs='attrs'
   static say(){
       return 'say'
   }
}
console.log(Animal.attrs) //attrs
console.log(Animal.say()) //say
class Animal{
    static attrs='attrs'
    constructor(){}
    say(){
        return 'say'
    }
    
}
class Tiger extends Animal{
   constructor(){

   }
}

console.log(Tiger.attrs) //attrs

实例属性

//没有写contructor情况下 增加属性 会默认执行constructor把属性添加到实例上
class Animal{
    attrs='attrs'   // 注意!!这里定义的不是在prototype上的属性,而是给实例初始化的 实例属性
}

let animal=new Animal();
console.log(animal.attrs)


class Animal{
   constructor(){
       this.attrs='attrs'  //实例属性
   }
}

let animal=new Animal();
console.log(animal.attrs)

prototype(定义在类里 es6的原型是不可枚举的)

class Animal{
    constructor(){}
    say(){
        return 'say'
    }
    
}
let animal=new Animal();
console.log(Animal.prototype.say) //[Function: say]

私有属性(ES2020实验草案中,增加定义私有类字段功能,使用# ,私有属性无法在类的外部调用)

//私有属性
class Animal{
     #privateValue="私有"
}

//私有方法
class Animal{
     #privateValue(){
        return 'private'
     }
}
//私有静态属性
class Animal{
    statci  #privateValue="私有"
}
//可以在类的内部使用,无法在类的外部使用

calss Animal{
    #attrs='11111'
    #bac(){
        Animal.#attrs="22222"
    }
}

console.log(Animal.#bac) //error

继承(extexds)

//子类不写constructor会默认执行父类的constructor
class Animal{
    constructor(attrs){
       this.attrs=attrs
    }
    say(){
        return 'say'
    }
}

class Tiger extends Animal{

}

let tiger=new Tiger('tiger attrs'); //Tiger { attrs: 'tiger attrs' }
console.log(tiger.__proto__.say) //[Function: say]
//子类写constructor,默认走子类的构造函数
class Animal{
    constructor(attrs){
       this.attrs=attrs
    }
    say(){
        return 'say'
    }
}

class Tiger extends Animal{
   constructor(attrs){
      super(attrs)  //等同于 Animal.call(this,attrs) 不调用super会报错
   }
}

let tiger=new Tiger('tiger attrs');
console.log(tiger) //Animal { attrs: 'tiger attrs' }
console.log(tiger.__proto__.say) //[Function: say]
//子类和父类定义相同的方法,会先执行子类 子类没有才会向类查找
class Animal{
    constructor(attrs){
       this.attrs=attrs
    }
    say(){
        return 'say'
    }
}

class Tiger extends Animal{
   constructor(attrs){
      super(attrs)  //等同于 Animal.call(this,attrs)
   }
   say(){
       return 'tiger say'
   }
}

let tiger=new Tiger('tiger attrs'); //Tiger { attrs: 'tiger attrs' }
console.log(tiger.say()) // tiger say
//子类调用父类方法
class Animal{
    constructor(attrs){
       this.attrs=attrs
    }
    say(){
        return 'say'
    }
}

class Tiger extends Animal{
   constructor(attrs){
      super(attrs)  //等同于 Animal.call(this,attrs)
   }
   say(){
       return super.say()  //等用于 Animal.prototype.say()  //super===Animal.prototype
   }
}

let tiger=new Tiger('tiger attrs'); //Tiger { attrs: 'tiger attrs' }
console.log(tiger.say()) //say
//静态方法调用
class Animal{
    constructor(attrs){
       this.attrs=attrs
    }
    static say(){
        return 'say'
    }
}

class Tiger extends Animal{
   constructor(attrs){
      super(attrs)  //等同于 Animal.call(this,attrs)
   }
   static say(){
       return super.say()  //super等用于Animal类  //super===Animal
   }
}

console.log(Tiger.say()) //say

抽象类(只可继承,不可被实例化new)

1.new.target实现抽象类

class Animal{
    constructor(){
        //执行两次 1.new.target=[Function: Animal] 2.new.target[Function: Tiger]
        if(new.target===Animal){
            throw new Error('not new')
        }
    }
}

class Tiger extends Animal{

}
new Animal()  //Error: not new
let tiger=new Tiger();

es5构造函数模拟实现es6(原型属性不可枚举)

function handleConstructor(Constructor,protoProperties){
    for(let i=0;i<protoProperties.length;i++){
        let property=protoProperties[i]
        Object.defineProperty(Constructor,property.key,{
             //是否可以删除
             configurable:true,
             //是否可以枚举
             enumerable:false,
             ...property
        })
    }
}

// console.log(Eat.prototype) //不可枚举
function definePrototype(Constructor,protoProperties,staticPorto){
    if(Array.isArray(protoProperties)){
       handleConstructor(Constructor.prototype,protoProperties)
    }
    if(Array.isArray(staticPorto)){
        handleConstructor(Constructor,staticPorto)
    }
}


//es5的类 模拟es6的类 不可枚举 Object.definprototype
let Animate=(function(){
    function Animate(){
         if(!(this instanceof Animate)){
             throw new Error('not new')
         }
    }
    //改变原型不可枚举  babel编译出es6的类就是这样写的
    definePrototype(Animate,[
        {
            key:'say',value:function(){
                console.log('say')
            }
        }
    ],[
        //静态属性
       {
            key:'eat',value:function(){
               return 'eat'
            }
        }
    ])
    return Animate
})()

console.log(Animate) //[Function: Animate]
console.log(Animate.prototype) //Animate {}
console.log(Animate.eat()) //eat

instanceof

instanceof运算符是用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上.

//obj instanceof Object 检测Object.prototype是否存在与参数obj的原型链上.
function Person(){}
let p=new Person();
//p.__proto__==Person.prototype
console.log(p instanceof Person) //true

hasOwnProperty()操作符

hasOwnProperty检查一个属性是否是实例属性.

//实例属性
function Animal(){
    this.name='name'
}

let animal=new Animal();
console.log(animal.hasOwnProperty('name')) //true
//不查看原型属性,只看实例属性
function Animal(){
    
}
Animal.prototype.name='123'

let animal=new Animal();
console.log(animal.hasOwnProperty('name')) //false

in操作符

有两种方法能使用in操作符:单独使用for-in循环使用.在单独使用时,in操作符会通过对象能够访问给定属性返回true,无论属性存在(实例)还是(原型中).

function Animal(){
    this.name='name'
}

let animal=new Animal();
console.log('name' in  animal) //true
function Animal(){
    
}
Animal.prototype.name='123'

let animal=new Animal();
console.log('name' in  animal) //true
function Animal(){
    
}
let animal=new Animal();
console.log('name' in  animal) //false
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335