好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

JavaScript 原型

JavaScript 原型

JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有 一个 原型对象,对象以其原型为模板、从原型继承 方法 和 属性 。原型对象也可能拥有原型,并从中继承 方法 和 属性 ,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何 一个 对象会拥有定义在其他对象中的 属性 和 方法 。(MDN)

每个对象都有 一个 标签 ,这个 标签 指向他的原型对象,对象基于一种机制,可以访问到原型对象上的 属性 。

在标准中, 一个 对象的原型是使用 [[prototype]] 表示的, chrome 对其的实现是使用 __proto__ 属性 表示。

1. 什么是原型

1.1 属性 的访问机制

在 JavaScript 中,除了几种基础类型,剩下的几乎都是对象。

当我们使用对象自面量创建 一个 对象的时候,可以访问到对象的 toString 方法 。

  var  obj  =   {  empty :   true   }  ; 

console .  log  ( obj .  toString  (  )  )  ;   //  输出 :[object Object] 
 

在书写这个自面量的时候,并没有提供 toString 这个 方法 ,却可以被成功 调用 。

这就涉及到了原型。

当在访问 一个 对象的 属性 时,如果当前对象没有这个 属性 ,就会继续往这个对象的原型对象上去找这个 属性 。

如果原型对象上没有这个 属性 ,则继续从这个 对象 的 原型对象 的 原型对象 找这个 属性 。

这就是 属性 查找的机制,直到查到原型的末端,也就是 null ,就会停止查找,这个时候已经确定没有这个 属性 了,就会返回 undefined 。

例子中的变量 obj 的原型可以通过 __proto__ 访问。

  var  obj  =   {  empty :   true   }  ; 

console .  log  ( obj . __proto__ )  ; 
 

在 输出 的原型对象中可以找到 toString 方法 。

可以通过相等运算符来判断 调用 的 toString 方法 是不是原型上的 方法 。

  var  obj  =   {  empty :   true   }  ; 

console .  log  ( 
  obj . toString  ===  obj . __proto__ . toString , 
 )  ;   //  输出 :true 
 

1.2 原型是怎么出现在 一个 对象上的

到这里有个问题,到底什么是原型,原型是怎么来的。

首先看一段 代码 :

  function   Point  ( x ,  y )   { 
   this  . x  =  x ; 
   this  . y  =  y ; 
 } 

 var  point  =   new   Point  (  ,   )  ; 

console .  log  ( point . __proto__ )  ; 
 

这样打印出来的 point 的原型对象,除了 constructor 和 __proto__ 属性 ,就什么都没有了。

接下来做个改写:

  function   Point  ( x ,  y )   { 
   this  . x  =  x ; 
   this  . y  =  y ; 
 } 

Point . prototype .  info   =   function  (  )   { 
  console .  log  (  'x: '   +   this  . x  +   ', y: '   +   this  . y )  ; 
 }  ; 

 var  point  =   new   Point  (  ,   )  ; 

point .  info  (  )  ;   //  输出 :"x: 1, y: 2" 

console .  log  ( point . __proto__ )  ; 
 

这样 输出 的 point 的原型对象,就具有了 一个 info 方法 。

从这就可以看出对象的原型,和他的构造 函数 的 prototype 属性 是有关的。

所有 函数 都具有 一个 prototype 属性 ,翻译过来也是 原型的意思 。

当 一个 函数 作为构造 函数 被 调用 的时候,就会把这个 函数 的 prototype 属性 ,作为构造 函数 生成 的对象的原型。

使用相等运算符,就可以验证上面这个规则:

 console .  log  ( 
  point . __proto__  ===  Point . prototype , 
 )  ;   //  输出 :true 
 

这就是 一个 对象原型的由来。

如果要知道对象由哪个构造 函数 生成 ,可以从 constructor 属性 获取 到,原型对象的 constructor 属性 则指向这个原型所处的 函数 。

这一点也可以由相等运算符验证, point 对象的 constructor 属性 和其原型对象下的 constructor 应该都指向同 一个 ,也就是 Point 函数 。

 console .  log  ( 
  point . constructor  ===  point . __proto__ . constructor ,   //  输出 :true 
  point . constructor  ===  Point ,   //  输出 :true 
  point . __proto__ . constructor  ===  Point ,   //  输出 :true 
 )  ; 
 

事实上对象的 constructor 属性 就是直接从原型上继承的。

1.3 原型链

前面有提到访问对象 属性 的机制。

  function   Point  ( x ,  y )   { 
   this  . x  =  x ; 
   this  . y  =  y ; 
 } 

 var  point  =   new   Point  (  ,   )  ; 

console .  log  ( point .  toString  (  )  )  ; 
 

假如要访问 point 对象的 toString 方法 ,首先会去 point 类里找,很显然是没有这个 方法 的。

然后回去 point 类的原型对象上找,也就是 Point 函数 的 prototype 属性 上,很显然也是没有的。

然后会再往上一层找,也就是找到了 Point.prototype.__proto__ 上 (等同于 point.__proto__.__proto__ ),这个时候就找到了 toString ,随后被返回并且 调用 。

Point.prototype.__proto__ 其实就是 Object.prototype 。

 console .  log  ( 
  Point . prototype . __proto__  ===  Object . prototype , 
 )  ;   //  输出 :true 
 

假如检查到 Object.prototype 还没有目标 属性 ,则在往上就找不到了,因为 Object.prototype.__proto__ 是 null 。

也就是说原型查找的末端是 null ,碰到 null 就会终止查找。

这些原型环环相扣,就形成了 原型链 。

有些同学会有疑问,为什么 Point.prototype 的原型是 Object.prototype 。其实 Point.prototype 也是 一个 对象,可以理解成这个对象是通过 new Object 创建的,所以原型自然是 Object.prototype 。

@H_ 404 _776@2. proto 属性

在 Chrome浏览器 下通过访问对象的 __proto__ 属性 可以取到对象的原型对象,这是所有对象都具备的 属性 。

  var  date  =   new   Date  (  )  ; 

console .  log  ( date . __proto__ )  ; 
 

__proto__ 具有兼容性问题,因此开发中尽量不要使用到,他不在 ES6 之前的标准中,但是许多旧版浏览器也对他进行了实现。

在 ES6 中 __proto__ 属性 被定制成了规范。

3. Object.getPrototypeOf 方法

由于 __proto__ 存在一定兼容性的问题,可以使用 Object.getPrototypeOf 方法 代替 __ptoto__ 属性 。

  var  date  =   new   Date  (  )  ; 

 var  dateProto  =  Object .  getPrototypeOf  ( date )  ; 

console .  log  ( dateProto )  ; 
console .  log  ( dateProto  ===  date . __proto__ )  ;   //  输出 :true 
 

4. JavaScript 中没有类

在 JavaScript 中是没有类的概念的。

有其他面向对象开发经验的同学可能会被 new 关键字误导。

JavaScript 中采用的是原型的机制,很多文献会称其为 原型代理 ,但个人认为对于初学者使用 原型继承 的方式会更好理解一点,日常讨论中其实是 一个 意思,不需要过多纠正其说法。

类 和 原型 是两种不同的机制。

有关于类的 内容 ,篇幅很大,如果不熟悉但又感兴趣,可以尝试着接触一下其他面向对象的语言,如 Python 、 Java 、 C++ 。

ES6 提供了 class 关键字,引入了一些类相关的概念,但其底层运行机制依然是原型这一套,所以即便是有了 class 关键字来帮助开发者提升开发体验,但其本质依然不是类,只是一种原型写法的语法糖。

5. 小结

原型的概念至关重要,利用原型的机制可以开发出更加灵活的 JavaScript 应用。

利用原型,可以很好的复用一些 代码 ,虽然在 JavaScript 中没有类,但是我们可以利用原型这个特性来模拟类的实现,达到继承、多态、封装的 效果 ,实现 代码 逻辑的复用,同时可以更好的组织 代码 结构。

new 运算符与构造函数 ? ?getter & setter

查看更多关于JavaScript 原型的详细内容...

  阅读:37次

上一篇

下一篇

第1节:什么是 JavaScript    第2节:学习环境准备    第3节:调试方案    第4节:JavaScript 变量    第5节:JavaScript 数据类型    第6节:JavaScript if 语句    第7节:for 语句    第8节:JavaScript 算数运算符    第9节:JavaScript 比较运算符    第10节:JavaScript 逻辑运算符    第11节:JavaScript 函数    第12节:JavaScript 表达式    第13节:JavaScript 对象    第14节:JavaScript 字符串    第15节:JavaScript 数字    第16节:JavaScript 数组    第17节:JavaScript switch 语句    第18节:JavaScript while 语句    第19节:JavaScript 的 break 与 continue    第20节:JavaScript with    第21节:document.cookie    第22节:JavaScript Function    第23节:JavaScript Math    第24节:JavaScript Date    第25节:JavaScript RegExp    第26节:JavaScript JSON    第27节:什么是 DOM    第28节:DOM 和 JavaScript 的关系    第29节:获取和操作 DOM 节点    第30节:DOM 与事件    第31节:DOM 事件绑定    第32节:DOM 事件对象    第33节:DOM 事件流    第34节:事件相关的优化    第35节:自定义事件    第36节:表单校验    第37节:什么是 BOM    第38节:常用的 BOM 相关对象    第39节:BOM 常用属性和方法    第40节:AJAX    第41节:异常处理    第42节:三元运算符    第43节:逗号操作符    第44节:void    第45节:typeof    第46节:delete 操作符    第47节:debugger    第48节:getter & setter    第49节:new 运算符与构造函数    第50节:JavaScript 原型    第51节:JavaScript instanceof    第52节:JavaScript this    第53节:严格模式    第54节:作用域    第55节:闭包    第56节:变量提升    第57节:对象包装器    第58节:Lodash    第59节:moment    第60节:swiper    第61节:ECMAScript 6    第62节:Node.js    第63节:Babel    第64节:CSS 预处理器    第65节:代码规范    第66节:TypeScript    第67节:WebComponents    第68节:Vue、React、Angular    第69节:小程序    第70节:JavaScript 关键字    第71节:分号导致的问题    第72节:对象属性访问问题    第73节:this 使用问题    第74节:浮点数精度问题    第75节:独一无二的 NaN    第76节:避免全局污染    第77节:控制台观察对象问题    第78节:根据环境选择语言特性    第79节:相关资源