当前位置: 首页 > 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请求
  • @angular/forms 源码解析之双向绑定
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • C++11: atomic 头文件
  • Git同步原始仓库到Fork仓库中
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • webpack项目中使用grunt监听文件变动自动打包编译
  • 悄悄地说一个bug
  • 如何在 Tornado 中实现 Middleware
  • 学习ES6 变量的解构赋值
  • No resource identifier found for attribute,RxJava之zip操作符
  • Python 之网络式编程
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​虚拟化系列介绍(十)
  • (独孤九剑)--文件系统
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (四)图像的%2线性拉伸
  • ..回顾17,展望18
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .Net Remoting(分离服务程序实现) - Part.3
  • .Net7 环境安装配置
  • .NET的数据绑定
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @Service注解让spring找到你的Service bean
  • @Transactional类内部访问失效原因详解
  • @vue/cli脚手架
  • [<事务专题>]
  • [2013AAA]On a fractional nonlinear hyperbolic equation arising from relative theory
  • [BT]小迪安全2023学习笔记(第15天:PHP开发-登录验证)
  • [bzoj1006]: [HNOI2008]神奇的国度(最大势算法)
  • [CISCN2019 华东北赛区]Web2
  • [codevs1288] 埃及分数
  • [dfs] 图案计数
  • [Enterprise Library]调用Enterprise Library时出现的错误事件之关闭办法
  • [javaSE] 看知乎学习工厂模式