0

0

javascript学习笔记(五)原型和原型链详解_基础知识

php中文网

php中文网

发布时间:2016-05-16 16:34:49

|

1389人浏览过

|

来源于php中文网

原创

私有变量和函数

在函数内部定义的变量和函数,如果不对外提供接口,外部是无法访问到的,也就是该函数的私有的变量和函数。

复制代码 代码如下:

<script type="text/<a style=" color: text-decoration:underline title="javascript" href="https://www.php.cn/zt/15724.html" target="_blank">javascript"><br /> function Test(){<br /> var color = "blue";//私有变量<br /> var fn = function() //私有函数<br /> { <p> }<br /> }<br /> </script>

这样在函数对象Test外部无法访问变量color和fn,他们就变成私有的了:

复制代码 代码如下:

var obj = new Test();
    alert(obj.color);//弹出 undefined
    alert(obj.fn);//同上

静态变量和函数

当定义一个函数后通过点号 “.”为其添加的属性和函数,通过对象本身仍然可以访问得到,但是其实例却访问不到,这样的变量和函数分别被称为静态变量和静态函数。

立即学习Java免费学习笔记(深入)”;

复制代码 代码如下:

<script type="text/javascript"><br /> function Obj(){ <p> }<br /> Obj.num = 72;//静态变量<br /> Obj.fn = function() //静态函数<br /> { <p> } <p> alert(Obj.num);//72<br /> alert(typeof Obj.fn)//function <p> var t = new Obj();<br /> alert(t.name);//undefined<br /> alert(typeof t.fn);//undefined<br /> </script>

实例变量和函数

在面向对象编程中除了一些库函数我们还是希望在对象定义的时候同时定义一些属性和方法,实例化后可以访问,JavaScript也能做到这样

复制代码 代码如下:

<script type="text/javascript"><br /> function Obj(){<br /> this.a=[]; //实例变量<br /> this.fn=function(){ //实例方法 <p> }<br /> } <p> console.log(typeof Obj.a); //undefined<br /> console.log(typeof Obj.fn); //undefined <p> var o=new Obj();<br /> console.log(typeof o.a); //object<br /> console.log(typeof o.fn); //function<br /> </script>

为实例变量和方法添加新的方法和属性

复制代码 代码如下:

<script type="text/javascript"><br /> function Obj(){<br /> this.a=[]; //实例变量<br /> this.fn=function(){ //实例方法 <p> }<br /> } <p> var o1=new Obj();<br /> o1.a.push(1);<br /> o1.fn={};<br /> console.log(o1.a); //[1]<br /> console.log(typeof o1.fn); //object <p> var o2=new Obj();<br /> console.log(o2.a); //[]<br /> console.log(typeof o2.fn); //function<br /> </script>

在o1中修改了a和fn,而在o2中没有改变,由于数组和函数都是对象,是引用类型,这就说明o1中的属性和方法与o2中的属性与方法虽然同名但却不是一个引用,而是对Obj对象定义的属性和方法的一个复制。

这个对属性来说没有什么问题,但是对于方法来说问题就很大了,因为方法都是在做完全一样的功能,但是却又两份复制,如果一个函数对象有上千和实例方法,那么它的每个实例都要保持一份上千个方法的复制,这显然是不科学的,这可肿么办呢,prototype应运而生。

基本概念

我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。那么,prototype就是通过调用构造函数而创建的那个对象实例的原型对象。

使用原型的好处是可以让对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中添加定义对象信息,而是可以直接将这些信息添加到原型中。使用构造函数的主要问题就是每个方法都要在每个实例中创建一遍。

在JavaScript中,一共有两种类型的值,原始值和对象值。每个对象都有一个内部属性 prototype ,我们通常称之为原型。原型的值可以是一个对象,也可以是null。如果它的值是一个对象,则这个对象也一定有自己的原型。这样就形成了一条线性的链,我们称之为原型链。

含义

函数可以用来作为构造函数来使用。另外只有函数才有prototype属性并且可以访问到,但是对象实例不具有该属性,只有一个内部的不可访问的__proto__属性。__proto__是对象中一个指向相关原型的神秘链接。按照标准,__proto__是不对外公开的,也就是说是个私有属性,但是Firefox的引擎将他暴露了出来成为了一个共有的属性,我们可以对外访问和设置。

复制代码 代码如下:

<script type="text/javascript"><br /> var Browser = function(){};<br /> Browser.prototype.run = function(){<br /> alert("I'm Gecko,a kernel of firefox&#65532;");<br /> } <p> var Bro = new Browser();<br /> Bro.run();<br /> </script>

当我们调用Bro.run()方法时,由于Bro中没有这个方法,所以,他就会去他的__proto__中去找,也就是Browser.prototype,所以最终执行了该run()方法。(在这里,函数首字母大写的都代表构造函数,以用来区分普通函数)

当调用构造函数创建一个实例的时候,实例内部将包含一个内部指针(__proto__)指向构造函数的prototype,这个连接存在于实例和构造函数的prototype之间,而不是实例与构造函数之间。

复制代码 代码如下:

<script type="text/javascript"><br /> function Person(name){<br /> this.name=name;<br /> } <p> Person.prototype.printName=function(){<br /> alert(this.name);<br /> } <p> var person1=new Person('Byron');<br /> var person2=new Person('Frank');<br /> </script>

Person的实例person1中包含了name属性,同时自动生成一个__proto__属性,该属性指向Person的prototype,可以访问到prototype内定义的printName方法,大概就是这个样子的:

再举个栗子:

复制代码 代码如下:

<script type="text/javascript"><br /> function Animal(name) //积累构造函数<br /> {<br /> this.name = name;//设置对象属性<br /> } <p> Animal.prototype.behavior = function() //给基类构造函数的prototype添加behavior方法<br /> { <br /> alert("this is a "+this.name);<br /> } <p> var Dog = new Animal("dog");//创建Dog对象<br /> var Cat = new Animal("cat");//创建Cat对象 <p> Dog.behavior();//通过Dog对象直接调用behavior方法<br /> Cat.behavior();//output "this is a cat" <p> alert(Dog.behavior==Cat.behavior);//output true;<br /> </script>

可以从程序运行结果看出,构造函数的prototype上定义的方法确实可以通过对象直接调用到,而且代码是共享的。(可以试一下将Animal.prototype.behavior 中的prototype属性去掉,看看还能不能运行。)在这里,prototype属性指向Animal对象。

数组对象实例

再看个数组对象的实例。当我们创建出array1这个对象的时候,array1实际在Javascript引擎中的对象模型如下:

复制代码 代码如下:

var array1 = [1,2,3];

array1对象具有一个length属性值为3,但是我们可以通过如下的方法来为array1增加元素:

PatentPal专利申请写作
PatentPal专利申请写作

AI软件来为专利申请自动生成内容

下载

array1.push(4);
push这个方法来自于array1的__proto__成员指向对象的一个方法(Array.prototye.push())。正是因为所有的数组对象(通过[]来创建的)都包含有一个指向同一个具有push,reverse等方法对象(Array.prototype)的__proto__成员,才使得这些数组对象可以使用push,reverse等方法。

函数对象实例

复制代码 代码如下:

function Base() { 
    this.id = "base"
}  

复制代码 代码如下:

var obj = new Base();

这样代码的结果是什么,我们在Javascript引擎中看到的对象模型是:

new操作符具体干了什么呢?其实很简单,就干了三件事情。

复制代码 代码如下:

var obj  = {}; 
obj.__proto__ = Base.prototype; 
Base.call(obj);

原型链

原型链:当从一个对象那里调取属性或方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象那里寻找,如果prototype没有,就会去prototype关联的前辈prototype那里寻找,如果再没有则继续查找Prototype.Prototype引用的对象,依次类推,直到Prototype.….Prototype为undefined(Object的Prototype就是undefined)从而形成了所谓的“原型链”。

复制代码 代码如下:

<script type="text/javascript"><br /> function Shape(){<br /> this.name = "shape";<br /> this.toString = function(){<br /> return this.name;<br /> }<br /> }<br /> function TwoShape(){<br /> this.name = "2 shape";<br /> }<br /> function Triangle(side,height){<br /> this.name = "Triangle";<br /> this.side = side;<br /> this.height = height;<br /> this.getArea = function(){<br /> return this.side*this.height/2;<br /> }<br /> } <p> TwoShape.prototype = new Shape();<br /> Triangle.prototype = new TwoShape();<br /> </script>

这里,用构造器Shape()新建了一个实体,然后用它去覆盖该对象的原型。

复制代码 代码如下:

<script type="text/javascript"><br /> function Shape(){<br /> this.name = "shape";<br /> this.toString = function(){<br /> return this.name;<br /> }<br /> }<br /> function TwoShape(){<br /> this.name = "2 shape";<br /> }<br /> function Triangle(side,height){<br /> this.name = "Triangle";<br /> this.side = side;<br /> this.height = height;<br /> this.getArea = function(){<br /> return this.side*this.height/2;<br /> }<br /> } <p> TwoShape.prototype = new Shape();<br /> Triangle.prototype = new TwoShape(); <p> TwoShape.prototype.constructor = TwoShape;<br /> Triangle.prototype.constructor = Triangle; <p> var my = new Triangle(5,10);<br /> my.getArea();<br /> my.toString();//Triangle<br /> my.constructor;//Triangle(side,height)<br /> </script>

原型继承

原型继承:在原型链的末端,就是Object构造函数prototype属性指向的那个原型对象。这个原型对象是所有对象的祖先,这个老祖宗实现了诸如toString等所有对象天生就该具有的方法。其他内置构造函数,如Function,Boolean,String,Date和RegExp等的prototype都是从这个老祖宗传承下来的,但他们各自又定义了自身的属性和方法,从而他们的子孙就表现出各自宗族的那些特征。

ECMAScript中,实现继承的方法就是依靠原型链实现的。

复制代码 代码如下:

<script type="text/javascript"><br /> function Box(){ //被继承的函数叫做超类型(父类,基类)<br /> this.name = "Jack";<br /> } <p> function Tree(){ //继承的函数叫做子类型(子类,派生类)<br /> this.age = 300;<br /> }<br /> //通过原型链继承,赋值给子类型的原型属性<br /> //new Box()会将box构造里的信息和原型里的信息都交给Tree<br /> Tree.prototype = new Box();//Tree继承了Box,通过原型,形成链条 <p> var tree = new Tree();<br /> alert(tree.name);//弹出 Jack<br /> </script>

原型链的问题:原型链虽然很强大,可以用它来实现继承,但它也存在一些问题。其中最主要的问题来自包含引用类型的值原型。包含引用类型的原型属性会被所有实例共享;而这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。在通过原型来实现继承时,原型实际上回变成另一个类型的实例。于是,原先的实例属性也就变成了原型的属性。

在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。再加上刚刚讨论的由于原型中包含引用类型值所带来的问题,实践中很少会单独使用原型链。

再举个栗子:

复制代码 代码如下:

<script type="text/javascript"><br /> function Person(name)<br /> {<br /> this.name = name;//设置对象属性<br /> }; <p> Person.prototype.company = "Microsoft";//设置原型的属性<br /> Person.prototype.SayHello = function() //原型的方法<br /> { <br /> alert("Hello,I'm "+ this.name+ " of " + this.company);<br /> }; <p> var BillGates = new Person("BillGates");//创建person对象<br /> BillGates.SayHello();//继承了原型的内容,输出"Hello,I'm BillGates of Microsoft" <p> var Jobs = new Person("Jobs");<br /> Jobs.company = "Apple";//设置自己的company属性,掩盖了原型的company属性<br /> Jobs.SayHello = function()<br /> {<br /> alert("Hi,"+this.name + " like " + this.company);<br /> };<br /> Jobs.SayHello();//自己覆盖的属性和方法,输出"Hi,Jobs like Apple"<br /> BillGates.SayHello();//Jobs的覆盖没有影响原型,BillGates还是照样输出<br /> </script>

看下面一个原型链例子:

复制代码 代码如下:

<script type="text/javascript"><br /> function Year(){<br /> this.value = 21;<br /> }<br /> Year.prototype = {<br /> method:function(){ <p> }<br /> }; <p> function Hi(){ <p> };<br /> //设置Hi的prototype属性为Year的实例对象<br /> Hi.prototype = new Year();<br /> Hi.prototype.year = 'Hello World'; <p> Hi.prototype.constructor = Hi; <p> var test = new Hi();//创建一个Hi的新实例 <p> //原型链<br /> test [Hi的实例]<br /> Hi.prototype [Year的实例]<br /> {year:'Hello World'}<br /> Year.prototype<br /> {method:……};<br /> object.prototype<br /> {toString:...}; <p></script>

从上面例子中,test对象从Hi.prototype和Year.prototype中继承下来;因此他能访问Year的原型方法method,同时他能访问实例属性value

__ptoto__属性

__ptoto__属性(IE浏览器不支持)是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor,通过这两个属性,就可以访问原型里的属性和方法了。

Javascript中的对象实例本质上是由一系列的属性组成的,在这些属性中,有一个内部的不可见的特殊属性——__proto__,该属性的值指向该对象实例的原型,一个对象实例只拥有一个唯一的原型。

复制代码 代码如下:

<script type="text/javascript"><br /> function Box(){ //大写,代表构造函数<br /> Box.prototype.name = "trigkit4";//原型属性<br /> Box.prototype.age = "21";<br /> Box.prototype.run = function()//原型方法<br /> { <br /> return this.name + this.age + 'studying';<br /> }<br /> } <p> var box1 = new Box();<br /> var box2 = new Box();<br /> alert(box1.constructor);//构造属性,可以获取构造函数本身,<br /> //作用是被原型指针定位,然后得到构造函数本身<br /> </script>  

__proto__属性和prototype属性的区别

prototype是function对象中专有的属性。
__proto__是普通对象的隐式属性,在new的时候,会指向prototype所指的对象;
__ptoto__实际上是某个实体对象的属性,而prototype则是属于构造函数的属性。__ptoto__只能在学习或调试的环境下使用。

原型模式的执行流程

1.先查找构造函数实例里的属性或方法,如果有,就立即返回。
2.如果构造函数的实例没有,就去它的原型对象里找,如果有,就立即返回

原型对象的

复制代码 代码如下:

<script type="text/javascript"><br /> function Box(){ //大写,代表构造函数<br /> Box.prototype.name = "trigkit4";//原型属性<br /> Box.prototype.age = "21";<br /> Box.prototype.run = function()//原型方法<br /> { <br /> return this.name + this.age + 'studying';<br /> }<br /> } <p> var box1 = new Box();<br /> alert(box1.name);//trigkit4,原型里的值<br /> box1.name = "Lee";<br /> alert(box1.name);//Lee,就进原则 <p> var box2 = new Box();<br /> alert(box2.name);//trigkit4,原型的值,没有被box1修改<br /> </script>

构造函数的

复制代码 代码如下:

<script type="text/javascript"><br /> function Box(){ <br /> this.name = "Bill";<br /> } <p> Box.prototype.name = "trigkit4";//原型属性<br /> Box.prototype.age = "21";<br /> Box.prototype.run = function()//原型方法<br /> { <br /> return this.name + this.age + 'studying';<br /> } <p> var box1 = new Box();<br /> alert(box1.name);//Bill,原型里的值<br /> box1.name = "Lee";<br /> alert(box1.name);//Lee,就进原则<br /> </script>

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

46

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

178

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

51

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

227

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

532

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

171

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号