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

原型(prototype)是每个JavaScript开发人员必须理解的基本概念,本文的目标是通俗而又详细地解释JavaScript的原型。如果你读完这篇博文以后还是不理解JavaScript的原型,请将你的问题写在下面的评论里,我本人会回答所有的问题。

为了理解JavaScript中的原型,你必须理解JavaScript的对象。如果你对对象还不熟悉,你需要阅读我的文章JavaScript Objects in Detail译文:详解JavaScript对象)。而且你要知道属性就是函数中定义的变量。

在JavaScript中有两个互相之间有关联的原型的概念:

1.首先,每一个JavaScript函数有一个原型属性,当你需要实现继承的时候你就给这个原型属性附加属性和方法。注意这个原型属性是不可以枚举的:它在for/in循环中是不可获取的。但是FireFox和大多数版本的Safari和Chrome浏览器有一个__proto__“伪”属性(一种选择方式)允许你访问对象原型的属性。你可能从来没有用过这个_proto__伪属性,但你得知道它的存在并且它是在某些浏览器中访问对象的原型属性的一种简单的方法。

对象原型主要用于继承:你为对象的原型属性增加方法和属性,使这些原型和属性存在于该函数的实例。

以下是一个简单的使用原型属性继承的例子(后面还有更多关于继承的内容):

function PrintStuff (myDocuments) {
    this.documents = myDocuments;
}

//我们为PrintStuff的原型属性增加方法print (),这样的话其他实例(对象)可以继承这个方法
PrintStuff.prototype.print = function () {
    console.log(this.documents);
}

//用构造函数PrintStuff ()创建一个新的对象,由此让这个新对象继承PrintStuff 的属性和方法。
var newObj = new PrintStuff ("I am a new Object and I can print.");

// newObj继承了函数PrintStuff所有的属性和方法,包括方法print。现在newObj可以直接调用print,即使我们从来没有为它定义过方法print()。
newObj.print (); //I am a new Object and I can print.

2. 第二个关于JavaScript原型的概念是原型特性。把原型特性想成该对象的一个性质;这个性质表明了该对象的“父母”。简而言之:对象的原型特性可以看成是对象的“父母”--该对象获得它属性的地方。对象特性常常被称为原型对象,并且它是在你创建对象时就自动建立的。对此的解释是:每一个对象从某个其他的对象那里继承属性,而这里的“某个其他的对象”就是该对象的原型特性或者“父母”。(你可以把原型特性想象成血缘关系或者父母)。在上面例子的代码中,newObj的原型是PrintStuff.prototype。

注意:所有的对象都有特性,就和对象属性有自己的特性一样。对象特性是原型、类和扩展特性。这些是我们在第二个例子中要讨论的原型特性。

还有一点需要注意, “伪”属性__proto__包含一个对象的原型对象(被该对象继承方法和属性的父对象)。

//重要提示
//构造函数
//在我们继续往下阅读之前,让我们来简单的考查一下构造函数。构造函数是一个用来初始化新对象的函数,并且使用新的关键字来调用构造函数。
//例如:

function Account () {
}
//这是利用构造函数Account来创建对象userAccount
var userAccount = new Account (); 

而且,所有继承自另一个对象的对象,也继承了那个对象的构造函数属性。而这个构造函数属性就是一个保存或者指向该对象的构造函数的属性(和任何变量一样)。

//本例的构造函数是Object ()
var myObj = new Object ();

//而如果你之后想要知道myObj的构造函数:
console.log(myObj.constructor); // Object()

// 另一个例子: Account ()是构造函数
var userAccount = new Account (); 

//查看对象userAccount的构造函数
console.log(userAccount.constructor); // Account()

用new Object()或对象式创建的对象的原型特性

所有用对象式或者构造函数Object创建的对象都继承自Object.prototype。因此Object.prototype是所有用Object()或者{}所创建的对象的原型特性(或原型对象)。Object.prototype本身没有从其他任何对象那里继承任何的方法或者属性。

// 对象userAccount 继承自Object 并且因此它的原型特性就是Object.prototype.
var userAccount = new Object ();

// 这个声明用了对象式来创造对象userAccount;该对象userAccount继承自Object;因此,就和上面的对象userAccount一样,它的原型特性是Object.prototype。
var userAccount = {name: “Mike”}

用构造函数所创建的对象的原型特性

用新关键字以及任何一种非Object()的构造函数所创建的对象,从该构造函数中获得它们的构造函数。

例如:

function Account () {

}
var userAccount = new Account () // 用构造函数Account ()初始化userAccount并且因此它的原型特性(原型对象)就是 Account.prototype。

类似的,任何数组,比如var myArray = new Array (),从Array.prototype获得原型并且继承Array.prototype的属性。

所以,当对象被创建时有两种通用的方式来建立对象的原型特性:

1.如果对象是使用对象式(var newObj = {})创建的,那么它从Object.prototype继承属性,并且我们说它的原型对象(或者原型特性)是Object.prototype。

2.如果对象是使用构造函数,比如 new Object ()或者new Fruit ()或者new Array ()或者 new Anything ()创建的,那么它继承自构造函数 (Object (), Fruit (), Array (), or Anything ())。例如,用一个函数,比如Fruit (),每次我们创建一个新的水果的实例(var aFruit = new Fruit ()),那么该新实例的原型就来自于构造函数Fruit,也就是 Fruit.prototype。任何用new Array ()所创建的对象都会将Array.prototype作为它的原型。任何用构造函数Object(Obj (), 比如 var anObj = new Object() )创建的对象继承自Object.prototype。

还有一点你需要知道的,在ECMAScript 5中,你可以用一个允许你指定新对象的原型的方法Object.create()来创建对象。我们会在后续的文章中学习ECMAScript 5。

原型为什么重要以及何时使用原型?

在JavaScript中原型有两种重要的用途,就像前文中提到的那样:

1.原型属性:基于原型的继承

在JavaScript中原型之所以重要是因为JavaScript没有(大多数面向对象的语言所有的)经典的基于类的继承,因此JavaScript所有的继承是通过原型属性来实现的。JavaScript有一套基于原型继承的机制。继承是一种能让对象(或者是其它语言中的类)继承其它对象(或类)的属性和方法的编程规范。在JavaScript中,通过原型来实现继承。例如,你可以创建一个Fruit函数(也就是对象,因为所有JavaScript中的函数都是对象)并且给这个Fruit的原型属性添加属性和方法,那么所有Fruit函数的实例会继承Fruit全部的属性和方法。

JavaScript中的继承示例:

function Plant () {
    this.country = "Mexico";
    this.isOrganic = true;
}

//把方法showNameAndColor添加到Plant原型属性
Plant.prototype.showNameAndColor =function () {
    console.log("I am a " + this.name + " and my color is " + this.color);
}

// 把方法amIOrganic添加到Plant原型属性
Plant.prototype.amIOrganic = function () {
    if (this.isOrganic)
        console.log("I am organic, Baby!");
}

function Fruit (fruitName, fruitColor) {
    this.name = fruitName;
    this.color = fruitColor;
}

//将Fruit的原型设为Plant的构造函数,因此继承了Plant.prototype全部的方法和属性
Fruit.prototype = new Plant ();

// 用构造函数Fruit创建一个新的aBanana
var aBanana = new Fruit ("Banana", "Yellow");

// 这里aBanana用了来自aBanana对象原型Fruit.prototype的name属性:
console.log(aBanana.name); // Banana

//用来自Fruit对象原型Plant.prototype的方法showNameAndColor。该aBanana对象继承了来自函数Plant和Fruit的全部属性和方法
console.log(aBanana.showNameAndColor()); // I am a Banana and my color is yellow.

注意到,尽管方法showNameAndColor是在对象Plant.prototype的原型链上定义的,但是此方法还是被对象aBanana所继承。

实际上,任何使用构造函数Fruit ()的对象,都将继承Fruit.prototype全部的属性和方法以及来自Fruit的原型Plant.prototype的全部的属性和方法。这就是JavaScript中实现继承的主要方式以及原型链在这一过程中所扮演的整合角色。

2.原型特性:获取对象的属性

原型对于获取对象的方法和属性也是很重要的。原型特性(或原型对象)是那些可继承的属性的“父母”对象,这些可继承的属性原本就是为这些“父母”对象定义的。这就有点类似于你可以从你的父亲--他是你的“原型父母”,那里继承姓。如果我们想知道你的姓是从哪里来的,我们会先看看是否是你自己给自己取了这个姓;如果不是,我们会继续查看是否你是从你的父亲那里继承了这个姓。如果这个姓不是你父亲的,那么我们会继续查看你父亲的父亲的姓(你父亲的原型父亲)。

与之类似的,如果你想要获取一个对象的原型,你将直接从该对象的属性开始寻找。如果JS运行时不能再那里找到该属性,那么它会去该对象的原型--该对象得到属性的地方,去查看这个属性。

如果在对象的原型中没有发现该属性,那么对于该属性的搜寻会转移到对象的原型的原型(对象的父亲的父亲--爷爷)那里去。就这样一直持续到没有原型为止(没有更多的曾祖父;没有更多有遗传来的血缘关系)。这其实就是原型链:从对象的原型到对象原型的原型不断向上的一条链。并且JavaScript就用这条原型链来搜寻对象的属性和方法。

如果某个属性在它的整条原型链上的任何一个对象的原型中均不存在,那么这个属性就是不存在并且会返回undefined。

这种原型链机制本质上和我们上面讨论的基于原型的继承是一样的概念,只是在这里我们更注重于JavaScript如何通过对象原型获取对象的属性和方法。

这个例子演示了对象的原型对象的原型链:

var myFriends = {name: "Pete"};

//为了找到下面的属性name,搜寻会直接从对象myFriends开始,并且会立刻找到属性name,因为我们为对象myFriends定义了属性name。这个可以被想像成有一条链接的原型链。
console.log(myFriends.name);

//在这个例子中,将会从对象myFriends开始搜寻方法toString (),但是因为我们从来没有为对象myFriends创建过方法toString,编译器会接着去myFriends的原型(被myFriends继承属性的那个对象)搜寻。

//并且因为所有用对象式创建的对象都继承自Object.prototype,方法toString将在 Object.prototype中被发现--关于所有继承自Object.prototype的属性,请看下面的重要提示
myFriends.toString ();

重要提示

所有对象都会继承的Object.prototype的属性

在JavaScript中所有对象的属性和方法继承自Object.prototype。这些继承来的属性和方法有构造函数,hasOwnProperty (), isPrototypeOf (), propertyIsEnumerable (), toLocaleString (), toString (), and valueOf ()。ECMAScript 5中还新增了四种访问Object.prototype的方法。

下面是另一个原型链的例子:

function People () {
    this.superstar = "Michael Jackson";
}
// 为People原型定义属性"athlete"以便"athlete" 可以被所有使用构造函数People () 的对象所访问。
People.prototype.athlete = "Tiger Woods";

var famousPerson = new People ();
famousPerson.superstar = "Steve Jobs";

//对于superstar的搜寻将首先查看对象famousPerson是否有属性superstar,而因为就是在那里定义的这个属性,这就是需要用到的属性。因为我们已经为对象famousPerson重复定义链famousPerson的属性superstar,所以对于superstar的搜寻就不会在原型链上继续上升。
console.log (famousPerson.superstar); // Steve Jobs

// 注意在ECMAScript 5中你可以将属性设置为只读,这样的话你就不能像我们刚才那样重复定义该属性。

//这里展示了来自famousPerson原型(People.prototype)的属性,因为属性athlete没有为对象famousPerson本身所定义。
console.log (famousPerson.athlete); // Tiger Woods

//在这个例子中,在原型链上向上搜寻并且在Object.prototype中找到了方法toString,这个方法来自对象Fruit的继承--像我们前面提到的那样,所有的对象最终继承自Object.prototype
console.log (famousPerson.toString()); // [object Object]

所有已经建立的构造函数 (Array (), Number (), String (), etc.)都是由构造函数Object所创建的,因此它们的原型是Object.prototype。

时间: 2024-10-10 09:33:29

用通俗易懂的语言介绍JavaScript原型的相关文章

javascript原型Prototype

在javaScript创建对象一文中提到过:用构造函数创建对象存在一个问题即同一构造函数的不同实例的相同方法是不一样的,所以我们用原型把构造函数中公共的属性和方法提取出来进行封装,达到让所有实例共享的目的. 接下来进一步介绍javaScript原型. 一.javaScript原型机制 1.函数与原型的关系 js中创建一个函数,就会自动创建一个prototype属性,这个属性指向函数的原型对象,并且原型对象会自动获得一个constructor(构造函数)属性,指向该函数. 举例:以前面的原型模式创

javascript原型

在javaScript创建对象一文中提到过:用构造函数创建对象存在一个问题即同一构造函数的不同实例的相同方法是不一样的,所以我们用原型把构造函数中公共的属性和方法提取出来进行封装,达到让所有实例共享的目的. 接下来进一步介绍javaScript原型. 一.javaScript原型机制 1.函数与原型的关系 js中创建一个函数,就会自动创建一个prototype属性,这个属性指向函数的原型对象,并且原型对象会自动获得一个constructor(构造函数)属性,指向该函数. 举例:以前面的原型模式创

介绍下Javascript原型和原型链的特点?

JavaScript原型: 每个对象都会在其内部初始化一个属性,就是prototype(原型). 原型链: 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,于是就这样一直找下去,也就是我们平时所说的原型链的概念. 特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本.当我们修改原型时,与之相关的对象也会继承这一改变.

JavaScript之基础-16 JavaScript 原型与继承

一.JavaScript 原型 原型的概念 - 在JavaScript中,函数本身也是一个包含了方法和属性的对象 - 每个函数中都有一个prototype属性,该属性引用的就是原型对象 - 原型对象是保存共享属性值和共享方法的对象 为对象扩展属性 - 扩展单个对象的成员 - 扩展共享的属性值 - 内存图描述 删除属性 - 可以使用delete关键字删除对象的属性 自由属性与原型属性 - 自有属性:通过对象的引用添加的属性;其它对象可能无此属性;即使有,也是彼此独立的属性 emp1.job = '

JavaScript 原型与继承机制详解

引言 初识 JavaScript 对象的时候,我以为 JS 是没有继承这种说法的,虽说 JS 是一门面向对象语言,可是面向对象的一些特性在 JS 中并不存在(比如多态,不过严格来说也没有继承).这就困惑了我很长的时间,当我学习到 JS 原型的时候,我才发现了 JS 的新世界.本篇文章讲解了 JavaScript new 操作符与对象的关系.原型和对象关联(也就是俗称的继承)的原理,适合有一定基础的同学阅读. 一.JavaScript 的类与对象 许多书籍上都会说到如何在 JS 当中定义“类”,通

理解JavaScript原型

Javascript原型总会给人产生一些困惑,无论是经验丰富的专家,还是作者自己也时常表现出对这个概念某些有限的理解,我认为这样的困惑在我们一开始接触原型时就已经产生了,它们常常和new.constructor相关,特别是函数(function)的原型(prototype)属性(property).事实上,原型是一种非常简单的概念.为了更好的理解它,我们应该首先记住这个原则,那就是忘记我们已经学到的关于构造原型(construtor prototypes)的认识. 什么是原型? 原型是一个对象,

深入理解javascript原型和闭包(转)

深入理解javascript原型和闭包(完结) 说明: 该教程绕开了javascript的一些基本的语法知识,直接讲解javascript中最难理解的两个部分,也是和其他主流面向对象语言区别最大的两个部分——原型和闭包,当然,肯定少不了原型链和作用域链.帮你揭开javascript最神秘的面纱. 为什么要偏偏要讲这两个知识点? 这是我在这么多年学习javascript的经历中,认为最难理解.最常犯错的地方,学习这两个知识点,会让你对javascript有更深层次的理解,至少理解了原型和作用域,就

Javascript原型模式总结梳理

在大多数面向对象语言中,对象总是由类中实例化而来,类和对象的关系就像模具跟模件一样.Javascript中没有类的概念,就算ES6中引入的class也不过是一种语法糖,本质上还是利用原型实现.在原型编程语言中,类并不是必需的,对象不一定需要由类实例化而来,而是通过克隆另外一个对象来得到. 原型模式是用来创建对象的一种模式.在以类为中心的语言中,要创建一个对象首先要指定这个对象的类型,然后实例化一个对象.使用原型模式创建对象时不必关心对象的具体类型,而是找到一个对象,然后通过克隆来创建一个一模一样

【转载】用平常语言介绍神经网络

http://wenku.baidu.com/link?url=23jM13V3Qe9Zx84FuEMebOMbTSk4i0Oaa5YPAVL1dfbSFC4G20GYDkUbbRvcJILgTGliXHPQgZqx5ToZscEOt0Iwp4eT71mRZhZ64USYk63 用平常语言介绍神经网络 (Neural Networks in Plain English) 因为我们没有能够很好了解大脑,我们经常试图用最新的技术作为一种模型来解释它.在我童年的时候,我们都坚信大脑是一部电话 交换机