Javascript高级编程学习笔记(21)—— 对象原型
JS中对象相关的最重要的恐怕就是原型链了
原型链也是JS中对象继承的实现的基础
接昨天的文章,我们使用构造函数创建对象的时候仍然存在一些问题
那就是所有的实例没法共用一个函数
这样无疑会造成极大的内存浪费
那么解决的办法是什么呢?
那就是通过对象的原型来实现
原型链
在JS中我们创建的每一个函数都有一个 prototype(原型)属性
该属性是一个指针指向一个对象,这个对象的用途是包含由特定类型所有实例的共享属性和方法
这个对象也就是我们常说的原型对象
原型链就是对象实例和对象的原型对象组成的继承链
而JS中所有的对象都继承自Object类,所以原型链的顶端也就是Object构造函数的prototype 指向的对象
可以参考下图,可能画的有点丑请多见谅

对于这样一个继承链,在其它的面向对象的语言中,最下面的有关对象的部分应该是公有的,和类的概念十分类似
而不太好理解的,就是有关构造函数的那一部分
造成JS继承灵活多变的地方就在于这里,JS中的函数也是一个对象
所以函数也有自己的原型链,为了让构造函数和被构造函数创建的实例之间联系起来,所以函数除了有自己的 [[Prototype]] 属性之外,还有prototype属性指向被其构造的对象实例的原型对象
与此同时被函数prototype属性指向的对象,其constructor属性也会指向这个函数,这样就使构造函数和对象紧密地结合在一起
PS. 上文中的 [[Prototype]] 属性,就是对象的__proto__属性,但是这一属性只在浏览器中实现,JS的其他实现,对象不一定会有 __proto__属性
在这些没有__proto__ 的实现中虽然无法访问,但是可以通过 isPrototypeOf() 来检测对象之间的这种联系
而在ES5以上的脚本实现中,还支持一个Object.getPrototypeOf() 用于获取一个对象的原型对象
原型模式
在此基础上,我们可以发现,一个构造函数构造的所有函数的实例都有办法能够访问到原型对象
那么一类对象的共享属性的存放位置就显而易见了,那就是将其存放到原型对象上
以昨天的例子来说,使用原型模式创建对象如下:
function Person(){}
var pPrototype = Person.prototype;
pPrototype.name = 'lhy';
pPrototype.job = 'font end';
pPrototype.age = 21;
pPrototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
能够访问的原因在于,当代码读取对象的属性的时候 ,都会执行一次搜索,该搜索过程会从该对象实例开始
如果没有找到指定属性,则顺着原型链进行查找,直到Object.prototype为止
找到相应属性则返回,没找到则报未定义的错误
PS. 虽然我们可以通过实例对象访问原型对象的属性,但是我们无法通过实例对象来重写原型对象的属性
什么意思呢?就是说虽然我们可以通过实例对象的属性访问原型对象的某个属性,但是我们对实例对象对应属性的修改不会映射到原型对象上
如果我们在实例对象上设置了,一个本来只有原型对象上才有的属性,那么JS会为实例对象创建一个同名属性,并设置它的值
然后原型对象上的属性就被同名的属性屏蔽了,再也无法访问到原型对象上的这个属性,除非使用delete删除实例上的该属性
例如:

为了能够判断一个属性是来自于该实例,还是其原型对象
可以使用 hasOwnProperty() 方法来进行判断,对于来自实例的属性返回 true
Tips. in操作符不在for-in循环中单独使用的时候,用于判断对象上是否存在该属性,上面的方法与in操作符配合就可以判断属性到底来自实例,还是原型
function test(propertyName,obj){
if(obj.hasOwnProperty(propertyName)){
alert('来自实例');
)else if(propertyName in obj){
alert('来自原型');
}
}
Ps. for-in 循环会遍历对象上的所有可枚举属性,包括来自原型的属性,需要注意的是,如果原型上有个不可枚举的属性,而实例上有个与之同名的可枚举属性,那么实例上的这个属性会被遍历出来,不会受原型的影响
除此而外JS提供了Object.keys() 方法,可以获取到对象所有可枚举属性的集合
原型模式还有另一种简化写法
function Person (){}
Person.prototype = {name:"lhy",age:"21"};
但是这种写法存在一个问题,由于这里直接将一个对象指定为函数的原型对象
所以这里的函数原型的 constructor 属性也被改变了,这样我们就没法通过 constructor 来判断函数类型
那怎么办呢?
办法还是有的,那就是手动将要指定的原型对象的 constructor 设置为构造函数
不过要注意的是如果手动设置,那么该属性会被for-in 遍历出来,因为这种方式的就是为实例 创建一个 constructor 属性,以此来屏蔽不正确的constructor属性
所以最后还要将 constructor 属性特性描述符的 Enumerable 设为false,才能完美地解决
原型的动态性
从之前举的例子,和刚才简化的写法,我们不难发现原型是一个动态地对象
因为我们对原型对象的修改都会立即体现在实例对象上
同样的我们在操作原型时就要更加地小心,尤其是在重写原型对象时
拿上方的例子来说,虽然我们通过一些操作尽量减少了重写原型对象带来的差异,但还有一些是没法消除的
那就是我们重写原型对象时,会切断已有实例和重写的原型对象的实例,因为它们仍旧指向旧的原型对象
讲了这么多,那么原型模式是我们创建一类对象的最好的选择嘛?
虽然对于函数来说十分地合适,但对于一些引用类型的值可能就会有一些问题
比如我希望对象拥有自己独立的属性,那么原型模式可能就无法胜任了
那么怎么办呢?
构造函数与原型的组合模式
构造函数的缺点是没法共用数据,原型模式的缺点是没法有独立数据
那将他们组合起来不就行了吗
所以这种组合模式就产生了
function Person(name, job, age){
this.name = name;
this.age = age;
this.job = job;
}
Person.prototype.sayName = function(){
alert(this.name);
}
这种模式基本上就能满足我们平时的所有需求,所以这也是我们广泛使用的方式
动态原型模式
上面的方法虽然从功能上说,无可挑剔,但是暴露在外的独立的原型属性的声明,让追求极致的程序员们十分难受
所以动态原型模式就出现了
function Person(name, job, age){
this.name = name;
this.age = age;
this.job = job;
if(this.sayName !== 'function'){
Person.prototype.sayName = function(){
alert(this.name);
}
}
}
寄生构造函数模式
虽然动态原型模式,已经能够完美地处理绝大部分情况,但是有些特殊情况,仍会存在问题
就像我们不建议给原生的对象的原型添加属性,但有时候我们确实需要根据某个原生的对象来创建一类对象,那么怎么办呢?
所以就有了寄生构造函数模式,其原理就是我不在原生的原型上添加,但我可以使用工厂方法,对其构造的原型对象进行加工,从而得到我想要的对象
function specialArray(){
var values = new Array();
values.push.apply(values,arguments);
values.myName = function(){
return 'lhy';
}
return values;
}
这种只是改良了的工厂方法,存在的弊端和工厂方法几乎一致,无法确定对象类型等
所以使用场景很少
稳妥构造函数
我们知道在一些安全环境下,我们没法使用this、new等来创建,或者需要防止数据被其他应用程序篡改时使用
pS. 不使用new 是指不用new调用构造函数
function Person(name, job, age ){
var o = new Object();
o.sayName = function(){
alert(name);
}
return o;
}
这样创建对象时传入的原始数据,只能通过返回的对象o 上面的方法进行访问,所以保证了原始数据的安全性
总的来说,创建一类函数使用组合模式的场景更为广泛,使用频率也更高
Javascript高级编程学习笔记(21)—— 对象原型的更多相关文章
- Javascript高级编程学习笔记(20)—— 创建对象
由于今天有点事,加上对象原型链的东西有点多,所以今天这篇就讲一个小的知识点吧 也算为明天的对象继承做铺垫 工厂模式 虽然使用对象字面量来创建一个对象十分地便捷,但是这个方法有一个显著的缺点 那就是如果 ...
- Javascript高级编程学习笔记(22)—— 对象继承
继承是所有面向对象的语言最让人津津乐道的概念 许多面向对象的语言都支持两种实现继承的方式: 1.接口继承 2.实现继承 由于ECMAScript中没有函数签名,所以自然也是不支持接口继承 所以JS中能 ...
- Javascript高级编程学习笔记(18)—— 引用类型(7)单体内置对象
什么是内置对象呢? js高级程序设计中给出的定义为:由ES规定不依赖于宿主环境的对象,这些对象在JS执行前就已经存在 前面我们介绍的引用类型都是内置对象 除了这些对象外ECMA还规定了两个单体内置对象 ...
- Javascript高级编程学习笔记(59)—— 事件(3)事件对象
事件对象 在触发DOM‘事件时,会产生一个事件对象 event 该对象包含着所有与事件有关的信息 所有浏览器都支持 event 对象但是支持的方式有所不同 DOM事件对象 兼容DOM的浏览器会将eve ...
- Javascript高级编程学习笔记(31)—— BOM(5)screen、history对象
screen对象 screen对象应该是BOM对象中最不常用的对象了 其主要用于提供客户端的显示能力信息 包括浏览器外部显示的信息,和像素的宽高等 这个对象的主要用于检测客户端能力,一般不会影响功能 ...
- Javascript高级编程学习笔记(30)—— BOM(4)navigator对象
window对象作为浏览器的全局对象.location对象保存了页面的url信息 那么navigator对象又有什么作用呢? navigator对象 该对象最早由 Netspace Navigator ...
- Javascript高级编程学习笔记(29)—— BOM(3)location对象
在JS中location是一个神奇的对象 它既是window对象的属性,也是document对象的属性 它的作用主要在于保存当前文档页面的信息,以及将 url 解析为独立的片段 location对象属 ...
- Javascript高级编程学习笔记(28)—— BOM(2)window对象2
今天讲一下window对象和浏览器导航,弹窗等有关的内容 导航和打开窗口 window.open() 用于导航到某个特定 url 该方法接收四个参数 1.url 2.窗口目标(当页面中有多个框架fra ...
- Javascript高级编程学习笔记(27)—— BOM(1)window对象1
ECMAScript是JS的核心 但是对于在浏览器中运行的JS,BOM显然才是真正的核心 我们知道JS是由三个部分组成的 BOM.DOM.ECMAScript 之前的文章我们主要介绍的是ECMAScr ...
随机推荐
- ERC20 token standard issues.
- vue 打包后本地先自己启动服务 anywhere 非常好用
:)nodejs服务器Anywhere Anywhere是一个随启随用的静态服务器,它可以随时随地将你的当前目录变成一个静态文件服务器的根目录. 一,安装node 在nodejs官网下载,安装后打开c ...
- VBA XML信息提取
Option Explicit Sub 读取XML节点() '后期绑定 'Dim xdoc As Object 'Set xdoc = CreateObject("MSXML2.DOMDoc ...
- [转] spring framework体系结构及内部各模块jar之间的maven依赖关系
很多人都在用spring开发java项目,但是配置maven依赖的时候并不能明确要配置哪些spring的jar,经常是胡乱添加一堆,编译或运行报错就继续配置jar依赖,导致spring依赖混乱,甚至下 ...
- Oracle使用触发器和mysql中使用触发器的比较
一.触发器 1.触发器在数据库里以独立的对象存储, 2.触发器不需要调用,它由一个事件来触发运行 3.触发器不能接收参数 --触发器的应用 举个例子:校内网.开心网.facebook,当你发一个日志, ...
- Oracle 12c 创建新的数据库实例、用户
前提:安装好了Oracle 12c数据库,已有一个数据库实例xe,登录用户/密码:system/oralce 我用的是docker安装的Oracle 12c的实例: docker run --name ...
- 加密流量分析cisco
思科ETA主页 https://www.cisco.com/c/en/us/solutions/enterprise-networks/enterprise-network-security/eta. ...
- 【aardio】]SQL创建、读写 excel
import access; var db,err = access( "/test.xls" ) //文件不存在可自动创建 //创建表 if( ! db.existsTable( ...
- LR 11 代理录制步骤
1.新一个HTTP录制,到下图步骤 按下图说明设置信息,点击update 选择rl里面的代理exe(wplus_init_wsock.exe),具体路径为下图 打开浏览器设置代理(Internet选项 ...
- Markdown 尝试
目录 简介 参数模型 vs. 非参数模型 创新点 at the modeling level at the training procedure 模型结构 attention kernel Full ...