资讯专栏INFORMATION COLUMN

深究JavaScript——原型:prototype、constructor、__proto__

jeffrey_up / 2059人阅读

摘要:是构造函数的属性,而不是实例对象的属性,指向原型对象。这个连接存在于实例与构造函数的原型对象而不是构造函数之间,也就是说实例对象与构造函数没有直接关系。

prototype、constructor、__proto__

  prototype是站在构造函数的角度讨论原型对象的,用来实现基于原型的继承与属性的共享。__proto__是站在实例对象的角度讨论原型对象,构成原型链,同样用于实现基于原型的继承。

function Person(){}
var p = new Person();
Person.prototype

Person.constructor

p.prototype

p.constructor

p.__proto__

通过new构造函数实例化得到对象。

prototype是构造函数的属性,而不是实例对象的属性,指向Person.prototype原型对象。

实例对象含有一个Constructor属性指向该对象的构造器。

p对象有个__proto__内部属性指向Person.prototype。

p.__proto__、p.constructor.prototype、Person.prototype指向Person的原型对象

  每个构造函数都有一个phototype属性(通过Function.prototype.bind方法构造出来的函数以及Object.create(null)例外,没有prototype属性),这个属性是一个指针,指向一个包含特定类型的所有实例共享属性和方法的对象。通过prototype对象可以返回对象的原型对象的引用。

  每个实例对象都有一个constructor属性指向prototype属性所在函数的指针。通过这个constructor(构造函数),还可以继续为原型对象添加其他属性和方法。

  每个实例对象都有一个内部属性[[prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__指针来访问。__proto__指向实例该对象的构造器的原型对象(Object.prototype 这个对象是个例外,它的__proto__值为null)。这个连接存在于实例与构造函数的原型对象(而不是构造函数)之间,也就是说实例对象与构造函数没有直接关系。并且,实例并不包含属性与方法,实例之所以能够调用原型上的方法,是依赖于查找对象属性的过程来实现的。虽然无法访问到__proto__,可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系,如果__proto__指向调用该方法的对象的prototype,返回true。

console.log(Person.prototype.isPrototypeOf(p)); //true;

  每当代码读取某个对象属性时,首先从对象实例开始,如果找到给定名字属性,返回该值;如果没有找到,继续搜索指针指向的原型对象。虽然能通过实例访问原型中的值,但不能通过对象实例重写原型中的值。

  当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为 null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。不过,使用 delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性。

function Person(){}
Person.prototype.name = "staven";
Person.prototype.say = function(){
    console.log("My name is "+this.name);
}
var p = new Person();
p.name = "赫连小妖"
p.say();  //My name is 赫连小妖
delete p.name;
p.say();  //My name is staven

  使用 hasOwnProperty()方法只在给定属性存在于对象实例中时,才会返回 true;可以检测一个属性是存在于实例中,还是存在于原型中。

  for(x in xxx)既可以检测实例属性,也可以检测自定义属性。

  hasOwnProperty()为true,属性存在在对象实例中;hasOwnProperty()为false,(x in xxx)为true,属性存在在对象的构造器原型中。

function Person(){}
Person.prototype.name = "staven";
var p = new Person();
console.log(p.hasOwnProperty("name"));    //false
console.log(("name" in p));                //true
p.name = "赫连小妖";
console.log(p.hasOwnProperty("name"));    //true
console.log(("name" in p));                //true

  ECMAScript 5 的 Object.keys()方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

function Person(){}
Person.prototype.name = "staven";
Person.prototype.say = function(){};
console.log(Object.keys(Person.prototype));        //["name", "say"]
var p = new Person();
p.sex = "man";
p.character = "handsome";
console.log(Object.keys(p));    //["sex", "character"]

  无论它是否可枚举,都可以使用 Object.getOwnPropertyNames()方法。

function Person(){}
Person.prototype.name = "staven";
Person.prototype.say = function(){};
console.log(Object.getOwnPropertyNames(Person.prototype));        //["constructor", "name", "say"]
var p = new Person();
p.sex = "man";
p.character = "handsome";
console.log(Object.getOwnPropertyNames(p));    //["sex", "character"]
使用字面量赋值原型对象
Person.prototype = {
    name : "staven",
    say : function(){
        console.log("My name is "+this.name);
    }
};

  此时constructor属性不再指向Person。尽管 instanceof操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了。

function Person(){}
Person.prototype = {
    name : "staven",
    say : function(){
        console.log("My name is "+this.name);
    }
};
var p = new Person();
console.log(p instanceof Object);        //true
console.log(p instanceof Person);        //true
console.log(p.constructor == Person);    //false
console.log(p.constructor == Object);    //true

  不过可以重新设置constructor属性,确保通过该属性的原有性。

function Person(){}
Person.prototype = {
    constructor : Person,
    name : "staven",
    say : function(){
        console.log("My name is "+this.name);
    }
};
原型的动态性

  随时可以为原型添加属性和方法,并且能够立即在所有对象实例中反映出来,但是如果重写了整个原型对象,情况就不同了。调用构造函数时会为实例添加一个[[Prototype]]指针,把原型修改为另一个对象,就相当于切断了构造函数与最初原型之间的联系。

function Person(){}
var p = new Person();
Person.prototype = {
    constructor : Person,
    name : "staven",
    say : function(){
        console.log("My name is "+this.name);
    }
};
p.say();    //Uncaught TypeError: p.say is not a function
原型对象的缺点

  省略了为构造函数传递初始化参数,结果所有实例在默认情况下都将取得相同的属性值。
  由于原型的属性共享,若在实例上添加一个同名的包含引用类型值得的属性,其他实例该属性也会被改变。

function Person(){}
Person.prototype = {
    books:["html5","css3","js"]
}
var p1 = new Person();
var p2 = new Person();
p1.books.pop();
console.log(p1.books);        //["html5", "css3"]
console.log(p2.books);        //["html5", "css3"]

☞☞☞深究JavaScript系列☜☜☜

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

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

相关文章

  • 讲清楚之 javascript原形

    摘要:构造函数和实例都通过属性指向了原形。代码示例是构造函数的实例的属性与的属性保存的值相等,即他们指向同一个对象原形。 讲清楚之javascript原型 标签: javascript javascript 中原形是一个比较难于理解的概念。javascript 权威指南在原形这一章也花了大量的篇幅进行介绍,也许你已经读过javascript 权威指南,或者已经是读第N篇了,然而这篇文章的目...

    高胜山 评论0 收藏0
  • js 原型链的那些事儿

    摘要:原型链首先,的对象普通对象和函数对象都会有属性,指向创建它的构造函数的原型对象,比如上面的例子这就形成了原型链,会一直查找原型对象的属性,直到为。,保证原型链能够正常结束。 前言 一般谈到js中的继承的时候,一定会遇到原型,原型链的问题,原型里面又有prototype,__proto__,constructor属性,讲到这儿,很多同学是不是都一头雾水,傻傻分不清楚,因为工作中用到的地方...

    fuyi501 评论0 收藏0
  • 深入JavaScript(一)this & Prototype

    摘要:然而事实上并不是。函数本身也是一个对象,但是给这个对象添加属性并不能影响。一图胜千言作者给出的解决方案,没有麻烦的,没有虚伪的,没有混淆视线的,原型链连接不再赤裸裸。所以是这样的一个函数以为构造函数,为原型。 注意:本文章是个人《You Don’t Know JS》的读书笔记。在看backbone源码的时候看到这么一小段,看上去很小,其实忽略了也没有太大理解的问题。但是不知道为什么,我...

    The question 评论0 收藏0
  • 深入理解:JavaScript原型与继承

    摘要:深入理解原型与继承看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解。 深入理解:JavaScript原型与继承 看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解。 首先JavaScript是一门基于原型编程的语言...

    mengbo 评论0 收藏0

发表评论

0条评论

jeffrey_up

|高级讲师

TA的文章

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