资讯专栏INFORMATION COLUMN

浅谈javascript中的prototype

seanHai / 2836人阅读

摘要:因此,请慎重重写原型继承说到就不得说继承,我们通过给上添加属性和方法,就能使该构造函数所有的实例对象拥有属性和方法。

本人博客:【www.xiabingbao.com】

在本文中,我们讲解prototype的内容主要由:什么是prototype,prototype与函数之间的关系,prototype与实例对象之间的关系,使用proto实现一个简单的继承。

1. prototype的简要介绍

在javascript中,创建的每个函数天生都自带着一个prototype属性。这里我们要强调的是:这个prototype属性是一个指针,指向一个对象,在这里,我们称指向的这个看不到但确实存在的对象为原型对象。其实可以用下面一个简单的例子来说明:

var proto = {
    name : "wenzi",
    age : 25
}
function Person(){

}
Person.prototype = proto;

这样Person.protptype就指向到了proto,如果还有其他的引用(A)也指向到了proto,那么这个引用和Person.prototype就是相等的:A==Person.prototype

prototype指向的这个对象是真实存在的,可能很多的同学对prototype属性和原型对象有些混淆,我们在这里把原型对象叫做大SB大SB与普通对象的一个不同之处就是,他也有一个天生的属性:constructor,这个constructor重新指回到了函数。不过,既然大SB也是一个对象,那么它也是继承于Object的,拥有Object所有的方法和属性,比如toString()等。

大SB = {
    constructor : Person,
    say : function(){
        return "hello world";
    }
}

那么现在我们就能得到两个引用关系:

大SB = Person.prototype; // 原型对象 = 函数.prototype;
Person = 大SB.constructor(Person.prototype.constructor); // 函数 = 原型对象.constructor(函数.prototype.constructor)

从运行的代码中,可以验证我们的观点,Person.prototype的类型是object,Person.prototype.constructor重新指回到了Person。
其实在实例对象中,也存在着一个属性指向到大SB中:

var John = new Person();
大SB = John.__proto__;

即 John.__proto__ 和 Person.prototype 指向的是同一个引用:John.__proto__==Person.prototype

2. 对象实例和prototype中的属性

在构造函数能设置属性和方法,在prototype中也能设置属性和方法,那new出的对象使用的是哪个呢?我们来看一个例子:

function Person(){
    this.name = "wenzi";
}
Person.prototype.name = "bing";
Person.prototype.say = function(){
    return "hello world";
}
var John = new Person();
alert(John.name);  // "wenzi"
alert(John.say()); // "hello world"

从运行的结果我们可以看到,John.name输出的是构造函数中的属性值"wenzi",John.say()输出的是"hello world"。这是因为,当读取某个对象的属性值时,会首先在实例对象中进行搜索,若搜索到则直接进行返回;若搜索不到则去原型对象中进行寻找。因此在使用John调用say()方法时是正确的,不会报错。而且是进行了两次的搜索。

虽然say()是挂载在prototype上,John也同样能直接进行访问。但是,我们不能直接对John.say进行修改从而影响prototype的say()方法,如下:

John.say = function(){
    return "this has changed";
}
John.say(); // "this has changed"
var Tom = new Person();
Tom.say(); // "hello world"

从运行John.say()的结果来看,say()方法确实发生了变化。但是,当我们再new出一个新对象Tom时,Tom.say()返回的还是"hello world",这是为什么呢?

因为,对John.say()进行修改时,不是修改了prototype上的say(),而是给John这个实例对象添加了一个say()方法,从而屏蔽了prototype上的say()方法,由上面的寻找顺序我们可以知道,若实例对象存在这个方法就直接返回了,不再去原型对象上寻找。而new出的新对象Tom在调用say()方法时,Tom自己是没有say()方法的,只能去原型对象上寻找,因此返回的还是"hello world"。所以,对John.say进行修改时,不是修改了prototype上的say()方法,而是给John添加了实例方法,屏蔽掉了prototype上的方法而已。那如何才能修改prototype上的方法呢,好办,直接在prototype上修改:

Person.prototype.say = function(){
    return "wenzi"s blog";
}

这样就能修改prototype的say()方法了。

3. 重写prototype

在上面的例子中,我们都是大部分给prototype添加属性或方法,本来prototype指向的是大SB,若我们给prototype添加属性或方法时,就是给大SB添加属性或方法:

Person.prototype.name = "wenzi"; 

大SB里其他的属性和方法是不受影响的,constructor依然指回到Person。但是,若我们这样写:

Person.prototype = {
    name : "wenzi",
    say : function(){
        return "my name is name"
    }
}

就是对Person的prototype重写了,让prototype进行了重新的指向,本来Person.prototype指向的是大SB,可是现在却指向了CTM,而CTM里也没有constructor属性指回到Person,若是想重新指回到Person,还得手动添加一个constructor属性:

Person.prototype = {
    constructor : Person, // 重新指回到Person
    name : "wenzi",
    say : function(){
        return "my name is name"
    }
}

这样就能手动构造出新的原型对象了,new出的实例对象也能使用这个prototype上的属性和方法。

但是这里还存在一个问题,若之前已经有new出的实例对象,然后再修改Person.prototype,之前的实例对象是无法使用新的原型对象(CTM)上的属性和方法的,因为之前的实例对象的__proto__指向的依然是大SB。因此,请慎重重写prototype

4. 原型继承

说到prototype就不得说prototype继承,我们通过给prototype上添加属性和方法,就能使该构造函数所有的实例对象拥有属性和方法。我们先来看下面的代码:

function Father(){
    this.name = "father";
    this.age = 43;
}
Father.prototype.job = "Doctor";
Father.prototype.getName = function(){
    return this.name;
}

function Son(){
    this.name = "son";
}
Son.prototype = new Father(); // Son的prototype指向Father的实例对象
var John = new Son();
for(var k in John){
    console.log(k+" : "+john[k]);
}
/*
    输出结果:
    name : son
    age : 43
    job : Doctor
    getName : function (){
        return this.name;
    }
*/

从上面的例子中可以看到,Son的实例对象继承了Father中所有的属性和方法。当然,若Son的对象实例中存在的,还依然保留。不过Son原型中的属性和方法是会被彻底覆盖的。我们来分析一下这是为什么?

Son的prototype指向的是Father的一个实例,我们把这拆成两步:

var father = new Father();
Son.prototype = father;

在实例father中既有name,age属性,也有Father.prototype中的属性和方法,我们对father循环看一下:

for(var k in father){
    console.log(k, father[k]);
}
/*
    name father
    age 43
    job Doctor
    getName Father.getName()
*/

由于constructor是不可枚举的类型,在for~in循环里是输出不了constructor的,其实father是这样的:

father = {
    constructor : Father,
    name : father
    age : 43
    job : Doctor
    getName : Father.getName()
}

因此Son的prototype指向的就是上面father的内容。到此,Son的prototype中的constructor的指向也发生了改变,本来Son.prototype是指向到Son的,现在指向到了Father。即完全重写了Son的prototype,重新指向了另一个引用。所以,我们就能知道为什么对象实例中的属性能够保存,而prototype中的会彻底被删除了。

如果想给Son添加原型方法或属性,那只能是在Son.prototype = new Father();之后进行添加:

Son.prototype = new Father();
Son.prototype.getJob = function(){
    return this.job;
}

这样就能添加上getJob方法了。

到这里,会有人问,如果Son.prototype = Father.prototype会怎么样呢?我们刚才也说了Father.prototype指向的是大SB,而大SB也是一个实例对象,是Object的一个实例,这就相当于:

Son.prototype == 大SB; // 指向了大SB

因此,如果是Son.prototype = Father.prototype的话,那么Son只能继承到Father.prototype上的jobgetName(),其他的属性是获取不到的。

5. 总结

本文简单的介绍了一下prototype,让我们能对prototype有个浅显的认识。当然,博主才疏学浅,文章里会有很多疏漏和不完善的地方,欢迎大家批评指正。

本人博客:【www.xiabingbao.com】

欢迎大家关注我的微信公众号:wenzichel

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85740.html

相关文章

  • 浅谈JavaScript原型链

    摘要:在这其中我们就逃不开要讨论继承原型对象构造函数实例了。想要获得某一类型的构造函数可以用来获得,也可以对该属性进行赋值操作。三上面就提到一点是指构造函数的原型对象,它是一个对象,它是构造函数的属性。 原型链这一个话题,需要和很多概念一起讲,才能串成一个比较系统的知识点。在这其中我们就逃不开要讨论继承、原型对象、构造函数、实例了。 一、构造函数 构造函数是一类特殊的函数,它的作用是用来生成...

    xumenger 评论0 收藏0
  • 浅谈JavaScript继承

    摘要:组合方式实现继承原型链构造函数喵喵喵汪汪汪与的唯一区别是多了这一句组合方式实现了对构造函数内和原型上所有属性和方法的继承,并且的实例对象之间也不会相互干扰。 前言 关于JavaScript继承相关的定义和方法网上已经有很多解释啦,本菜鸟就不抄抄写写惹人嫌了,本文主要探讨三种基本的继承方式并且给出优化方案。 正文 借助构造函数实现继承 function Parent1() { ...

    Jingbin_ 评论0 收藏0
  • 浅谈 JavaScript 原型链

    摘要:数组的构造函数是原型链的指向与其他除以外的构造函数相同,的也指向顶级原型对象,每一个数组都是的实例,都指向。实例对象查找构造函数原型对象的方法一般会把对象共有的属性和方法都放在构造函数的原型对象上。 showImg(https://segmentfault.com/img/remote/1460000018998704?w=900&h=506); 阅读原文 概述 在 JavaScr...

    explorer_ddf 评论0 收藏0
  • 浅谈面向对象的javascript几个特性

    摘要:中的和是一门很灵活的语言,尤其是。即然是面向对象的编程语言,那也是不可或缺的。在中,永远指向的是他的调用者。定义是存在于实例化后对象的一个属性,并且指向原对象的属性。我们在扩展的时候,同时父类也会有对应的方法,这很显然是一个很严重的问题。 javascript中的this和new javascript是一门很灵活的语言,尤其是function。他即可以以面向过程的方式来用,比如: f...

    JayChen 评论0 收藏0
  • 浅谈 javascript 中的this绑定问题

    摘要:绑定使用方式进行调用函数时,会发生构造函数的调用。先上图,然后根据文字阅读使用调用函数之后,该函数才作为构造函数进行调用,构造一个全新的对象赋值给,而对象的指向了的对象,的对象有一个属性指向的构造函数这个就是的原型链,也是的特性。 javascript语言是在运行时前即进行编译的,而this的绑定也是在运行时进行绑定的。也就是说,this实际上是在函数被调用时候发生绑定的,它指向什么完...

    duan199226 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<