本文实例讲述了JS原型和原型链原理与用法。分享给大家供大家参考,具体如下:
Javascript语言的继承机制一直很难被人理解。
它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承。
Brendan Eich设计javascript之初是为了实现网页与浏览器之间交互的一种简单的脚本语言
如果真的是一种简易的脚本语言,其实不需要有"继承"机制。但是,Javascript里面都是对象,必须有一种机制,将所有对象联系起来。所以,Brendan Eich最后还是设计了"继承"。
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象。每个构造函数都有prototype(原型)属性
每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含特定类型的所有实例共享的属性和方法,即这个原型对象是用来给实例共享属性和方法的。
而每个实例内部都有一个指向原型对象的指针。
原型链
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含指向原型对象内部的指针。我们让原型对象的实例(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
在对象实例中,访问对象原型的方法
此属性是浏览器支持的一个属性,并不是ECMAScript里的属性
对于不支持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.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。
本文向大家介绍JavaScript原型继承和原型链原理详解,包括了JavaScript原型继承和原型链原理详解的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了JavaScript原型继承和原型链原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在讨论原型继承之前,先回顾一下关于创建自定义类型的方式,这里推荐将构造函数和原型模式组
本文向大家介绍JS原型与原型链的深入理解,包括了JS原型与原型链的深入理解的使用技巧和注意事项,需要的朋友参考一下 要了解原型和原型链,首先要理解普通对象和函数对象。 一、普通对象和函数对象的区别 在Javascript的世界里,全都是对象,而对象之间也是存在区别,我们首先区分一下普通对象和函数对象,如下代码: 在上面的代码中可以看出,f1、f2和f3都是函数对象,而o1,o2和o3都是objec
本文向大家介绍JS原形与原型链深入详解,包括了JS原形与原型链深入详解的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了JS原形与原型链。分享给大家供大家参考,具体如下: 前言 在JS中,我们经常会遇到原型。字面上的意思会让我们认为,是某个对象的原型,可用来继承。但是其实这样的理解是片面的,下面通过本文来了解原型与原型链的细节,再顺便谈谈继承的几种方式。 原型 在讲到原型之前,我们先来回顾一
本文向大家介绍全面解析js中的原型,原型对象,原型链,包括了全面解析js中的原型,原型对象,原型链的使用技巧和注意事项,需要的朋友参考一下 理解原型 我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。看如下例子: 理解原型对象 根据上面代码,看下图: 需要理解三点: 我们只要创建了一个新的函
前言 JavaScript 不包含传统的类继承模型,而是使用 prototypal 原型模型。 虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。实现传统的类继承模型是很简单,但是实现 JavaScript 中的原型继承则要困难的多。 由于 JavaScript 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的
本文向大家介绍javascript学习笔记(五)原型和原型链详解,包括了javascript学习笔记(五)原型和原型链详解的使用技巧和注意事项,需要的朋友参考一下 私有变量和函数 在函数内部定义的变量和函数,如果不对外提供接口,外部是无法访问到的,也就是该函数的私有的变量和函数。 这样在函数对象Test外部无法访问变量color和fn,他们就变成私有的了: 静态变量和函数 当定义一个函数后通过点号