当前位置: 首页 > news >正文

JavaScript学习总结——原型

什么是原型

首先,原型是一个对象。而且所有的对象都有一个原型(有一种例外:当把对象的原型设为null时),并且任何对象都可以成为一个原型。

当我们定义一个对象时 var a = new Object(); 默认的原型在原型链的顶端。

原型有什么好处

原型最大的好处体现在它的 共享 的特性。所有原型对象的实例对象共享它所包含的属性和方法。所以我们常用利用原型来创建对象,也就是 原型模式

原型模式

原型模式 是一种用来创建多个实例对象的方法,我们常常把它和 构造函数结合起来用来创建特定类型的对象。

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

// 构造函数
function Person(){};

Person.prototype.name = "darko";
Person.prototype.age = 21;
Person.prototype.sayName = function(){
    alert(this.name);
}

var person1 = new Person();
person1.sayName();  // "darko"

var person2 = new Person(); 
person2.sayName();  // "darko"

我们将所有的属性和sayName()方法添加到了构造函数Personprototype属性中,构造函数成了空函数。但是即便如此,我们也可以通过调用构造函数来创建新对象,而且新对象还会具有相同的属性和方法。

构造函数,实例对象和原型对象的关系

实例对象就是通过构造函数创造的,默认拥有一个constructor属性指向其构造函数。

原型对象就是构造函数的属性prototype指向的那个对象,同时也是基于构造函数生成的实例对象的原型对象。在默认情况下,所有的原型对象都会自动获得一个constructor属性,这个属性是一个指针,指向其构造函数。

实例对象可以访问原型对象上的属性和方法。在实例对象的内部有一个属性(内部属性)[[Prototype]]指向其原型对象。有一种非标准方法__proto__访问[[Prototype]]

在上面的例子中person1person2就是实例对象,构造函数为Person,原型对象为Person.prototype

来,看个栗子(还是上面那段代码):

alert(person1.constructor === Person);  // true

alert(Person.prototype.constructor === Person);  // true

alerta(person1.__proto__ === Person.prototype); // true

来看个图你就什么都懂了:
图片描述

理解prototype,getPrototypeOf和 proto 之间的不同

prototype是函数的一个默认属性,只有函数对象才有

Object.getPrototypeOf()方法用来返回实例对象内部属性[[prototype]]的值。这是ES5中定义的用来获取原型对象的标准方法。

__proto__属性是获取原型对象的非标准方法(IE不支持)
看个栗子(还是上面那段代码):

alert(Object.getPrototypeOf(person1) === Person.prototype); // true
alert(Object.getPrototypeOf(person1).name); // "darko"

alert(person1.__proto__ === Person.prototype);    // true
alert(person1.__proto__.name);  // "darko"

原型模式下的对象

每次查找对象的每个属性,都是一次搜索。搜索从实例对象本身开始,如果在实例对象中找到,停止查找,返回值。如果没有则继续搜索实例对象指向的原型对象。

若实例对象中属性和其指向的原型对象的属性重名,实例对象中的属性屏蔽原型对象中的那个属性。
举个栗子:

function Person(){};

Person.prototype.name = "darko";
Person.prototype.age = 21;
Person.prototype.sayName = function(){
    alert(this.name);
}

var person1 = new Person();
var person2 = new Person();

person1.name = "leon";
person1.sayName();   // "leon",来自实例
person2.sayName()   // "darko",来自原型

delete person1.name;
person1.sayName();  // "darko",来自原型

可以利用hasOwnProperty()方法判断一个属性是位于实例中,还是原型中。只有在属性来自实例中时,才会返回true。通常和in操作符配合使用。

// 接上
alert("name" in person1);   // true
alert(person1.hasOwnProperty("name"));  // false

原生对象的原型

所有的原生引用类型都在其原构造函数的原型上定义了方法,例如,Array.prototype.sort()方法,正是由于原型的共享特性,我们定义的数组才可以使用sort()方法等一系列的方法。
举个栗子:

var num = [1, 5, 3, 7, 9];
num.sort(); // 1,3,5,7,9
alert(num.constructor === Array);   // true
alert(num.__proto__ === Array.prototype);    // true
alert(num.__proto__.__proto__ === Object.prototype);    //true

数组对象num本身就是构造器Array的实例对象,而Arrayprototype属性指向的对象上定义了sort()方法,所以新定义了num对象经过搜索找到了sort()方法,并调用了方法。

原型的动态性

由于在原型中查找值的过程是一次搜索,所以对原型对象的任何修改都能立即从实例上反应出来。
举个栗子:

function Person(){};
var firend = new Person();
// 修改原型
Person.prototype.sayHi = function(){
    alert("Hi");
}

firend.sayHi(); // "Hi"

但是若将原型重写,来看看有什么不同:

function Person(){};
Person.prototype.name = "darko";
var firend = new Person();
// 重写了原型对象
Person.prototype = {
    constructor: Person,  // 注意:重写原型对象,所以此时的constructor属性变成了新对象的构造函数,默认为Object构造函数,应该将其设置回适当的值
    sayHi: function(){
        alert("Hi");
    }
}

alert(friend.name); // "darko"
firend.sayHi(); // error

这说明,重写原型对象切断了现有原型和任何之前已经存在的实例对象之间的联系,它们引用的仍是最初的原型。

如果你觉得我写的还可以,点一下推荐吧。

相关文章:

  • 2的幂在约瑟夫环问题的应用
  • 【烈日炎炎战后端】MySQL理论(2.8万字)
  • Mysql5.6主从复制
  • 【烈日炎炎战后端】MySQL编程(3.6万字)
  • 【Mongodb】Master-Slave 复制
  • 解决前端文件修改后浏览器页面未更新的问题
  • 【烈日炎炎战后端】Redis(6.1万字)
  • UIScrollView视差模糊效果
  • 真正的上锁前,为何要调用preempt_disable()来关闭抢占的case【转】
  • 【烈日炎炎战后端】Linux(0.3万字)
  • POJ3159 Candies(最短路径:SPFA+链表+栈)
  • 【烈日炎炎战后端】SpringMVC(0.5万字)
  • 【shell 脚本】两种登录方式
  • 【烈日炎炎战后端】Spring(2.1万字)
  • tcpdump统计http请求
  • [PHP内核探索]PHP中的哈希表
  • isset在php5.6-和php7.0+的一些差异
  • Java新版本的开发已正式进入轨道,版本号18.3
  • rc-form之最单纯情况
  • Shadow DOM 内部构造及如何构建独立组件
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • 诡异!React stopPropagation失灵
  • 聊聊flink的TableFactory
  • 目录与文件属性:编写ls
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 深度学习入门:10门免费线上课程推荐
  • 听说你叫Java(二)–Servlet请求
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #define 用法
  • #FPGA(基础知识)
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (SpringBoot)第二章:Spring创建和使用
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (规划)24届春招和25届暑假实习路线准备规划
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)h264中avc和flv数据的解析
  • (转)memcache、redis缓存
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • ./configure、make、make install 命令
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .describe() python_Python-Win32com-Excel
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .NET MVC第三章、三种传值方式
  • .net wcf memory gates checking failed
  • .net 设置默认首页
  • .Net8 Blazor 尝鲜
  • .NetCore 如何动态路由