javascript原型继承工作原理和实例详解


Posted in Javascript onApril 07, 2016

先为大家分享JS原型继承实例,供大家参考,具体内容如下

一、JS原型继承

<!DOCTYPE html>
<html>

<head>
 <meta charset="UTF-8">
 <title>JS原型继承</title>
</head>

<body>
 <!--原型继承-->
 <script type="text/javascript">
 //clone()函数用来创建新的类Person对象
 var clone = function(obj) {
  var _f = function() {};
  //这句是原型式继承最核心的地方,函数的原型对象为对象字面量
  _f.prototype = obj;
  return new _f;
 }

 //先声明一个对象字面量
 var Animal = {
   somthing: 'apple',
   eat: function() {
    console.log("eat " + this.somthing);
   }
  }
  //不需要定义一个Person的子类,只要执行一次克隆即可
 var Cat = clone(Animal);
 //可以直接获得Person提供的默认值,也可以添加或者修改属性和方法
 console.log(Cat.eat());
 Cat.somthing = 'orange';
 console.log(Cat.eat());

 //声明子类,执行一次克隆即可
 var Someone = clone(Cat);
 </script>
</body>

</html>

二、JavaScript原型继承工作原理

JavaScript采用原型继承这事儿是众所皆知的,但由于它默认只提供了一个实现的实例,也就是 new 运算符,因此对于它的解释总是令人困惑,接下来阐明什么是原型继承以及在JavaScript中究竟如何使用原型继承。

原型继承的定义

当你阅读关于JS原型继承的解释时,你时常会看到以下这段文字:

  当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。——出自JavaScript秘密花园
大多数JavaScript的实现用 __proto__ 属性来表示一个对象的原型链。在这篇文章里我们将看到 __proto__与 prototype 的区别何在。

注:__proto__ 是一个不应在你代码中出现的非正规的用法,这里仅仅用它来解释JavaScript原型继承的工作原理。

以下代码展示了JS引擎如何查找属性:

function getProperty(obj, prop) { 
 if (obj.hasOwnProperty(prop)) 
 return obj[prop] 
 
 else if (obj.__proto__ !== null) 
 return getProperty(obj.__proto__, prop) 
 
 else 
 return undefined 
}

让我们举一个常见的例子:二维点,拥有二维坐标 x y ,同似拥有一个 print 方法。

用之前我们说过的原型继承的定义,我们创建一个对象 Point ,拥有三个属性:x,y 和 print 。为了能创建一个新的二维点,我们需要创建一个新的对象,让他其中的 __proto__ 属性指向 Point :

var Point = { 
 x: 0, 
 y: 0, 
 print: function () { console.log(this.x, this.y); } 
}; 
 
var p = {x: 10, y: 20, __proto__: Point}; 
p.print(); // 10 20

JavaScript怪异的原型继承

令人困惑的是,每个教授原型继承的人都不会给出这样一段代码,反而会给出下面这样的代码:

function Point(x, y) { 
 this.x = x; 
 this.y = y; 
} 
Point.prototype = { 
 print: function () { console.log(this.x, this.y); } 
}; 
 
var p = new Point(10, 20); 
p.print(); // 10 20

这和说好的不一样啊,这里 Point 变成了函数,然后还有个什么 prototype 的属性,而且有了 new 运算符。这他喵的是什么情况?

new 运算符是如何工作的

造物者 Brendan Eich 想让JS和传统的面向对象的编程语言差不太多,如Java和C++。在这些语言里,我们采用 new 运算符来给类实例化一个新的对象。所以他在JS里写了一个 new 运算符。

C++里有用来初始化实例属性的构造函数概念,因此 new 运算符必须针对函数。
我们需要将对象的方法放到一个地方去,既然我们在用原型语言,我们就把它放到函数的原型属性中去。
new 运算符接受一个函数 F 及其参数:new F(arguments...)。这一过程分为三步:

创建类的实例。这步是把一个空的对象的 __proto__ 属性设置为 F.prototype 。
初始化实例。函数 F 被传入参数并调用,关键字 this 被设定为该实例。
返回实例。
现在我们知道了 new 是怎么工作的,我们可以用JS代码实现一下:

function New (f) { 
 var n = { '__proto__': f.prototype }; /*第一步*/ 
 return function () { 
 f.apply(n, arguments);  /*第二步*/ 
 return n;    /*第三步*/ 
 }; 
}

一个小小的例子来看一下他的工作状况:

function Point(x, y) { 
 this.x = x; 
 this.y = y; 
} 
Point.prototype = { 
 print: function () { console.log(this.x, this.y); } 
}; 
 
var p1 = new Point(10, 20); 
p1.print(); // 10 20 
console.log(p1 instanceof Point); // true 
 
var p2 = New (Point)(10, 20); 
p2.print(); // 10 20 
console.log(p2 instanceof Point); // true

JavaScript中真正的原型继承

JS的ECMA规范只允许我们采用 new 运算符来进行原型继承。但是大宗师 Douglas Crockford 却发现了一种可以利用 new 来实现真正的原型继承的方式!他写下了 Object.create 函数如下:

Object.create = function (parent) { 
 function F() {} 
 F.prototype = parent; 
 return new F(); 
};

这看起来蛮奇怪的,但却是相当的简洁:它创建了新的对象,并将其原型设置为你想设置的任意值。如果我们允许使用 __proto__ ,那我们也可以这样写:

Object.create = function (parent) { 
 return { '__proto__': parent }; 
};

下面这段代码就是让我们的 Point 采用真正的原型继承:

var Point = { 
 x: 0, 
 y: 0, 
 print: function () { console.log(this.x, this.y); } 
}; 
 
var p = Object.create(Point); 
p.x = 10; 
p.y = 20; 
p.print(); // 10 20

结论

我们已经了解了JS原型继承是什么,以及JS如何用特定的方式来实现之。然而使用真正的原型继承(如 Object.create 以及 __proto__)还是存在以下缺点:

标准性差:__proto__ 不是一个标准用法,甚至是一个不赞成使用的用法。同时原生态的 Object.create 和道爷写的原版也不尽相同。
优化性差: 不论是原生的还是自定义的 Object.create ,其性能都远没有 new 的优化程度高,前者要比后者慢高达10倍。

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
javascript实用小函数使用介绍
Nov 11 Javascript
浅析JavaScript中的隐式类型转换
Dec 05 Javascript
jQuery div拖拽用法实例
Jan 14 Javascript
jQuery插件DataTable使用方法详解(.Net平台)
Dec 22 Javascript
详解vue中点击空白处隐藏div的实现(用指令实现)
Apr 19 Javascript
如何在Angular应用中创建包含组件方法示例
Mar 23 Javascript
AngularJs中$cookies简单用法分析
May 30 Javascript
JS实现求字符串中出现最多次数的字符和次数示例
Jul 05 Javascript
node.js处理前端提交的GET请求
Aug 30 Javascript
js实现课堂随机点名系统
Nov 21 Javascript
Vue.js实现立体计算器
Feb 22 Javascript
quickjs 封装 JavaScript 沙箱详情
Nov 02 Javascript
浅析JS原型继承与类的继承
Apr 07 #Javascript
AngularJs 弹出模态框(model)
Apr 07 #Javascript
详解AngularJS 模态对话框
Apr 07 #Javascript
分离与继承的思想实现图片上传后的预览功能:ImageUploadView
Apr 07 #Javascript
jQuery动态添加
Apr 07 #Javascript
javascript模块化简单解析
Apr 07 #Javascript
jquery编写Tab选项卡滚动导航切换特效
Jul 17 #Javascript
You might like
一个PHP数组应该有多大的分析
2009/07/30 PHP
PHP使用glob函数遍历目录或文件夹的方法
2014/12/16 PHP
php mysql操作mysql_connect连接数据库实例详解
2016/12/26 PHP
基于php解决json_encode中文UNICODE转码问题
2020/11/10 PHP
JScript中的undefined和&quot;undefined&quot;的区别
2007/03/08 Javascript
jquery的index方法实现tab效果
2011/02/16 Javascript
Jquery 实现表格颜色交替变化鼠标移过颜色变化实例
2013/08/28 Javascript
js动态添加表格数据使用insertRow和insertCell实现
2014/05/22 Javascript
JavaScript使用Max函数返回两个数字中较大数的方法
2015/04/06 Javascript
js实现可折叠展开的手风琴菜单效果
2015/09/07 Javascript
JavaScript reduce和reduceRight详解
2016/10/24 Javascript
JS判断键盘是否按的回车键并触发指定按钮点击操作的方法
2017/02/13 Javascript
javascript  数组排序与对象排序的实例
2017/07/17 Javascript
elementUI Tree 树形控件的官方使用文档
2019/04/25 Javascript
[01:50]2014DOTA2西雅图邀请赛 专访欢乐周宝龙
2014/07/08 DOTA
Python网站验证码识别
2016/01/25 Python
视觉直观感受若干常用排序算法
2017/04/13 Python
python WindowsError的错误代码详解
2017/07/23 Python
Python网络爬虫神器PyQuery的基本使用教程
2018/02/03 Python
python 获取list特定元素下标的实例讲解
2018/04/09 Python
Python numpy实现二维数组和一维数组拼接的方法
2018/06/05 Python
修复 Django migration 时遇到的问题解决
2018/06/14 Python
pandas DataFrame 行列索引及值的获取的方法
2019/07/02 Python
opencv python 图像轮廓/检测轮廓/绘制轮廓的方法
2019/07/03 Python
python实现递归查找某个路径下所有文件中的中文字符
2019/08/31 Python
一款简洁的纯css3代码实现的动画导航
2014/10/31 HTML / CSS
详解HTML5中div和section以及article的区别
2015/07/14 HTML / CSS
详解HTML5 window.postMessage与跨域
2017/05/11 HTML / CSS
使用SVG实现提示框功能的示例代码
2020/06/05 HTML / CSS
施华洛世奇巴西官网:SWAROVSKI巴西
2019/12/03 全球购物
医院辞职信范文
2014/01/17 职场文书
小学六一儿童节活动方案
2014/08/27 职场文书
孩子教育的心得体会
2014/09/01 职场文书
祝寿主持词
2015/07/02 职场文书
2016年社区“我们的节日·中秋节”活动总结
2016/04/05 职场文书
2019年“红色之旅”心得体会1000字(3篇)
2019/09/27 职场文书