JavaScript学习系列之原型、原型链

原型是Javascript中的继承的基础,JavaScript的继承主要依靠原型链来实现的。html

1.原型

在JavaScript中,咱们建立一个函数A(就是声明一个函数), 就会为该函数建立一个prototype属性。并且也会在内存中建立一个对象B,A函数的属性 prototype 指向这个对象B( 即:prototype的属性的值是这个对象 )。这个对象B就是函数A的原型对象,简称函数的原型。这个原型对象B 默认会有一个属性 constructor, constructor属性指向函数A ( 意思就是说:constructor属性的值是函数A )。java

/*
    		声明一个函数,则这个函数默认会有一个属性叫 prototype 。并且浏览器会自动按照必定的规则
    		建立一个对象,这个对象就是这个函数的原型对象,prototype属性指向这个原型对象。这个原型对象
    		有一个属性叫constructor 执行了这个函数
	
	      注意:原型对象默认只有属性:constructor。其余都是从Object继承而来,暂且不用考虑。
	   */
	    function Person () {
	    	
	    }	
复制代码

下面的图描述了声明一个函数以后发生的事情:浏览器

当把一个函数做为构造函数 (理论上任何函数均可以做为构造函数) 使用new建立对象的时候,那么这个对象就会存在一个默认的不可见的属性,来指向了构造函数的原型对象。 这个不可见的属性咱们通常用 [[prototype]] 来表示,只是这个属性没有办法直接访问到。bash

function Person () {  }	
        /*
        	利用构造函数建立一个对象,则这个对象会自动添加一个不可见的属性 [[prototype]], 并且这个属性
        	指向了构造函数的原型对象。
        */
      	var p1 = new Person();
复制代码

观察下面的示意图:函数

1.1 小结

__proto__ 是对象实例才有的属性,指向对象的原型。
prototype 是构造函数才有的属性,该属性指向了一个对象,这个对象正是调用该构造函数而建立的实例的原型
实例的__proto__属性 和 构造函数的 prototype 都指向该对象原型
复制代码

这几句话能解释一切关于原型方面的问题:优化

当 new 一个函数的时候会建立一个对象,『函数.prototype』 等于 『被建立对象.__proto__』
一切函数都是由 Function 这个函数建立的,因此『Function.prototype === 被建立的函数.__proto__』
一切函数的原型对象都是由 Object 这个函数建立的,因此『Object.prototype === 一切函数.prototype.__proto__』
复制代码

2.原型链

原型链基本思路:利用原型让一个引用类型继承另外一个引用类型的属性和方法。this

每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数想指针(constructor),而实例对象都包含一个指向原型对象的内部指针(__proto__)。
若是让原型对象等于另外一个类型的实例,此时的原型对象将包含一个指向另外一个原型的指针(__proto__),另外一个原型也包含着一个指向另外一个构造函数的指针(constructor)。
假如另外一个原型又是另外一个类型的实例……这就构成了实例与原型的链条。
复制代码

原型链基本思路(图解):spa

'__proto__'是对象的属性、'prototype'是函数的属性
null是对象原型链的终点,其值既有(是一个对象)又无(不引用任何对象),
表明着对象本源的一种混沌、虚无的状态,正与老子《道德经》中的“道”,有着同等的意义
(心中一万只艹尼玛奔腾而过,仍是写java爽啊)。

在JS中,undefined是全局对象的一个属性,它的初始值就是原始数据类型undefined,而且没法被配置,也没法被改变。
undefined从字面意思上理解为“未定义”,即表示一个变量没有定义其值。

而null是一个JS字面量,表示空值,即没有对象。
与undefined相比,null被认为是“指望一个对象,可是不引用任何对象的值”,而undefined是纯粹的“没有值”。

复制代码

从一张图看懂原型对象、构造函数、实例对象之间的关系prototype

prototype:构造函数中的属性,指向该构造函数的原型对象。

constructor:原型对象中的属性,指向该原型对象的构造函数

_proto_:实例中的属性,指向new这个实例的构造函数的原型对象
复制代码

3.利用原型实现继承

3.1 利用 call 借用构造函数继承

优势:实现了继承属性,但值都不相同指针

缺点: 没法继承父级类别中原型上的方法

function Person(name,age,sex,weight){
    this.name=name;
    this.age=age;
    this.sex=sex;
    this.weight=weight;
}
Person.prototype.sayHi=function(){
    console.log("您好")
}
 
function Student(name,age,sex,weight,score){
    //将当前实例对象传入Person 借过来使用一次来达到继承效果
    Person.call(this,name,age,sex,weight);
    this.score=score;
}
 
var stu1=new Student("小明",10,"男","10kg","100")
复制代码

3.2 prototype 实现继承

利用prototype,将Student 的prototype 指向 Person 来达到继承效果,

优势:继承了父级原型上的方法

缺点: 实例化多个Student 都必须共用相同的name 和 age

// 此处
Student.prototype.constructor=Student
复制代码
function Person(name,age){
        this.name=name;
        this.age=age;
     }
 
     Person.prototype.eat=function(){
        console.log("Person 吃饭")
     }
 
     function Student(num,score){
        this.num=num
        this.score=score
     }
     //继承
    Student.prototype=new Person("小红",10)
    Student.prototype.constructor=Student
 
    var stu =new Student(2016002288,80)
 
    stu.eat()//Person 吃饭
复制代码

3.3 组合继承

组合继承其实就是结合了上述的两种方法来实现继承,拥有两种方法的优势

function Person(name,age,sex){
        this.name=name;
        this.age=age;
        this.sex=sex;
     }
     Person.prototype.sayHi=function(){
        console.log("你好")
     }
 
     function  Student(name,age,sex,score){
        //借用构造函数
        Person.call(this,name,age,sex)
        this.score=score
     }
 
     // 改变了原型指向
     Student.prototype=new Person();//不传值
     Student.prototype.eat=function(){
        console.log("吃东西");
     }
 
     var stu=new Student("小黑",20,"男","100分")
     console.log(stu.name,stu.age,stu.sex,stu.score);
     stu.sayHi()//你好
     stu.eat()//吃东西
复制代码

3.4 拷贝继承

相似于复制,把一个对象中的属性和方法直接复制到另外一个对象中

function Person(){
    }
 
    Person.prototype.name="小红"
    Person.prototype.age=18
 
    function Student(){
    }
    
    var p=Person.prototype;
    var s=Student.prototype;
 
    for(key in p){
        s[key]=p[key]
    }
 
    console.dir(Student)
复制代码

每次都要for in 好累 , 能够进行优化封装一下

function extend(Child,Parent) {
 
    var p = Parent.prototype;
    var c = Child.prototype;
 
    for (var i in p) {
      c[i] = p[i];
      }
        
        //这个属性直接指向父对象的prototype属性,能够直接调用父对象的方法,为了实现继承的完备性,纯属备用性质
    c.par = p;
 
  }
复制代码

3.5 直接继承prototype

优势 : 效率比较高

缺点 : 由于至关因而个传址过程 因此修改Student的属性 Person 的也会被更改

function Person(){};
 
    Person.prototype.name="小红";
    Person.prototype.age=18;
 
    function Student(){};
 
    Student.prototype=Person.prototype;
 
    console.dir(Student);
    console.dir(Person);
    Student.prototype.age=25;
复制代码

3.6 利用空对象做中介实现继承

用这种方式修改 Student 的prototype 不会影响到 Person的prototype

function Person(){};
    Person.prototype.name="小红";
    Person.prototype.age=11;
 
    function Student(){};
    var F=function(){};
    F.prototype=Person.prototype;
 
    Student.prototype=new F();
    Student.prototype.constructor=Student;
 
    Student.prototype.age=25;
 
    console.dir(Person)
    console.dir(Student)
复制代码

参考文献

www.cnblogs.com/wangfupeng1…

相关文章
相关标签/搜索