JS原型和原型链原理与用法实例详解


Posted in Javascript onFebruary 05, 2020

本文实例讲述了JS原型和原型链原理与用法。分享给大家供大家参考,具体如下:

Javascript语言的继承机制一直很难被人理解。

它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承。

Brendan Eich设计javascript之初是为了实现网页与浏览器之间交互的一种简单的脚本语言

如果真的是一种简易的脚本语言,其实不需要有"继承"机制。但是,Javascript里面都是对象,必须有一种机制,将所有对象联系起来。所以,Brendan Eich最后还是设计了"继承"。

背景介绍

1.构造函数

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象。每个构造函数都有prototype(原型)属性

2.原型模式

每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含特定类型的所有实例共享的属性和方法,即这个原型对象是用来给实例共享属性和方法的。

而每个实例内部都有一个指向原型对象的指针。

JS原型和原型链原理与用法实例详解

原型链

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含指向原型对象内部的指针。我们让原型对象的实例(1)等于另一个原型对象(2),

此时原型对象(2)将包含一个指向原型对象(1)的指针,

再让原型对象(2)的实例等于原型对象(3),如此层层递进就构成了实例和原型的链条,这就是原型链的概念

构造函数

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象。 即为对象变量赋初始值。每个构造函数的实例都将共享构造函数的初始值。 构造函数的出现是为了解决使用Object构造函数和字面量表示法不方便创建大量重复对象的问题。

传统创建对象实例的方法

var person={
    name:'张女士',
    age:'80',
    gender:'女'
  };
 console.log(person)

注:这个方法如果用于创建大量相同属性和方法的对象时,会产生大量重复代码

构造函数的方法

//构造函数方法创建对象实例
function Person(name,age,gender) {
this.name=name;
this.age=age;
this.gender=gender;
this.say=function () {
    alert(this.name)
  }
}
var person1=new Person('钟女士',80,'女');
var person2=new Person('张女士',80,'女');
console.log(person2)
console.log(person1)

原型模式

使用构造函数的问题是,每个方法都要在每个实例上重新创建一遍,即在构造函数的不同实例上的同名函数是不相等的。而我们创建每个构造函数都有一个prototype(原型)属性,这个属性是个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,我们使用这个原型对象来共享实例的属性和方法的模式就叫原型模式

//原型模式创建对象
function Person(){
}
Person.prototype.name='钟女士';
Person.prototype.age=80;
Person.prototype.gender='女';
var person1= new Person();
console.log(person1)
//简写原型模式
Person.prototype={
  constructor:Person
  name:'钟女士',
  age:80,
  gender:'女'
}

注:每个原型对象都有constructor属性,由于简写模式重写了默认的prototype对象,所以constructor也会被重新定义,不再指向他的构造函数,所以可以自己写一个constructor属性指向他的构造函数

原型链

每个构造函数都有原型对象,每个构造函数实例都包含一个指向原型对象的内部指针(proto),如果我们让第一个构造函数的原型对象等于第二个构造函数的实例,结果第一个构造函数的原型对象将包含一个指向第二个原型对象的指针,再然第三个原型对象等于第一个构造函数的实例,这样第三个原型对象也将包含指向第一个原型对象的指针,以此类推,就够成了实例于原型的链条,这就是原型链的基本概念

function One(){
}
function Two(){
}
function Three(){
}
Two.prototype=new One();
Three.prototype=new Two();
var three=new Three();
console.log(three);
console.log(three.__proto__===Three.prototype) //true
console.log(three.__proto__.__proto__===Two.prototype) //true
console.log(three.__proto__.__proto__.__proto__===One.prototype) //true
console.log(three.__proto__.__proto__.__proto__.__proto__===Object.prototype) //true

在对象实例中,访问对象原型的方法

  • 1、使用proto属性

此属性是浏览器支持的一个属性,并不是ECMAScript里的属性

  • 2.Object.getPrototypeOf
  • 3.使用constructor.prototype的方法

对于不支持proto的浏览器,可以使用constructor,访问到对象的构造函数,在用prototype访问到原型

使用原型链解释ANUGLAR作用域

在开发过程中,我们可能会出现控制器的嵌套,看下面这段代码:

<div ng-controller="OuterCtrl">
    <span>{{a}}</span>
     <div ng-controller="InnerCtrl">
      <span>{{a}}</span>
     </div>
   </div>
  <script>
  function OuterCtrl($scope) {
  $scope.a = 1;
  }
  function InnerCtrl($scope) {
  }
  </script>

我们可以看到界面显示了两个1,而我们只在OuterCtrl的作用域里定义了a变量,但界面给我们的结果是,两个a都有值,现在自控制器里的a是从父控制器里继承过来的

我们可以父子级的作用域看成两个原型对象,其中一个原型对象继承另一个原型对象的实例

function Outer() {
  this.a = 1;
}
function Inner() {
}
var outer = new Outer();
Inner.prototype=new Outer();
var inner = new Inner();
console.log(outer.a)
console.log(inner.a)

Angular的实现机制其实也就是把这两个控制器中的$scope作了关联,外层的作用域实例成为了内层作用域的原型。

既然作用域是通过原型来继承的,自然也就可以推论出一些特征来。比如说这段代码,点击按钮的结果是什么?

<div ng-controller="OuterCtrl">
  <span>{{a}}</span>
  <div ng-controller="InnerCtrl">
    <span>{{a}}</span>
    <button ng-click="a=a+1">a++</button>
  </div>
</div>
<script>
function OuterCtrl($scope) {
  $scope.a = 1;
}
 
function InnerCtrl($scope) {
}
</script>

点了按钮之后,两个a不一致了,里面的变了,外面的没变,这是为什么?

function Outer() {
  this.a = 1;
}
function Inner() {
}
var outer = new Outer();
Inner.prototype=new Outer();
var inner = new Inner();
inner.a = inner.a + 1;
console.log(outer.a)
console.log(inner.a)

因为在原型链中,访问一个实例属性时,会在实例本身查找,如果找不到,则搜索实例的原型,如果再搜索不到,则继续沿着原型链往上查找。找到之后则会赋给该实例,所以inner上面就被赋值了一个新的a,outer里面的仍然保持原样,这也就导致了刚才看到的结果。

上下级共享变量

比如说,我们就是想上下级共享变量,不创建新的,该怎么办呢?

function Outer() {
  this.data = {
    a: 1
  };
}
function Inner() {
}
var outer = new Outer();
Inner.prototype = outer;
var inner = new Inner();
console.log(outer.data.a);
console.log(inner.data.a);
inner.data.a += 1;
console.log(outer.data.a);
console.log(inner.data.a);

我们可以把a写在一个对象里,当inner找到对象data并赋值到自己身上时,其实是复制了对象的指针(参考高程第4章复制引用类型和基本类型的区别),我们对对象里的属性的改动都会反映到所有引用该对象的元素上。
反映到AngularJs,我们可以这么写

<div ng-controller="OuterCtrl">
  <span>{{data.a}}</span>
  <div ng-controller="InnerCtrl">
    <span>{{data.a}}</span>
    <button ng-click="data.a=data.a+1">increase a</button>
  </div>
</div>
<script>
function OuterCtrl($scope) {
  $scope.data = {
    a: 1
  };
}
function InnerCtrl($scope) {
}
</script>

这样点击按钮两个控制器的a都会+1

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
JavaScript 封装Ajax传递的数据代码
Jun 05 Javascript
JavaScript 直接操作本地文件的实现代码
Dec 01 Javascript
HTML5之lang属性与dir属性的详解
Jun 19 Javascript
javascript学习笔记(二)数组和对象部分
Sep 30 Javascript
jQuery给元素添加样式的方法详解
Dec 30 Javascript
[原创]SyntaxHighlighter自动识别并加载脚本语言
Feb 07 Javascript
js实现打地鼠小游戏
Feb 13 Javascript
ES6新特性五:Set与Map的数据结构实例分析
Apr 21 Javascript
Jquery的autocomplete插件用法及参数讲解
Mar 12 jQuery
jQuery实现带3D切割效果的轮播图功能示例【附源码下载】
Apr 04 jQuery
JavaScript键盘事件响应顺序详解
Sep 30 Javascript
Nuxt 项目性能优化调研分析
Nov 07 Javascript
js实现视图和数据双向绑定的方法分析
Feb 05 #Javascript
小程序如何写动态标签的实现方法
Feb 05 #Javascript
vue如何实现动态加载脚本
Feb 05 #Javascript
vue实现图片懒加载的方法分析
Feb 05 #Javascript
Vue组件基础用法详解
Feb 05 #Javascript
vue组件传值的实现方式小结【三种方式】
Feb 05 #Javascript
vue路由传参的基本实现方式小结【三种方式】
Feb 05 #Javascript
You might like
php实现的三个常用加密解密功能函数示例
2017/11/06 PHP
求解开jscript.encode代码的asp函数
2007/02/28 Javascript
jQueryUI的Dialog的简单封装
2010/06/07 Javascript
jQuery学习笔记之jQuery的DOM操作
2010/12/22 Javascript
jQuery仿Excel表格编辑功能的实现代码
2013/05/01 Javascript
解析javascript 实用函数的使用详解
2013/05/10 Javascript
jquery实现带复选框的表格行选中删除时高亮显示
2013/08/01 Javascript
jquery获取URL中参数解决中文乱码问题的两种方法
2013/12/18 Javascript
javascript实现浏览器窗口传递参数的方法
2014/09/03 Javascript
jQuery实现下拉菜单(内容为时间)的实时更新及图表的随动更新的方法
2016/07/07 Javascript
AngularJS基础 ng-hide 指令用法及示例代码
2016/08/01 Javascript
基于JS实现类似支付宝支付密码输入框
2016/09/02 Javascript
Bootstrap实现渐变顶部固定自适应导航栏
2020/08/27 Javascript
老生常谈jquery中detach()和remove()的区别
2017/03/02 Javascript
JavaScript 上传文件(psd,压缩包等),图片,视频的实现方法
2017/06/19 Javascript
如何重置vue打印变量的显示方式
2017/12/06 Javascript
vue-cli3.0+element-ui上传组件el-upload的使用
2018/12/03 Javascript
vue中进行微博分享的实例讲解
2019/10/14 Javascript
vue实现在线预览pdf文件和下载(pdf.js)
2019/11/26 Javascript
JavaScript 空间坐标的使用
2020/08/19 Javascript
在服务器端实现无间断部署Python应用的教程
2015/04/16 Python
Python基于property实现类的特性操作示例
2018/06/15 Python
Python编写打字训练小程序
2019/09/26 Python
Python在终端通过pip安装好包以后在Pycharm中依然无法使用的问题(三种解决方案)
2020/03/10 Python
python实现启动一个外部程序,并且不阻塞当前进程
2020/12/05 Python
浅析python字符串前加r、f、u、l 的区别
2021/01/24 Python
英国时尚饰品和发饰购物网站:Claire’s
2017/07/04 全球购物
美国演唱会和体育门票购买网站:Ticketnetwork
2018/10/19 全球购物
什么是Assembly(程序集)
2014/09/14 面试题
英文导游欢迎词
2014/01/11 职场文书
团委书记的竞聘演讲稿
2014/04/24 职场文书
出国留学自荐信模板
2015/03/06 职场文书
2015年网管个人工作总结
2015/05/22 职场文书
小学英语听课心得体会
2016/01/14 职场文书
2016年暑期社会实践活动总结报告
2016/04/06 职场文书
世界文化遗产导游词
2019/08/07 职场文书