JavaScript 是面向对象的脚本语言,长期以来用作 Web 浏览器应用程序的客户端脚本接口。JavaScript 让 Web 开发人员能以编程方式处理 Web 页面上的对象,并提供了一个能够动态操作这些对象的平台。在最初引入 JavaScript 时,通常只用于提供 Web 页面上的一些不重要的特性,如时钟功能和浏览器状态栏中的滚动文本等。
另一个常见的特性是 “rolloverlink”,就是当用户将鼠标移到图片或文本链接上时,图片或文本链接的颜色会改变。然而,近年来,随着AsynchronousJavaScript and XML (Ajax) 概念将基于 Web 的编程的交互性提升到一个新高度,JavaScript 也变得越来越重要。在出现 Ajax 之前,所有服务器端处理或数据库访问都需要 “刷新” 整个页面或通过浏览器呈现一个新页面。这不仅减慢了速度并使用户感到沮丧,而且还浪费带宽和资源。
JavaScript原型
原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有“[[prototype]]”属性,函数对象有“prototype”属性,原型对象有“constructor”属性。
为了弄清原型,以及原型相关的这些属性关系,就有了这篇文章。
相信通过这篇文章一定能够清楚的认识到原型,现在就开始原型之旅吧。
认识原型
开始原型的介绍之前,首先来认识一下什么是原型?
在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象中都包含了一个“ [[Prototype]]”内部属性,这个属性所对应的就是该对象的原型。
“[[Prototype]]”作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome中提供了“__proto__”这个非标准(不是所有浏览器都支持)的访问器(ECMA引入了标准对象原型访问器“Object.getPrototype(object)”)。
实例分析
下面通过一个例子来看看原型相关概念:
this.name = name;
this.age = age;
this.getInfo = function(){
console.log(this.name + “ is ” + this.age + “ years old”);
};
}
var will = new Person(“Will”, 28);
在上面的代码中,通过了Person这个构造函数创建了一个will对象。下面就通过will这个对象一步步展开了解原型。
Step 1: 查看对象will的原型
通过下面代码,可以查看对象will的原型:
console.log(will.__proto__);
console.log(will.constructor);
结果分析:
“Person {}”对象就是对象will的原型,通过Chrome展开可以看到,“Person {}”作为一个原型对象,也有“__proto__”属性(对应原型的原型)。
在这段代码中,还用到了“constructor”属性。在JavaScript的原型对象中,还包含一个“constructor”属性,这个属性对应创建所有指向该原型的实例的构造函数。
通过“constructor”这个属性,我们可以来判断一个对象是不是数组类型
function isArray(myArray) {
return myArray.constructor.toString().indexOf(“Array”) 》 -1;
}
在这里,will对象本身并没有“constructor”这个属性,但是通过原型链查找,找到了will原型(will.__proto__)的“constructor”属性,并得到了Person函数。
Step 2: 查看对象will的原型(will.__proto__)的原型
既然will的原型“Person {}”也是一个对象,那么我们就同样可以来查看“will的原型(will.__proto__)的原型”。
运行下面的代码:
console.log(will.__proto__ === Person.prototype);
console.log(Person.prototype.__proto__);
console.log(Person.prototype.constructor);
console.log(Person.prototype.constructor === Person);
结果分析:
首先看 “will.__proto__ === Person.prototype”,在JavaScript中,每个函数都有一个prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性),也就是说,所有实例的原型引用的是函数的prototype属性。了解了构造函数的prototype属性之后,一定就明白为什么第一句结果为true了。
prototype属性是函数对象特有的,如果不是函数对象,将不会有这样一个属性。
当通过“Person.prototype.__proto__”语句获取will对象原型的原型时候,将得到“Object {}”对象,后面将会看到所有对象的原型都将追溯到“Object {}”对象。
对于原型对象“Person.prototype”的“constructor”,根据前面的介绍,将对应Person函数本身。
通过上面可以看到,“Person.prototype”对象和Person函数对象通过“constructor”和“prototype”属性实现了相互引用(后面会有图展示这个相互引用的关系)。
Step 3: 查看对象Object的原型
通过前一部分可以看到,will的原型的原型是“Object {}”对象。实际上在JavaScript中,所有对象的原型都将追溯到“Object {}”对象。
下面通过一段代码看看“Object {}”对象:
console.log(Person.prototype.__proto__ === Object.prototype);
console.log(typeof Object);
console.log(Object);
console.log(Object.prototype);
console.log(Object.prototype.__proto__);
console.log(Object.prototype.constructor);
通过下面的代码可以看到:
Object对象本身是一个函数对象。
既然是Object函数,就肯定会有prototype属性,所以可以看到“Object.prototype”的值就是“Object {}”这个原型对象。
反过来,当访问“Object.prototype”对象的“constructor”这个属性的时候,就得到了Obejct函数。
另外,当通过“Object.prototype.__proto__”获取Object原型的原型的时候,将会得到“null”,也就是说“Object {}”原型对象就是原型链的终点了。
Step 4: 查看对象Function的原型
在上面的例子中,Person是一个构造函数,在JavaScript中函数也是对象,所以,我们也可以通过“__proto__”属性来查找Person函数对象的原型。
console.log(Person.__proto__ === Function.prototype);
console.log(Person.constructor === Function)
console.log(typeof Function);
console.log(Function);
console.log(Function.prototype);
console.log(Function.prototype.__proto__);
console.log(Function.prototype.constructor);
结果分析 :
在JavaScript中有个Function对象(类似Object),这个对象本身是个函数;所有的函数(包括Function,Object)的原型(__proto__)都是“Function.prototype”。
Function对象作为一个函数,就会有prototype属性,该属性将对应“function () {}”对象。
Function对象作为一个对象,就有“__proto__”属性,该属性对应“Function.prototype”,也就是说,“Function.__proto__ === Function.prototype”
对于Function的原型对象“Function.prototype”,该原型对象的“__proto__”属性将对应“Object {}”
对比prototype和__proto__
对于“prototype”和“__proto__”这两个属性有的时候可能会弄混,“Person.prototype”和“Person.__proto__”是完全不同的。
在这里对“prototype”和“__proto__”进行简单的介绍:
对于所有的对象,都有__proto__属性,这个属性对应该对象的原型
对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)
评论
查看更多