深入理解JavaScript编程中的原型概念


Posted in Javascript onJune 25, 2015

 JavaScript 的原型对象总是让人纠结。即使是经验丰富的JavaScript专家甚至其作者,经常对这一概念给出很有限的解释。我相信问题来自于我们对原型最早的认识。原型总是与new, constructor 以及令人困惑的prototype属性紧密联系。事实上,原型是一个相当简单的概念。为了更好地理解它,我们需要忘记我们所‘学到'的构造原型,然后,追本溯源。

什么是原型?

原型是一个从其他对象继承属性的对象。

是不是任何对象都可以是原型?

是的

那些对象有原型?

每个对象都有一个默认的原型。原型本身就是对象,每一个原型本身也存在一个原型。(只有一个例外,默认的对象原型在每条原型链的顶端,其他的原型在原型链的后面)

退一步说,什么又是对象呢?

在JavaScript中一个对象是以键值对保存的任意的无序集合,如果它不是原始类(undefined,null,boolean.nuber或string),它就是一个对象。

你可以认为每个对象都有一个prototype. 但当我写({}).prototype的时候,我却得到了undefined,你疯不疯?

忘记你所掌握的关于prototype属性的理解 - 这很可能是迷惑的根源. 一个对象真正的prototype是内部[[Prototype]]属性. ECMA 5介绍了标准的访问方法,Object.getPrototypeOf(object)。这个最新的实现已被Firefox, Safari, Chrome and IE9所支持. 另外,除了IE,所有的浏览器都支持非标准的访问方法__proto__.不然的话,我们只能说对象的构造方法就是它的prototype属性.
 

var a = {};
 
//Opera 或 IE<=8下失败
Object.getPrototypeOf(a); //[object Object]
 
//IE下失败
a.__proto__; //[object Object]
 
//所有浏览器
//(but only if constructor.prototype has not been replaced and fails with Object.create)
a.constructor.prototype; //[object Object]

很好, false 是原始类型, 为什么false.__proto__ 会返回一个值呢?

当访问原始类型的原型(prototype),它会强制转化为一个对象。
 

//(works in IE<=8 too, due to double-negative)
false.__proto__ === Boolean(false).__proto__; //true

我想使用原型实现继承,我现在该怎么做?

给一个实例添加原型属性,几乎是没有意义的.除非一种情况,那就是,很有效率的添加属性直接到实例本身.假设我们已经有了一个对象,要共享已经存在的对象的功能.例如Array,我们可以这样做
 

//fails in IE<=8
var a = {};
a.__proto_ = Array.prototype;
a.length; //0

但是我们可以看到原型的真正强大在于多个实例共享同一原型。原型对象的属性只被定义一次就可以被它引用的所有实例所继承。使用原型对性能和程序可维护性的提高效果是很显而易见的。那么这就是构造函数产生的原因吗?是的,构造函数提供了一个便捷的跨浏览器机制来实现对实例创建时的公用原型分配。。

在给出一个例子之前,我需要知道constructor.prototype property是干什么的?

好吧,首先,JavaScript不区分构造函数和其它方法,所以每个方法都有prototype属性。反而任何不是方法的,都没有这样的属性。
 

//永远不是构造函数的方法,无论如何都是有prototype属性的
Math.max.prototype; //[object Object]
 
//构造函数也有prototype属性
var A = function(name) {
  this.name = name;
}
A.prototype; //[object Object]
 
//Math不是一个方法,所以没有prototype属性
Math.prototype; //null

现在可以定义: 一个方法的prototype属性是当这个方法被用作构造函数来创建实例时赋给该实例的prototype的对象。

非常重要的一点是,要理解方法的prototype属性和实际的prototype没有任何关系。
 

//(在IE中会失败)
var A = function(name) {
  this.name = name;
}
 
A.prototype == A.__proto__; //false
A.__proto__ == Function.prototype; //true - A的prototype是它的构造函数的prototype属性

能给个例子不?

以下的代码,可能你已经看到或用过上百次了,但这里又把它搬上来了,但可能会有些新意。
 

//构造器. <em>this</em> 作为新对象返回并且它内部的[[prototype]]属性将被设置为构造器默认的prototype属性
var Circle = function(radius) {
  this.radius = radius;
  //next line is implicit, added for illustration only
  //this.__proto__ = Circle.prototype;
}
 
//扩充 Circle默认的prototype对象的属性因此扩充了每个由它新建实例的prototype对象的属性
Circle.prototype.area = function() {
  return Math.PI*this.radius*this.radius;
}
 
//创建Circle的两个示例,每个都会使用相同的prototype属性
var a = new Circle(3), b = new Circle(4);
a.area().toFixed(2); //28.27
b.area().toFixed(2); //50.27

这很棒。如果我改变了constructor的prototype属性,即使是已存在的实例对象也可以立刻访问新的prototype版本吗?

嗯......不完全是。如果我修改的是现存prototype的属性后,那么确实是这种情况,因为对象创建时a.__proto__引用了A.prototype所定义的对象。
 

var A = function(name) {
  this.name = name;
}
 
var a = new A('alpha');
a.name; //'alpha'
 
A.prototype.x = 23;
 
a.x; //23

但是如果我将prototype属性用一个新对象代替,a.__proto__ 仍然指向原始对象。
 

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

一个缺省的prototype是什么样的?

一个拥有constructor属性的对象。
 

var A = function() {};
A.prototype.constructor == A; //true
 
var a = new A();
a.constructor == A; //true (a 的constructor属性继承自它的原型)

instanceof与prototype有啥关系?
如果A的prototype属性出现在a的原型链中,则表达式a instanceof A会返回true。这意味着我们可以欺骗instanceof,让它失效。
 

var A = function() {}
 
var a = new A();
a.__proto__ == A.prototype; //true - so instanceof A will return true
a instanceof A; //true;
 
//mess around with a's prototype
a.__proto__ = Function.prototype;
 
//a's prototype no longer in same prototype chain as A's prototype property
a instanceof A; //false

那么我还能利用原型干些其它的什么事儿?

记得我曾经说过每一个构造器都拥有一个prototype属性,利用该属性可以将原型赋值给所有由构造器产生的实例?其实这同样适用于本地构造器,例如Function和String。通过扩展(而不是替换)这个属性,我们可以更新每个指定类型对象的prototype。 

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..."

告诉我更多关于继承与原型是怎么工作的。原型链又是什么东东?

因为每个对象和每个原型(本身)都有一个原型,我们可以想象, 一个接一个的对象连接在一起形成一个原型链。 原型链的终端总是默认对象(object)的原型。
 

a.__proto__ = b;
b.__proto__ = c;
c.__proto__ = {}; //默认对象
{}.__proto__.__proto__; //null

原型继承机制是内在且隐式实现的。当对象a要访问属性foo时,Javascript会遍历a的原型链(首先从a自身开始),检查原型链的每一个环节中存在的foo属性。如果找到了foo属性就会将其返回,否则返回undefined值。

直接赋值会咋样?

当直接为对象属性赋值时,原型继承机制就玩不转了。a.foo='bar'会直接赋值给a的foo属性。要想为原型对象的属性赋值,你需要直接定位原型对象的该属性。
关于javascript原型就讲全了。我觉得对于原型概念的理解,我把握的还是比较准确的,但是我的观点无论如何也不是最后的结果。请随便告之我的错误之处或提出和我不一致的观点。

Javascript 相关文章推荐
js跟随滚动条滚动浮动代码
Dec 31 Javascript
基于Jquery制作的幻灯片图集效果打包下载
Feb 12 Javascript
Javascript Throttle &amp; Debounce应用介绍
Mar 19 Javascript
Jquery实现点击切换图片并隐藏显示内容(2种方法实现)
Apr 11 Javascript
jQuery中fadeIn、fadeOut、fadeTo的使用方法(图片显示与隐藏)
May 08 Javascript
js特殊字符过滤的示例代码
Mar 05 Javascript
javascript多物体运动实现方法分析
Jan 08 Javascript
深入浅析JSON.parse()、JSON.stringify()和eval()的作用详解
Apr 03 Javascript
jQuery使用deferreds串行多个ajax请求
Aug 22 Javascript
JS数字千分位格式化实现方法总结
Dec 16 Javascript
JS排序之选择排序详解
Apr 08 Javascript
微信小程序视频弹幕发送功能的实现
Dec 28 Javascript
Backbone.js 0.9.2 源码注释中文翻译版
Jun 25 #Javascript
在JavaScript应用中实现延迟加载的方法
Jun 25 #Javascript
Underscore.js 1.3.3 中文注释翻译说明
Jun 25 #Javascript
深入分析JSON编码格式提交表单数据
Jun 25 #Javascript
jquery移动点击的项目到列表最顶端的方法
Jun 24 #Javascript
jquery使整个div区域可以点击的方法
Jun 24 #Javascript
jQuery寻找n以内完全数的方法
Jun 24 #Javascript
You might like
火影忍者:三大瞳力之一的白眼,为什么没有写轮眼那么出色?
2020/03/02 日漫
Content-type 的说明
2006/10/09 PHP
php程序的国际化实现方法(利用gettext)
2011/08/14 PHP
php中各种定义变量的方法小结
2017/10/18 PHP
Thinkphp 框架配置操作之动态配置、扩展配置及批量配置实例分析
2020/05/15 PHP
setInterval 和 setTimeout会产生内存溢出
2008/02/15 Javascript
使用js获取QueryString的方法小结
2010/02/28 Javascript
js从10种颜色中随机取色实现每次取出不同的颜色
2013/10/23 Javascript
鼠标滑过出现预览的大图提示效果
2014/02/26 Javascript
浅谈Javascript中substr和substring的区别
2015/09/30 Javascript
理解javascript中的with关键字
2016/02/15 Javascript
Bootstrap基本插件学习笔记之模态对话框(16)
2016/12/08 Javascript
通过jsonp获取json数据实现AJAX跨域请求
2017/01/22 Javascript
微信小程序 使用腾讯地图SDK详解及实现步骤
2017/02/28 Javascript
详解vue事件对象、冒泡、阻止默认行为
2017/03/20 Javascript
React Native基础入门之初步使用Flexbox布局
2018/07/02 Javascript
JS解析后台返回的JSON格式数据实例
2018/08/06 Javascript
Vue实现一个无限加载列表功能
2018/11/13 Javascript
JavaScript使用类似break机制中断forEach循环的方法
2018/11/13 Javascript
js实现指定时间倒计时效果
2019/08/26 Javascript
解决vue项目刷新后,导航菜单高亮显示的位置不对问题
2019/11/01 Javascript
JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】
2020/05/12 Javascript
JS中准确判断变量类型的方法
2020/06/01 Javascript
python爬取51job中hr的邮箱
2016/05/14 Python
python 队列详解及实例代码
2016/10/18 Python
Python简单计算文件MD5值的方法示例
2018/04/11 Python
纯CSS3实现表单验证效果(非常不错)
2017/01/18 HTML / CSS
如何用border-image实现文字气泡边框的示例代码
2020/01/21 HTML / CSS
保安员岗位职责
2013/11/17 职场文书
高中自我评价分享
2013/12/05 职场文书
党的群众路线教育实践活动动员会主持词
2014/03/20 职场文书
委托书的格式
2014/08/01 职场文书
医德医魂心得体会
2014/09/11 职场文书
党员组织生活会发言材料
2014/10/17 职场文书
2015清明节祭奠英烈寄语大全
2015/03/04 职场文书
教你利用Selenium+python自动化来解决pip使用异常
2021/05/20 Python