`

浅谈JS原型链

 
阅读更多

浅谈JS原型链

原型链

ECMAScript中描述了原型链的概念。我们知道ECMAScript并不像C++,Java那样使用类,但是对象仍然可以通过多种方式创建,其中就有构造函数方式。每个构造函数都有一个原型对象,同时都有一个prototype属性, prototype属性指向构造函数的原型对象,它被用来实现基于原型的继承和共享。而原型对象又都默认会取得一个constructor属性,这个属性包含一个指向构造函数(prototype属性所在函数)的指针。每个通过调用构造函数创建的实例对象都拥有一个指向原型对象的指针,ECMA-262第5版中叫这个指针为[[prototype]],虽然在脚本上没有标准的方式访问[[prototype]],但Chrome、Firefox和Safari在每个对象上都支持一个属性_proto_,而在其他实现中,这个属性对脚本是完全不可见的。假如原型对象等于另一个类型的实例,那么它就拥有指向创建该实例的构造函数的原型对象的指针,依此类推,就形成了一条指针链,这就是原型链的概念。通过下面的图形我们可以更清晰地了解原型链的概念。

ECMA5中可以使用Object.getPrototypeOf()来获取实例的构造函数的prototype

事实上,上图所展示的原型链还少一环。我们知道,所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。函数是可调用的对象,所有函数的默认原型对象都是Object的实例,所以函数的原型对象都会包含一个指向Object构造函数的原型对象的指针,也即指向Object.prototype的指针[[prototype]]。这样就解释了为什么所有自定义对象类型都会继承toLocaleString()、toString()等Object原型对象的默认方法了。还是来上图吧。

当然,还有很重要的一点是我们需要注意的:对象实例中的指针[[prototype]]只指向原型对象,并不指向构造函数。

原型语法

通常,我们可以用一个包含所有属性和方法的对象字面量来重写整个原型对象。例如

1 function Person(){}
2 Person.prototype = {
3   name: "bella",
4   age: 21,
5   sayHello: function(){
6     alert(this.name);
7   }
8 }

不过,我们需要注意的是,重写之后,构造函数Person的原型对象的constructor属性不再指向Person了,因为该语法的本质是完全重写了默认的原型对象,所以constructor属性也就变成了新对象的constructor属性,指向Object构造函数,我们此时就不能通过constuctor来确定对象的类型了。

可以通过Person.prototype.constructor = Person恢复constructor的指针。

原型的动态性

原型在查找值的过程中是一次搜索,当我们想引用一个对象的某个属性时,所引用到的是原型链中包含该属性名的第一个对象所对应的属性值。换句话说,直接引用这个属性的对象会首先被查询是否包含该属性名,如果包含,该属性值就是我们想获取的,查询停止,如果不包含,会接着查询该对象的原型是否包含该属性,依此类推。

我们可以随时动态地为原型添加属性和方法,而且,基于这种搜索过程,我们对原型对象所做的任何修改都能立即从对象实例上看到,即使该修改是在创建实例之后。但如果是用上面提到的语法重写整个原型对象就另当别论了。因为重写原型对象会切断现有原型对象与原来已经存在的任何对象实例之间的联系,它们包含的指针[[prototype]]仍然指向原来的原型对象,我们可以看看下面的小例子。

01 function Person(){}
02 var person1 = new Person();
03 Person.prototype = {
04   name: "bella",
05   age: 21,
06   sayHello: function(){
07     alert(this.name);
08   }
09 }
10 person1.sayHello();  //error

上面的例子中,我们先创建了Person的一个实例对象person1,然后重写了Person的原型对象,之后再调用person1.sayHello()就会发生错误。因为person1中包含的指针[[prototype]]仍然指向原来的原型对象,并不包含新的原型对象中定义的sayHello属性。

原型的问题

原型模式使得所有对象实例在默认情况下取得相同的属性值,对于属性值为函数的情况,这正是我们希望看到的,所有对象实例共享这一函数而不需要重复定义,但是对于属性值为基本值的情况,我们通常希望不同的对象实例拥有不同的基本值,不过,我们可以通过在对象实例上添加同名属性来隐藏原型对象中的属性。但是,如果包含引用类型值的属性,问题就显现出来了。

01 function Person(){}
02 Person.prototype = {
03   name: "bella",
04   age: 21,
05   classmates: ["Lucy", "Lily"],
06   sayHello: function(){
07     alert(this.name);
08   }
09 }
10 var person1 = new Person();
11 var person2 = new Person();
12 person1.classmates.push("Mark");
13 alert(person1.classmates === person2.classmates);  //true

这里,我们为Person.prototype对象添加了classmates属性,值为一个字符串数组,然后创建了两个对象实例person1, person2。由于person1, person2所拥有的classmates属性其实是共享原型对象Person.prototype的classmates属性得到的,也就是数组只存在于Person.prototype对象中,person1和person2引用的是同一个数组,对person1中classmates的修改也会从person2.classmates中反映出来,这样会导致所有对象实例共享一个数组,这往往不是我们想要的。

以上,我只是简单地分析了原型链的概念和原型对象的基本特性,希望能对大家有小小的帮助,想要更深刻地认识它,当然还是得靠大家在实际项目中去学习和体会。

 

参考资料:Standard ECMA-262,JavaScript高级程序设计。

分享到:
评论

相关推荐

    浅谈javascript原型链与继承

    在JavaScript中,原型链与继承是核心概念之一。原型链是JavaScript实现继承的机制,它允许一个对象访问另一个对象的属性和方法。继承是指新创建的对象可以继承其原型对象的属性和方法。下面详细探讨这两个概念。 **...

    浅谈JS原型对象和原型链

    JavaScript中的原型对象和原型链是理解其面向对象特性的核心概念。万物皆对象,而对象又分为普通对象和函数对象。函数对象通过`new Function`创建,如`f1`、`f2`和`f3`,而普通对象则通过对象字面量或`new`表达式...

    浅谈JavaScript对象之教学.pdf

    浅谈JavaScript对象之教学 本文旨在探讨JavaScript对象的教学要点,旨在帮助高职软件技术专业的学生更好地理解和掌握JavaScript语言。文章从对象的概念、使用原生对象、原型链的理解、自定义对象的创建和新的语法...

    浅谈javascript面向对象编程

    ### 浅谈JavaScript面向对象编程 #### 一、概述 面向对象编程(OOP)是一种编程范式,它使用“对象”来设计软件。在JavaScript中,尽管语言本身并不支持传统的类(class)概念,但开发者可以通过原型继承和其他技术来...

    浅谈js对象的创建和对6种继承模式的理解和遐想

    本文将探讨JavaScript中对象的创建方式以及六种常见的继承模式,包括原型链继承、借用构造函数继承、组合继承、原型式继承、寄生式继承以及寄生组合式继承。 首先,让我们从对象的创建开始谈起。在JavaScript中创建...

    浅谈JavaScript对象与继承_.docx

    关于对象继承,JavaScript采用的是原型链机制。每个对象都有一个`__proto__`属性,指向它的原型。当试图访问对象的一个属性时,如果对象本身没有该属性,JavaScript会查找`__proto__`,如果`__proto__`也没有,就会...

    浅谈JavaScript 覆盖原型以及更改原型

    即使删除了对象实例上的`sentence`属性,JavaScript引擎依然会从原型链中找到这个属性,因此`firstPrisoner.sentence`仍能输出4。 如果改变原型对象`proto`的`sentence`属性值,例如将其改为5,那么所有通过`...

    浅谈js构造函数的方法与原型prototype

    JavaScript对象在查找属性或方法时,会先在对象自身上查找,如果没有找到,再沿着原型链向上查找,直到找到所需的属性或方法,或者到达原型链的末端。 使用原型链的一个好处是,我们可以在原型对象上定义方法,这样...

    浅谈javascript的原型继承

    在JavaScript中,对象可以通过原型链相互关联,从而实现属性和方法的共享。本文将深入探讨JavaScript的原型继承机制,并通过示例代码来展示不同的实现方式。 首先,让我们理解JavaScript的原型(prototype)。每个...

    浅谈JS的原型和继承

    在JavaScript中,所有的对象都源自`Object.prototype`,而`Object.prototype`的`__proto__`属性指向`null`,这构成了原型链的顶端。因此,无论对象如何复杂,沿着`__proto__`链追溯,最终都会到达`null`。 总结一下...

    浅谈JavaScript函数的四种存在形态_.docx

    在JavaScript中,函数是一种非常重要的数据类型,它们可以以多种形态...它们不仅影响函数的行为,还与面向对象编程、原型链、闭包等核心概念紧密相关。因此,开发者应当熟练掌握这些知识,以便在实际开发中灵活运用。

    浅谈Javascript的静态属性和原型属性

    当一个实例对象需要使用某个属性或方法时,如果它自身不具备,那么Javascript引擎会在其原型链上查找该属性或方法。原型属性的定义方式如下: ```javascript Atest.prototype.sex = "男"; ``` 调用原型属性时,...

    浅谈Javascript实现继承的方法

    原型链继承是Javascript中最基本的继承方式。它利用了原型对象的特性,即所有对象实例共享同一个原型对象上的属性和方法。原型链继承通过将子构造函数的prototype属性指向一个父构造函数的实例来实现。这样,所有...

    浅谈Javascript面向对象编程

    标题《浅谈Javascript面向对象编程》涉及了JavaScript语言在实现面向对象编程(OOP)方面的核心概念及其灵活运用。接下来,我们详细地梳理一下从给定文件内容中提取的关于JavaScript面向对象编程的知识点。 1. 数据...

    浅谈Javascript中Object与Function对象

    当尝试访问一个对象的属性时,JavaScript会首先在该对象自身查找,如果找不到,就会沿着原型链向上查找,直到找到属性或到达原型链的顶端(即`null`)。例如: ```javascript Object.prototype.a = 3.14; alert(...

Global site tag (gtag.js) - Google Analytics