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 相关文章推荐
JS常用函数使用指南
Nov 23 Javascript
IE6/IE7中JavaScript json提示缺少标识符、字符串或数字问题处理
Dec 16 Javascript
javascript操作字符串的原生方法
Dec 22 Javascript
js+CSS实现模拟华丽的select控件下拉菜单效果
Sep 01 Javascript
JavaScript如何获取数组最大值和最小值
Nov 18 Javascript
AngularJS向后端ASP.NET API控制器上传文件
Feb 03 Javascript
快速解决Canvas.toDataURL 图片跨域的问题
May 10 Javascript
JS实现的幻灯片切换显示效果
Sep 07 Javascript
详解Node.js中的Async和Await函数
Feb 22 Javascript
10分钟彻底搞懂Http的强制缓存和协商缓存(小结)
Aug 30 Javascript
使用angular-cli webpack创建多个包的方法
Oct 16 Javascript
JS实现简单日历特效
Jan 03 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
使用TinyButStrong模板引擎来做WEB开发
2007/03/16 PHP
探讨php中防止SQL注入最好的方法是什么
2013/06/10 PHP
PHP实现的堆排序算法详解
2017/08/17 PHP
php基于Redis消息队列实现的消息推送的方法
2018/11/28 PHP
Thinkphp5框架ajax接口实现方法分析
2019/08/28 PHP
jQuery操作 input type=checkbox的实现代码
2012/06/14 Javascript
js使用html()或text()方法获取设置p标签的显示的值
2014/08/01 Javascript
js获取UserControl内容为拼html时提供方便
2014/11/02 Javascript
JavaScript中的Math.LOG2E属性使用详解
2015/06/14 Javascript
JavaScript模版引擎的基本实现方法浅析
2016/02/15 Javascript
深入探讨Vue.js组件和组件通信
2016/09/12 Javascript
bootstrap datepicker插件默认英文修改为中文
2017/07/28 Javascript
JS基于正则表达式实现的密码强度验证功能示例
2017/09/21 Javascript
Parcel.js + Vue 2.x 极速零配置打包体验教程
2017/12/24 Javascript
layui问题之渲染数据表格时,仅出现10条数据的解决方法
2019/09/12 Javascript
微信小程序实现3D轮播图效果(非swiper组件)
2019/09/21 Javascript
ES2020 已定稿,真实场景案例分析
2020/05/25 Javascript
Vue中添加滚动事件设置的方法详解
2020/09/14 Javascript
NodeJS开发人员常见五个错误理解
2020/10/14 NodeJs
原生js实现九宫格拖拽换位
2021/01/26 Javascript
利用python实现简单的邮件发送客户端示例
2017/12/23 Python
Python使用Django实现博客系统完整版
2020/09/29 Python
PyQt5每天必学之QSplitter实现窗口分隔
2018/04/19 Python
Python产生Gnuplot绘图数据的方法
2018/11/09 Python
python numpy存取文件的方式
2020/04/01 Python
谈一谈HTML5本地存储技术
2016/03/02 HTML / CSS
使用phonegap播放音频的实现方法
2017/03/31 HTML / CSS
中国专业的综合网上购物商城:京东
2016/08/02 全球购物
惠普墨西哥官方商店:HP墨西哥
2016/12/01 全球购物
夜大毕业生自我评价分享
2013/11/10 职场文书
经典优秀毕业生求职信范文分享
2013/12/18 职场文书
挂靠协议书范本
2014/04/22 职场文书
建设工程授权委托书
2014/09/22 职场文书
幼儿园国培研修日志
2015/11/13 职场文书
超市啤酒狂欢夜策划方案范文!
2019/07/03 职场文书
mysql中数据库覆盖导入的几种方式总结
2022/03/25 MySQL