JavaScript 原型

转载 2015年11月19日 15:25:59

原型是一个对象,其他对象可以通过它实现属性继承。任何一个对象都可以成为原型。

所有的对象在默认的情况下都有一个原型,因为原型本身也是对象,所以每个原型自身又有一个原型(只有一种例外,默认的对象原型在原型链的顶端),这就是我们能通过原型实现继承的原因。

下面举个例子来说明原型中的东西:

复制代码
function Person() {
    Person.prototype.name = "Altria";
    Person.prototype.age = "16";
    Person.prototype.job = "Saber";
    Person.prototype.sayName = function () {
        console.log(this.name);
    }
}

var person1 = new Person();
person1.sayName();    // "Altria"

var person2 = new Person();
person2.sayName();    // "Altria"

console.log(person1.sayName == person2.sayName);  //true
复制代码

一个对象的真正原型是被对象内部的 [[Prototype]] 属性(property) 所持有。在所有实现中都无法访问到 [[Prototype]] ,但可以通过 isPrototypeOf() 方法来确定对象之间是否存在这种关系。如果 [[Prototype]] 指向调用 isPrototypeOf() 方法的对象( Person.prototype),那么这个方法就返回 true,如下所示:

console.log(Person.prototype.isPrototypeOf(person1));    //ture
console.log(Person.prototype.isPrototypeOf(person2));    //ture

这里用原型对象的 isPrototypeOf() 方法测试了 person1 和 person2 。因为他们内部都有一个指向 Person.prototype 的指针,因此都返回了true。

   

ECMAScript5 增加了一个方法 Object.getPrototypeOf() ,可以方便地取得一个对象的原型,在所有支持的实现中,这个方法返回 [[Prototype]] 的值,例如:

console.log(Object.getPrototypeOf(person1) == Person.prototype);    //true
console.log(Object.getPrototypeOf(person2).name)    //"Altria"

第一行代码确定 Object.getPrototypeOf() 返回的对象就是这个对象的原型;第二行代码取得原型对象中 name 属性的值 "Altria" 。

遇到浏览器不支持这个方法的时候,可以用非标准的访问器 __proto__ (IE不支持),如果这两者都不起作用的,我们需要从对象的构造函数中找到的它原型。下面的代码展示了获取对象原型的方法:

复制代码
var a = {}; 
 
//Firefox 3.6 and Chrome 5 
 Object.getPrototypeOf(a);   //[object Object]   
 
//Firefox 3.6, Chrome 5 and Safari 4 
a.__proto__;     //[object Object]   

//all browsers 
 a.constructor.prototype;    //[object Object]
复制代码

 

 

hasOwnProperty() 方法可以检测一个属性是存在于实例中还是存在于原型中。这个方法(是从 Object 继承来的)只在给定属性存在于对象实例中时,才会返回 true。

复制代码
function Person() {
    Person.prototype.name = "Altria";
    Person.prototype.age = "16";
    Person.prototype.job = "Saber";
    Person.prototype.sayName = function () {
        console.log(this.name);
    }
}

var person1 = new Person();
var person2 = new Person();

console.log(person1.hasOwnProperty("name"));    //false

person1.name = "Sakura";
console.log(person1.name);    //"Sakura" ----来自实例
console.log(person1.hasOwnProperty("name"));   //true

console.log(person2.name);    //"Altria" ----来自原型
console.log(person2.hasOwnProperty("name"));    //false
复制代码

 

原型实现继承

假如我们已经创建了一个实例对象 ,我们想要继承一个已经存在的对象的功能,比如说Array,我们可以像下面这样做( 在支持__proto__ 的浏览器中):

//IE中无效
var a = {};
a.__proto__ = Array.prototype;
a.length; //0

在上面这个例子中,首先创建了一个对象a,然后通过a的原型来达到继承Array 这个已经存在的对象的功能的目的。

 

但原型真正魅力体现在多个实例共用一个通用原型的时候。原型对象(也就是一个对象的原型所引用的对象)的属性一旦定义,就可以被多个引用它的实例所继承(即这些实例对象的原型所指向的就是这个原型对象)。

我们先来搞清楚 constructor.prototype 属性是什么。

javascript并没有在构造函数(constructor)和其他函数之间做区分,所以说每个函数都有一个原型属性。反过来,如果不是函数,将不会有这样一个属性:

复制代码
//普通函数不会成为一个构造函数,但是它依然可以有原型属性
Math.max.prototype;     //[object Object] 
 
//函数旨在成为一个构造函数的原型
var A = function(name) { 
 
     this.name = name; 
 
} 
A.prototype;     //[object Object]   
 
//Math 不是函数因此没有原型属性
Math.prototype;     //null
复制代码

可以这样说,函数A的原型属性(prototype property )是一个对象,当这个函数A被用作构造函数来创建实例时,该函数的原型属性将被作为原型赋值给所有对象实例(即所有实例的原型引用的是函数的原型属性)。

下面的代码可以更加详细的说明问题:

复制代码
//创建一个函数 b
var b = function () {
    var one;
}

//使用b创建一个对象实例c
var c = new b();

//查看 b 和 c 的构造函数
b.constructor;                          // function Function() { [native code]}
b.constructor == Function.constructor;  //true
c.constructor;                          //实例c的构造函数 即 b function(){ var one; }
c.constructor == b                      //true

//b 是一个函数,查看b的原型如下
b.constructor.prototype                // function (){}
b.__proto__                            //function (){}

//b 是一个函数,由于javascript没有在构造函数constructor和函数function之间做区分,所以函数像constructor一样,
//也有一个原型属性,这和函数的原型( b.__proto__ 或者 b.construtor.prototype)是不一样的
b.prototype //[object Object]   函数b的原型属性

b.prototype==b.constructor.prototype //fasle
b.prototype==b.__proto__             //false
b.__proto__==b.constructor.prototype //true

//c是一个由b创建的对象实例,查看 c 的原型如下
c.constructor.prototype              //[object Object] 这是对象的原型
c.__proto__                          //[object Object] 这是对象的原型

c.constructor.prototype==b.constructor.prototype;  //false  c 的原型和 b 的原型比较
c.constructor.prototype==b.prototype;              //true c 的原型和 b 的原型属性比较

//为函数 b 的原型属性添加一个属性max
b.prototype.max = 3
//实例 c 也有了一个属性max
c.max  //3
//上面的例子中,对象实例 c 的原型和函数的 b 的原型属性是一样的,如果改变 b 的原型属性,则对象实例 c
的原型也会改变

复制代码

 

要弄清楚 :一个函数的原型属性(function’s prototype property )其实和实际的原型(prototype)是没有关系的!

复制代码
//(例子在IE中无效) 
 var A = function(name) { 
     this.name = name; 
} 
 
A.prototype == A.__proto__;    //false 
 
A.__proto__ == Function.prototype;    //true 
// 函数 A 的原型被设置为她的构造函数的原型属性
复制代码

 

再来看看这个例子:

复制代码
//构造函数。 this 作为一个新的对象被返回,并且它内部的 [[prototype]] 属性被设置为构造函数的默认原型属性
var Circle = function(radius) {
    this.radius = radius;

//this.__proto__ = Circle.prototype; //这行是隐含的,只是为了说明而加
 }    

//拓展 Circle 的默认原型属性从而拓展每个普通实例的原型
Circle.prototype.area = function() {
    return Math.PI*this.radius*this.radius;
 }   
 
//创建两个 Circle 的实例
var a = new Circle(3), 
    b = new Circle(4);

a.area().toFixed(2);    //28.27
b.area().toFixed(2);    //50.27
复制代码

如果我更改了构造函数的原型,是不是意味着已经存在的该构造函数的实例将获得构造函数的最新版本呢?

不一定。假如修改的是原型属性,那么这样的改变将会发生。因为在 a 实际被创建之后,a.__proto__ 实际上一个是对 A.prototype 的引用。

复制代码
var A = function(name) {
    this.name = name;
 }  

var a = new A('adam');
a.name;    //'adam'

A.prototype.x = 23;   
a.x; //23

A.__proto__.max = 20150101;
a.max   //undefined
复制代码

 

但是如果我现在替换A的原型属性为一个新的对象,实例对象的原型a.__proto__却仍然引用着原来它被创建时A的原型属性。

复制代码
var A = function(name) {
    this.name = name;
}  
var a = new A('alpha');
a.name;   //'alpha'   
 
A.prototype = {x:23};    
a.x;     //null
复制代码

即如果在实例被创建之后,改变了函数的原型属性所指向的对象,也就是改变了创建实例时实例原型所指向的对象。

但是这并不会影响已经创建的实例的原型。

 

对原生态的构造函数Function,String等,扩展这个属性,我们可以使用原型达到扩展指定构造函数的所有实例的目的。

如下,对所有的string实例都实现了times这个方法,对字符串本身进行指定数目的复制。

复制代码
String.prototype.times = function(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
 } 
 
"hello!".times(3); 
//"hello!hello!hello!"; 
 
"please...".times(6);
//"please...please...please...please...please...please..."
复制代码

 

因为每个对象和原型都有一个原型(原型也是一个对象),对象的原型指向对象的父,而父的原型又指向父的父,我们把这种通过原型层层连接起来的关系撑为原型链。这条链的末端一般总是默认的对象原型。这就是利用原型实现继承的方法。

复制代码
a.__proto__ = b; 
 
b.__proto__ = c; 
 
c.__proto__ = {}; //default object 
 
{}.__proto__.__proto__; //null
复制代码

彻底理解JavaScript原型

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

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

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

JavaScript中原型对象的彻底理解

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

关于js原型的经典面试题解析

这是我一次的面试的时候 鄙视的部分 回来之后才发现是一道很经典的面试原型题   话不多说  上图 ---------------------------------------------...
  • u013321789
  • u013321789
  • 2017年07月20日 02:25
  • 402

javascript中对象与原型的理解;null和undefined的理解

对象与原型 【ES5 4.3.3】 对象类型的成员。对象是属性的集合,并有一个原型对象。原型可以是空值。 【ES5 4.3.5】原型 (prototype)  为其他对象提供共享属性的对象。 当构造器...
  • mht1829
  • mht1829
  • 2017年08月30日 07:55
  • 310

JavaScript prototype原型和原型链详解

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

深入理解JavaScript的原型对象

JavaScript的继承机制是基于原型,而不是类。因此要理解JavaScript的继承机制,包括基于ES5的框架模型(例如Backbone)的继承机制,需要更深入了解原型对象。...
  • hongse_zxl
  • hongse_zxl
  • 2015年03月25日 16:13
  • 3711

javascript的原型与原型链

深入理解javascript原型链   在javascript中原型和原型链是一个很神奇的东西,对于大多数人也是最难理解的一部分,掌握原型和原型链的本质是javascript进阶的重要一环。今天...
  • qq_29999249
  • qq_29999249
  • 2017年03月15日 11:45
  • 595

简单理解javascript原型及原型链

简单理解javascript原型及原型链原型和原型链式javascrpit中很重要的概念,但是无论书上还是网上 ,不少关于javascript原型和原型链的解释都不易理解,所以本人在此介绍一下自己的简...
  • github_35143739
  • github_35143739
  • 2016年05月28日 10:12
  • 363

JavaScript Array 原型方法 大盘点

数组是一个超常用的数据结构,JavaScript的数组方法都有什么怎样的特性呢?是时候一探究竟了。JavaScript中数组是一个对象,默认的赋值是传了一个引用。针对结果是引用还是拷贝,对原数组的变更...
  • zccz14
  • zccz14
  • 2016年06月04日 13:12
  • 2666
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:JavaScript 原型
举报原因:
原因补充:

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