ES5面向对象(原型与原型链及继承)


原型及相关概述

prototype (函数的属性:对象{})

原型是JavaScript独有的行为继承机制。主要内容是每个JavaScript函数在创建的时候默认会向该函数添加一个属性(prototype),这个属性对应一个Object对象,称为原型对象(原型在程序里面默认就是一个Object对象)

  • 函数作为普通函数调用prototype没有任何作用
  • 通过函数创建的对象可以直接使用原型对象上的行为。
  • 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中,继承的基础
  • Object类是JavaScript自带的一个类,默认该类的对象会作为所有函数的原型对象。
  • 我们给原型上添加行为,通过函数生成的对象也可以使用新增的行为
  • 各自函数的原型不是同一个对象

__proto__(对象Object的一个属性:对象{})

原型的链接点

  • 其实不属于构造函数的原型,而是来自于Object.prototype

  • 当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__来访问该属性

    function Person() {}
    var person = new Person();
    console.log(person.__proto__ === Person.prototype); // true
    
  • 当读取实例属性时候,如果找不到,就去继承原型中的属性,如果还查不到?那咋整?当然是再去找原型的原型喽,知道找到最顶层为止

    function Person() {}
    Person.prototype.name = 'Kevin';
    var person = new Person();
    person.name = 'Daisy';
    console.log(person.name) // Daisy
    //删除person自身属性name
    delete person.name;
    //输出的为原型对象的属性
    console.log(person.name) // Kevin
    

    这里面的首先给实例原型赋属性值name为“Kevin”,下面给实例化对象也赋值属性name为“Daisy”,第一遍访问person的name属性时,值为“Daisy”,delete之后,实例化本身的name已经为空,所以再次访问的时候,就会往上一层寻找name属性,即通过prototype赋的name值,所以这时的值是“Kevin“。

对象的__proto__保存着该对象构造函数的prototype

constructor

  • 构造函数的prototype指向原型,原型对象的constructor指向构造函数

    function Person() {}
    var person=new Person();
    console.log(Person === Person.prototype.constructor); // true
    console.log(person.constructor === Person.prototype.constructor)//true
    
  • 可以被更改

原型的原型

  • 原型对象也是对象,所以它也有原型,

  • 当我们使用一个对象的属性或方法时,会现在自身中寻找,

    自身中如果有,则直接使用

  • 如果没有则去原型对象中寻找,如果原型对象中有,则使用,

  • 如果没有则去原型的原型中寻找,直到找到Object对象的原型,

  • Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined

    console.log(Person.prototype.__proto__===Object.prototype) //true
    
    console.log(Object.prototype.__proto__)//null
    

原型链

概念

以一个对象为基准,__proto__为连接的链条,一直到Object.prototype.__proto__为止

  • JavaScript 每个对象都有一个私有属性 __proto__ 指向它的构造函数的原型对象 prototype。该原型对象也有一个 __proto__,层层向上直到一个对象的原型对象__proto__null,作为这个原型链中的最后一个环节
  • 当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,会在该对象的原型链上依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾
  • 意义:在ES5中实现继承

Function和Object的特殊性

  • FunctionObject既是函数又是对象
function Text(){}
console.log(Text.__proto__===Function.Prototype);//true
//Text构造底层原理 
const Text=new Function()
//底层规范  Function 构造了本身
console.log(Function.__proto__===Function.prototype)//true

console.log(typeof Object)//function
console.log(Object.__proto__===Function.prototype)//true
console.log(Function.__proto__===Object.__proto__)//true

判断对象中是否包含某属性

  • hasOwnProperty 判断某个属性是否是自身的属性,不考虑继承过来的属性。

    console.log(text.hasOwnProperty('name'));
    
  • in判断某个属性是否是自身的属性,考虑继承过来的属性

    console.log('name' in text);
    

继承(ES5)

  • 语法

    function Son(name){
        this.name = name;
    }
    function Father(phone){
        this.phone  =phone;
    }
    function GrandPa(money){
        this.money = money;
    }
    //用原型链实现继承
    //老爸继承爷爷
    Father.prototype = new GrandPa(100000000);
    //儿子继承老爸
    Son.prototype = new Father('10086');
    let s = new Sun('张三');
    console.log(s.money);
    
  • 存在的问题

    • 继承的属性值是默认的,固定的,不符合继承的实现

解决方案

  • 子构造函数的原型设置为父构造函数的实例对象

    子函数名.prototype = new 父函数名();
    
  • 子函数第一行利用Call让子函数创建出来的对象拥有父构造函数中定义的属性和行为

    function 子函数(参数1,参数2,参数n){
        父函数名.call(this,参数1,参数2,参数n);
    }
    

注:call 本身是JavaScript的一个内置函数(之后会再进行补充)

  • 作用:能够改变一个函数的执行环境,实现’借用’

  • 语法

    函数名.call(新的执行环境,函数调用需要的参数);
    
  • 以新的执行环境调用一次函数,函数所需的参数,是从call的第二个实际参数开始。

在ES5中的作用’

  • 把子类作为父类构造函数的执行环境并执行一次父类的构造。目的是为了子类对象拥有父类构造函数里定义的属性和行为

文章作者: 时光路人
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 时光路人 !
评论
  目录