Javascript原型

原创 2016年06月01日 16:04:45

一、 说明

由于proto仅谷歌等部分浏览器支持,而IE低版本不支持,所以为了表述方便,下面说对象的原型时,直接就用prototype(如果偶尔看到proto,可能是笔误,也请不要深究,原理能明白即可)
绘图说明:像下面这样的,表示t2的原型prototype指向SuperType的原型

这里写图片描述

二、 初识原型

JS的function类型会内置一个特殊的属性prototype。通常prototype是Object

对象。而object有proto,等于将其new出来的function的prototype。即,如
果有类
  function Test(){}
  var t=new Test();
  则Test.prototype==t.proto将会是true。也可以这样表述:
t.constructor.prototype== t.proto,结果为true
注:proto并不是js的标准属性,所以仅谷歌火狐等部分浏览器支持此属性,IE低版本并不支持这个属性。所以IE低版本下,t.proto是会报错的,不过可以通过Object.getPrototypeOf进行获取。

示例:

function SuperType(){};
SuperType.age=18;
SuperType.prototype.color=[“red”,”blue”];

var t1=new SuperType ();
t1.name=”hh”;
var t2=new SuperType ();
t2.qq=”aa”;

SuperType,t1,t2原型关系示意图:
这里写图片描述

定义function SuperType时,编译器会为其分配一个prototype属性,并在内存
中开辟一片区域用于存放SuperType的原型的数据,假设这片区域的地址为0xA。
然后让SuperType的prototype指向0xA。由SuperType创建出来的t1和t2的
prototype也指向内存地址0xA。如果有SuperType.prototype.color.push(“green”),则
t1和t2的color也会变为[“red”,”blue”,”green”]。因为SuperType,t1,t2的原型指
向的是同一个地址的数据。这个过程可以这么比喻:把原型比喻为你的银行卡账户,
假设有一万块钱。当有人盗刷你的银行卡,刷走了4000,然后你去银行查看你账
户余额,当然的会剩下6000。因为你跟盗刷你银行卡的人所持的银行卡指向的是
同一个账户。
SuperType.prototype.color.push(“green”)后,示意图如下:
这里写图片描述

另外要注意的是,prototype也是object类型的,是Object的一个实例。所以完整示意图如下:
这里写图片描述


三、 原型链

其实在上面的示意图中,顺着箭头走,如Test—>Test的原型—>Object的原型,就是一条原型链了。当然t2—>Test的原型—>Object的原型也是一条原型链。下面对原形链进行补充,说明原型链在继承体系中是怎么工作的。
示例:

function SuperType(){};
SuperType.age=18;
SuperType.prototype.color=[“red”,”blue”];

function SubType(){}
SubType.property=”property”;
SubType.prototype=new SuperType();
SubType.prototype.test=function(){alert(“test”);}

function Child(){}
Child.prototype=new SubType();

var t1=new SuperType ();
t1.name=”hh”;
var t2=new SuperType ();
t2.qq=”aa”;

var s=new SubType();
var c=new Child();

注意,SubType的原型被重新赋值了,SubType.prototype=new SuperType();
所以SubType的原型是SuperType的一个对象实例。
上述代码的原型链示意图如下:
这里写图片描述

说到这里,原型链已经呼之欲出了。假设有这样的调用,c.toString();于是就会从上图的c开始沿着箭头到Child的原型,再到SubType的原型,到最后Object的原型,找到toString,然后执行。如果到Object的原型还没找到toString,那么就会报错。如果是这么调用,c.test(),那么就会沿着原型链,找到SubType的原型,从中找到test,然后执行。
从上图也可以看到,SuperType的age,只能SuperType自己引用。t2的qq也只能t2引用。它们都是实例属性。这便是原形链的工作机制。


四、重置原型

要重置原型,只要对原型重新赋值即可。例如:

function Person(){}
Person.prototype={
          name:”Leo”
}

需要注意的是,Person原型重写后,Person的原型为{ name:”Leo”}。{ name:”Leo”}是一个匿名的Object实例,所以其constructor,为Object。也就是说重写后Person的原型的constructor为Object。如果constructor很重要,可以为其增加一个constructor属性,如下:

Person.prototype={
          name:”Leo”,
          constructor:Person
}

不过此时还会有一个问题,这样设置的constructor将会使constructor变成是可枚举的。所以,如果想让它变为不可枚举的,可用Object.defineProperty进行设置


五、原型的动态性

示例:

function Person(){}
var p=new Person();
Person.prototype.sayHi=function(){alert(“Hi”);};
p.sayHi();//这里没问题,因为p在调用sayHi时,会先从自身找sayHi,找不到则会沿着原形链去寻找,
          //然后在Person的原型中找到了sayHi,于是就执行

原型动态性之——-重写原型的问题

function Person(){}
var p=new Person();
Person.prototype={
sayName: function(){alert(“Hi”);},
constructor:Person
}
p.sayName();//此时将会报错,p没有sayName方法

这里为何p.sayName会报错,Person的原型不是有sayName么?
且看下面的示意图:
这里写图片描述

从图中可以看出Person重写原型后,又分配了一片内存区域,用于存储新的Person原型,原先的Person原型依然存在,p指向的就是原先的没有sayName属性的Person原型。所以p调用sayName会找不到这个属性,于是报错。


六、原型共享所引发的问题

原型的优点就是共享。例如:

function Person (){}
Person.prototype.sayName=function(){};
var p1=new Person ();
var p2=new Person ();

在原型中定义sayName函数,于是p1,p2对象有同一个sayName。而不会在内存中分配两次内存来分别存放p1的sayName和p2的sayName。而缺点也是由共享所致。共享对于函数而言,是合适的,但是对于其他属性而言,可能就会出问题。
示例如下:

function Person(){}
Person.prototype.color=[“red”,”green”];
var p1=new Person();
var p2=new Person();
alert(p1.color);//输出red,green
alert(p2.color); //输出red,green
p2.color.push(“blue”);
alert(p1.color); //输出red,green,blue。注意,这里我并没有更改p1的color
alert(p2.color); //输出red,green,blue

这里我们可以发现,p2的color进行更改之后,p1的color也跟着更改,这正是原型共享所引发的问题


七、原型链与instanceof实现原理

本节参考:http://www.ibm.com/developerworks/cn/web/1306_jiangjj_jsinstanceof/

7.1 ECMAScript-262 edition 3 中 instanceof 运算符的定义(略复杂,可跳过此节,感兴趣读者可阅读)
11.8.6 The instanceof operator
The production RelationalExpression:
RelationalExpression instanceof ShiftExpression is evaluated as follows:

  1. Evaluate RelationalExpression.
  2. Call GetValue(Result(1)).// 调用GetValue方法得到 Result(1) 的值,设为 Result(2)
  3. Evaluate ShiftExpression.
  4. Call GetValue(Result(3)).// 同理,这里设为 Result(4)
  5. If Result(4) is not an object, throw a TypeError exception.
    // 如果 Result(4) 不是 object,抛出异常
    /* 如果 Result(4) 没有 [[HasInstance]] 方法,抛出异常。规范中的所有 [[…]] 方法或者属性都是内部的,
    在 JavaScript 中不能直接使用。并且规范中说明,只有 Function 对象实现了 [[HasInstance]] 方法。
    所以这里可以简单的理解为:如果 Result(4) 不是 Function 对象,抛出异常 */
  6. If Result(4) does not have a [[HasInstance]] method,
    throw a TypeError exception.
    // 相当于这样调用:Result(4).[HasInstance]
  7. Call the [[HasInstance]] method of Result(4) with parameter Result(2).
  8. Return Result(7).

    // 相关的 HasInstance 方法定义
    15.3.5.3 [[HasInstance]] (V)
    Assume F is a Function object.// 这里 F 就是上面的 Result(4),V 是 Result(2)
    When the [[HasInstance]] method of F is called with value V,
    the following steps are taken:

  9. If V is not an object, return false.// 如果 V 不是 object,直接返回 false
  10. Call the [[Get]] method of F with property name “prototype”.
    //用[[Get]]方法取F的 prototype 属性
  11. Let O be Result(2).//O = F.[[Get]](“prototype”)
  12. If O is not an object, throw a TypeError exception.
  13. Let V be the value of the [[Prototype]] property of V.//V = V.[[Prototype]]
  14. If V is null, return false.
    // 这里是关键,如果 O 和 V 引用的是同一个对象,则返回 true;否则,到 Step 8 返回 Step 5 继续循环
  15. If O and V refer to the same object or if they refer to objects
    joined to each other (section 13.1.2), return true.
  16. Go to step 5.

7.2 由规范得到的instanceof实现

//注:如果想写得更通用些,下面的L.__proto__可以换成L.constructor.prototype
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
  var O = R.prototype;// 取 R 的显示原型
  L = L.__proto__;// 取 L 的隐式原型
  while (true) { 
    if (L === null) 
      return false; 
    if (O === L)// 这里重点:当 O 严格等于 L 时,返回 true 
      return true; 
    L = L.__proto__; 
  } 
 }

7.3 instanceof实现原理说明
假设a instanceof b,那么会从a的原形链中找出是否有跟b的原型相等的原型,如果找到则返回true,否则返回false。
示例如下:
function SuperType(){}
function SubType(){}
function Test(){}
SubType.prototype=new SuperType();
var s=new SubType();
console.log(s instanceof SubType);//打出true,这是因为s的原型等于SubType的原型
SubType.prototype=new SuperType();
console.log(s instanceof SubType);//打出false,这是因为SubType的原型变了,而s还//是原来的SubType的原型

上面代码的工作过程是这样的:
先说s的原型:s由SubType构造而来,SubType原型由SuperType构造而来。所以有
这样的关系(注:对象并没有prototype属性,但是在谷歌火狐等浏览器有proto
proto又不是标准属性,IE低版本浏览器并不支持,所以为表述方便,下面表
示s的原型直接用prototype),
①、s.prototype=SubType.prototype= new SuperType();
②、s.prototype.prototype=SuperType.prototype;

于是第一个s instanceof SubType时,会先这样判断s.prototype是否跟
SubType.prototype是同一个对象,用表达式表示的话就是这样,判断
s.prototype==SubType.prototype,如果相等结果返回true。在第二次
s instanceof SubType时SubType.prototype重新new了一次,于是
s.prototype==SubType.prototype就返回false了。但是s的原形链还没到终点,于是再判断s.prototype.prototype==SubType.prototype,此时仍然是false,而s的原形链也已经到了终点,于是返回false,所以第二次s instanceof SubType的运算结果就是false。以此类推,如果是s instanceof SuperType那么会先这样
s.prototype==SuperType.prototype显然是false,于是沿着原形链,继续判断s.prototype.prototype==SuperType.prototype,此时返回true。于是
s instanceof SuperType的结果就是true。

说明:
s是object类型,所以s的原型是不可以直接引用的,即s.prototype是会报错的,这里这么表示只是为了不想画图,又要表述方便,所以采用这种方式进行说明。

八、Function与Object互为实例

1、Object及所有的其他类都是Function的实例
当执行Object instanceof Function,将会为true。这是因为
Object.proto===Function.prototype,实际上所有类的proto都是全等于
Function.prototype,所以所有的function类型都是Function的实例,执行instanceof Function都会返回true

2、Function也是Object的实例
Function.proto.proto===Object.prototype执行结果为true。
所以根据前面instanceof的实现函数,执行结果将会为true,即
Function instanceof Object将会为true

所以Function与Object是互为实例的

九、Function与function

类由Function构造而来,比如定义一个Test类:
function Test(str){console.log(str)}
当你这么定义Test类时,JS底层将会利用Function类将其构造出来,所以上面这句等价于:
var Test = new Function(‘str’,’console.log(str)’);
所以,Test是Function的实例,Test.constructor为Function,既然Test也可以是
实例,那么就会有proto这样的属性,前面已经知道,Test还会有prototype。所
以Test. proto===Function.prototype

十、混沌初开

1、无极
null,undefined

2、无极生太极:
最初的对象,其实就是Object的原型
var prototype={toString等};
prototype.prototype=null;
prototype.proto=undefined;

3、太极生两仪:
Function.prototype=Function.proto;
Function.prototype.prototype=undefined;
Function.prototype.proto= prototype;
有了Function后,再由Function创建Object
function Object(){}
Object.prototype=prototype

4、两仪生四象
function Array(){}
function Boolean(){}
function Date(){}
等系统类,其构造过程跟Object相同,都有Function构造而来,它们的原型都指
向最初那个原型prototype
当你定义其他类的时候,其实也是一样的过程,比如:
function Test(str){console.log(str)}
在JS底层会调用Functon类将Test类创建出来:
var Test= new Function(‘str’,’console.log(str)’);
在创建过程中,会给Test内置一个prototype,这个prototype指向最初的prototype,
也就是Object的原型,实际上,Object除了原型,基本什么都没有。而new操作
会为Test增加一个proto属性,Test.proto=Function.prototype

5、四象生八卦
有了类之后就可以创建各种各样的对象了。对象创建的方式很多,比如new,字
面量,Object.create等。

彻底理解JavaScript原型

 原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有"[[prototype]]"属性,函数对象有"prototype"属性,原型对象有"constructor...
  • wxw_317
  • wxw_317
  • 2015年11月03日 16:14
  • 5556

JavaScript中原型对象的彻底理解

一、什么是原型 原型是Javascript中的继承的继承,JavaScript的继承就是基于原型的继承。 1.1 函数的原型对象​ 在JavaScript中,我们创建一个函数A(就是声明一个...
  • u012468376
  • u012468376
  • 2016年11月10日 20:43
  • 10582

JavaScript原型,原型链 ? 有什么特点?

没有图片的代码可以发到以下两种在线工具,获取其他帮助在线调试。 www.jsbin.com,不需要登录,即发即预览,速度不靠谱; runjs.cn,国内访问速度快,可以上传图片,需要登录,需要用户...
  • camillea
  • camillea
  • 2016年02月04日 18:01
  • 6704

JavaScript prototype原型和原型链详解

用过JavaScript的同学们肯定都对prototype如雷贯耳,但是这究竟是个什么东西却让初学者莫衷一是,只知道函数都会有一个prototype属性,可以为其添加函数供实例访问,其它的就不清楚了,...
  • i10630226
  • i10630226
  • 2015年10月21日 14:25
  • 1465

彻底理解javascript中的原型链

要弄清楚原型链就要先弄清楚 function 类型,在javascript中没有类的概念,都是函数,所以它是一门函数式的编程语言。类有一个很重要的特性,就是它可以根据它的构造函数来创建以它为模板的对象...
  • ljl157011
  • ljl157011
  • 2014年02月22日 13:38
  • 12352

用通俗易懂的语言介绍JavaScript原型

此位译文 原型(prototype)是每个JavaScript开发人员必须理解的基本概念,本文的目标是通俗而又详细地解释JavaScript的原型。如果你读完这篇博文以后还是不理解JavaSc...
  • developer_biao
  • developer_biao
  • 2014年06月23日 00:35
  • 1566

基于原型的JavaScript面向对象

1、前言 从JavaScript开始流行到今天,学习它的人都会有个疑惑——JavaScript是不是面向对象语言?其实,ECMA-262早就给出了答案,在ECMAScript的第一个版本中就明确指出...
  • zqjflash
  • zqjflash
  • 2014年03月30日 12:28
  • 764

JS重点整理之JS原型链彻底搞清楚

彻底搞清楚原型链。看图理解原型链。从对象、原型对象、原型链进行一步步分析,每一块都有根据自己的理解画图,更容易理解,又有例子代码佐证。 最普通的对象:有__proto__属性(指向其原型链...
  • sinat_21274091
  • sinat_21274091
  • 2016年10月06日 01:58
  • 8698

JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)

这篇文章主要介绍了面向对象JS基础讲解,工厂模式、构造函数模式、原型模式、混合模式、动态原型模式,需要的朋友可以参考下 什么是面向对象?面向对象是一种思想!(废话)。   面向对象...
  • Willian1st
  • Willian1st
  • 2016年06月28日 13:48
  • 974

Javascript中的原型链、prototype、__proto__的关系

javascrip中内置的对象共12个,其中10个属于函数类型——String,Number,Boolean,Array,Function,Date,RegExp,Error,Object,Event...
  • u010089686
  • u010089686
  • 2016年10月06日 16:07
  • 1079
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Javascript原型
举报原因:
原因补充:

(最多只允许输入30个字)