javaScript-原型、继承-01

时间:2022-08-22 17:38:22

为什么会有原型这个概念;

1、优雅的创建对象;

2、继承;

先看js 之前创建对象的方式存在的问题;

创建对象方式

1、字面量

var obj={name:"join",age:18}

var obj1={name:"Tom",age:88}

当我们需要创建多个属性只有值不一样的对象时,这种方式就悲剧来了,会造成大量的冗余代码,最重要的是代码不能复用;

2、工厂模式

这种工厂模式并不是设计模式中的工厂方法,代码如下:

    function createObj(name, age) {
        var o = new Object();
        o.name = name;
        o.age = age;
        o.sayHello= function() {
            alert("Hello Word!");
        }
        return o;
    }

    var obj = createBlog("Tom", "88");

这种方式好多了,但是也存在一个问题,就是没办法知道一个对象是什么类型;console.log(typeof obj);//object

3、构造函数模式

 
 
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayNmae=function(){
alert(this.name);
}
}
var persong1=new Person("Join",18,"农民");
var persong2=new Person("Join",18,"地主");
 

构造函数模式看起来oop(面向对象)多了,上面两种模式存在的问题在构造模式中都不存在了(创建对象的代码可以复用、对象类型可以识别)看起来世界挺美好,遗憾的是

这种还是存在一个问题;

1、每一个方法都要在每个实例上重新创建一次(问题不大);

2、从上面例子可以看出每个person 实例都包含一个不同的function 实例;不要忘了ECMAScript中的函数也是对象,所以上面的代码与下面的代码等价(主要会造成不同的作用域和解析问题)

    function Person(name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;
        this.sayNmae=new function(){
            alert(this.name);
        }
    }
alert(person1.sayNmae==person2.sayNmae)// false

当然这个问题也是能解决的,就是把方法移到外面,让他成为全局的函数,但是这样一来该函数就只能由widows对象调用了,而且如果存在多个方法,就会存在多个全局函数,那我们自定义的的引用类型
就丝毫没有封装可言了。
好在这些问题都可言通过原型模式来解决;
说了一大堆废话进入正题:原型,他是一个对象;

function特征

在js中一切都是对象,当我们写function 时js 就会同时创建它的prototype对象(原型对象),这个对象包含一个属性prototype(原型),这个属性是一个指针,该指针指向原型对象,

该对象的作用是特定类型(该类型实例的对象)的所有实例共享属性和方法;

function Person(name,age,job){

this.name=name;
this.age=age;
this.job=job;
}
Person.prototype.sayNmae=function(){
alert(this.name);
}
var persong1=new Person("Join",18,"农民");
var persong2=new Person("Join",18,"地主");

alert(persong1.sayNmae==persong2.sayNmae)// true

 

Prototype对象

在默认情况下所有原型对象都会自动获得一个属性Constructor(构造函数属性)这个属性包含一个指向prototype属性所在的函数指针;

实例之New 操作符

创建一个实例通常会经历以下4步:

1、创建一个新的对象;

2、将构造函数的作用域赋给新对象(this指针指向这个新的对象);

3、执行构造函数中的代码(还会将该对象的__proto__指向了函数的prototype对象);

4、返回该对象;

需要注意的地方是当调用构造函数创建一个新对象后,该对象内部将包含一个指正(内部属性 _proto_),指向构造函数的原型对象(prototype 对象),该属性在ES6中貌似规范化了;

实例、构造函数、原型对象关系

创建的每个实例都会有一个内部属性_proto_ 指向构造函数的原型对象;

构造函数存在prototype 属性指向该函数的原型对象;

上面person代码如图:

javaScript-原型、继承-01

图画的不好下次试试思维导图这个工具;

注意实例只跟原型对象有关系;

查找原理

当代码读取某个对象的属性或者方法时都会执行一次搜索,首先从从对象本身开始,如果存在则返回给定的属性名或者方法名,不存在继续搜索指针(_proto_)指向的原型对象,如上面的代码 persong1.sayNmae。首先解析器会问:"对象实例person1有sayName吗?",答:没有,然后继续搜索,问person1的原型有"sayName 吗?",答:有,读取原型对象中的函数;这就是多个实例共享原型中属性、方法的基本原理;

由此可知当实例中与原型中存在相同的属性名时会读取实例的属性名,但是他们之间不存在引用问题,即你设置了实例的属性为null,并不会影响到原型中的属性,他们终究是属于不同的对象;

更简单的原型语法

当我们要添加多个方法时,没必要每次都写类似这样的代码 Person.Prototype.XXX=function(){//doing something}

Person.Prototype={

constructor:Person,//注意这里要重写

sayAge:function(){ alert(this.age);},

sayJob:function(){ alert(this.job);}

}

这里为什么要重写constructor呢,很简单每创建一个函数,就会同时创建它的prototype对象(原型对象),这个对象也会获得constructor属性,

而上面我们重写了默认的prototype对象,因此constructor这时并没有指向Person,而是指向新object构造函数,所以这里要重写constructor让他指向Person;

原型动态性

原型是对象,对象是引用类型,所以当在原型中干了些什么事情,都能立即在实例对象上反应出来;

原生对象的模型

JS 原生的引用类型,都是采用这种模式创建的。如 在Array.prototype.sort(),可以找到sort()方法。

原型模式的问题

当我们把引用类型属性添加到原型对象时,会造成数据共享的问题,如 在person 中把一个数组放到原型对象中,此时所有的对象都会共享这个数组:

friedns:["tom","Join","Elie"];

当我们其中一个实例对这个对象进行push("Tem");

所有的实例都可以访问到"Tem";

组合使用构造函数与原型模式

基于上面数据共享的问题,解决方案通常是:属性写在构造函数中,方法写在原型中,这样每个实例都会有自己属性,同时又共享着方法的引用,这样既能最大限度的节省了内存;

 

总结

1、创建function 就会同时创建它的prototype对象(原型对象),function也是对象其实在原型对象中也存在一个默认的_proto_ 指向函数的原型对象(Function.prototype);

2、JS 每个对象中都存在_proto_,函数对象才存在prototype 属性,所以会看到一些博客说JS 对象分为两种:一种是Object(自定义),一种是function;

3、实例话对象就会把function中的prototype属性值赋给对象实例的_proto_;

4、原型是一个对象,我们可以重写该对象;

本人JS新手有误之处还请谅解,顺便指出错误,让我们这些新手有进步!