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 日期对象Date扩展方法
May 30 Javascript
javascript动态改变img的src属性图片不显示的解决方法
Oct 20 Javascript
自定义一个jquery插件[鼠标悬浮时候 出现说明label]
Jun 27 Javascript
jquery属性过滤选择器使用示例
Jun 18 Javascript
jQuery性能优化的38个建议
Mar 04 Javascript
JavaScript简介
Feb 15 Javascript
AngularJS 整理一些优化的小技巧
Aug 18 Javascript
Vue.js每天必学之构造器与生命周期
Sep 05 Javascript
微信小程序使用audio组件播放音乐功能示例【附源码下载】
Dec 08 Javascript
Angular实现svg和png图片下载实现
May 05 Javascript
jquery操作checkbox的常用方法总结【附测试源码下载】
Jun 10 jQuery
TypeScript 引用资源文件后提示找不到的异常处理技巧
Jul 15 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查询附近的人及其距离的实现方法
2016/05/11 PHP
thinkPHP框架实现类似java过滤器的简单方法示例
2018/09/05 PHP
document.compatMode介绍
2009/05/21 Javascript
Aptana调试javascript图解教程
2009/11/30 Javascript
Date对象格式化函数代码
2010/07/17 Javascript
不用锚点也可以平滑滚动到页面的指定位置实现代码
2013/05/08 Javascript
鼠标经过tr时,改变tr当前背景颜色
2014/01/13 Javascript
JavaScript弹出窗口方法汇总
2014/08/12 Javascript
JavaScript判断字符长度、数字、Email、电话等常用判断函数分享
2015/04/01 Javascript
javascript模拟命名空间
2015/04/17 Javascript
JavaScript中this详解
2015/09/01 Javascript
js电话号码验证方法
2015/09/28 Javascript
JS实现兼容性较好的随屏滚动效果
2015/11/09 Javascript
jquery 无限极下拉菜单的简单实例(精简浓缩版)
2016/05/31 Javascript
第一次接触神奇的Bootstrap导航条
2016/08/09 Javascript
jQuery简单自定义图片轮播插件及用法示例
2016/11/21 Javascript
jsp 自动编译机制详细介绍
2016/12/01 Javascript
解决vue中使用Axios调用接口时出现的ie数据处理问题
2018/08/13 Javascript
JavaScript设计模式之观察者模式实例详解
2019/01/16 Javascript
javascript+HTML5 canvas绘制时钟功能示例
2019/05/15 Javascript
php结合js实现多条件组合查询
2019/05/28 Javascript
nodejs log4js 使用详解
2019/05/31 NodeJs
Vue的click事件防抖和节流处理详解
2019/11/13 Javascript
快速解决element的autofocus失效问题
2020/09/08 Javascript
[02:12]DOTA2英雄基础教程 变体精灵
2013/12/16 DOTA
实践Python的爬虫框架Scrapy来抓取豆瓣电影TOP250
2016/01/20 Python
python tools实现视频的每一帧提取并保存
2020/03/20 Python
通过cmd进入python的实例操作
2019/06/26 Python
使用python实现飞机大战游戏
2020/03/23 Python
Pycharm配置PyQt5环境的教程
2020/04/02 Python
Pandas实现一列数据分隔为两列
2020/05/18 Python
Django解决frame拒绝问题的方法
2020/12/18 Python
专业幼师实习生自我鉴定范文
2013/12/08 职场文书
周一给客户的问候语
2015/11/10 职场文书
Java 超详细讲解ThreadLocal类的使用
2022/04/07 Java/Android
Spring Cloud OpenFeign模版化客户端
2022/06/25 Java/Android