文档章节

原型(prototype)相关方法,jquery类型判断源码分析。

Say_-no
 Say_-no
发布于 2017/09/01 17:21
字数 2118
阅读 7
收藏 0

前言

介绍原型的概念,和相关属性,以及jquery判断纯净对象的实现,不小心点进来的直接 ctrl+f 搜你想找的属性。

  1. 什么是原型
  2. isPrototypeOf() || Object.getPrototypeOf()
  3. hasOwnProperty() || in
  4. jQuery.isPlainObject() 源码解读

什么是原型

prototype(原型,雏形,蓝本) 说新上市的一部手机的原型机,就可以用这个单词。

每一个函数默认都有一个prototype(原型)属性,这个属性指向函数的原型对象。就是说函数的原型是一个对象。先来打印一个函数的这个prototype属性,来看看他是什么样的。

function Zoom(){};
var proto = Zoom.prototype;
console.log(proto);

眼见为实,这就是Zoom函数的原型对象,其中还有一个constructor 属性,我们并未对prototype原型对象进行修改,但却有一个constructor属性。默认情况下,函数的原型对象都会获取到一个constructor属性。

constructor(构造器)英文中的解释为构造器。图中constructor的属性值为Zoom函数。便于记忆,也可以理解为,函数的原型是由函数产生的,那构造出原型的东西,就是函数本身。也就是:

Zoom.prototype.constructor = Zoom;
//语言描述就是:zoom函数的原型对象的constructor属性,指向函数本身。

当我们通过new操作符,获取了一个构造函数的实例后(就是产生了一个对象)。先来看一个这样的对象:

function Zoom(){
  this.bird = function(){
    console.log('bird');
  }
};
//在函数的原型上扩展了一个方法
Zoom.prototype.cat = function(){
  console.log("cat");
}
var zoom = new Zoom();
console.log(zoom);

可以看到实例化的zoom对象下有一个__proto__属性,而这个属性就指向构造函数Zoom的原型对象。重点是,__proto__连接的是实例对象与构造函数原型对象,而不是,实例对象和构造函数。

isPrototypeOf() || getPrototypeOf()

function Zoom(){
  this.bird = function(){
    console.log('bird');
  }
};
//在函数的原型上扩展了一个方法
Zoom.prototype.cat = function(){
  console.log("cat");
}
Zoom.prototype.fish= function(){
  console.log("fish");
}
var zoom1 = new Zoom();
var zoom2 = new Zoom();
zoom1.cat ();//cat
zoom2.fish();//fish
console.log(zoom1);
console.log(zoom2);

根据上一节说的,zoom1,zoom2实例对象都有一个属性__proto__指向构造函数的原型对象,换句话说,就是实例对象和构造函数没什么直接的联系。

但是我们发现,这两个实例都不包含方法,却能够使用a,b 方法,这就是通过查找对象属性的过程来实现的。

当我们在调用一个对象的属性值是,首先会从实例对象本身开始搜索,如果找到了就返回属性值,没有找到就在原型对象上查找。这也是原型对象的属性或方法可以共享的原因。

那如何知道一个对象,例如两个实例的原型链中,是否有构造函数的原型对象(Zoom)的方法呢。这就是isPrototypeOf()。

用来确定一个对象是否存在于另一个对象的原型链中

console.log(Zoom.prototype.isPrototypeOf(zoom1));//true

即为,Zoom 的原型对象是否在zoom1(实例)的原型链上,或者说zoom1的原型链上是否有Zoom的原型对象。

通过这个方法仅仅是可以判断,如果想取得,在ES5中提供了一个Object.getPrototypeOf(),他是Object对象下的方法,传入你想取原型的对象。

Object.getPrototypeOf() 方法返回指定对象的原型(即, 内部[[Prototype]]属性的值)。

console.log(Object.getPrototypeOf(zoom1) === Zoom.prototype) //true

虽然原型可以共享,但是不能通过实例对象修改原型:

zom1.cat = function (){
  console.log('zom1 输出的 cat');
}
zom1.cat ();//z1 输出的 cat
zom2.cat ();//原型输出的cat

这个其实很好理解,因为对象属性查找是从实例向原型上查找,所以写在实例上的方法如果和原型上的方法同名的话,会屏蔽原型上的方法,可以简单理解为就近原则。

hasOwnProperty() || in

既然同一个方法可以出现在实例中,也可以出现在原型中,如何可以判断是否在实例中呢。

hasOwnProperty() 方法会返回一个布尔值,指示对象是否具有指定的属性作为自身(不继承)属性。

如果判断在zoom1对象自身是否有a属性,就可以:

zoom1.hasOwnProperty(bird); // true
zoom1.hasOwnProperty(fish); // false

因为bird 是zoom1 自身的属性,所以返回true,而fish是zoom1原型的的属性,所以返回false。

另一个要介绍的in方法,它比hasOwnProperty判断的范围更大,无论在原型上或者是在实例上,如果存在要检测的属性,都会返回true。

'bird' in zoom1; // true
'fish' in zoom1; // false

那就可以理解为,在in方法的判断范围中中排除hasOwnProperty的判断范围,剩下的不就是属性只出现在原型中的可能。

转为简单逻辑就是,如果in为真则可能在实例中也可能在原型中,如果hasOwnProperty方法为假,就肯定不是在实例中。所以in为真,hasOwnProperty为假,就一定是在原型中:

function hasProtoTypeProto(attr,obj){
   return !obj.hasOwnProperty(attr) && (attr in obj)
}
hasProtoTypeProto('fish',zoom1) //true
hasProtoTypeProto('bird',zoom1) //false

由上可以判断fish,是原型中的属性,而bird不是原型中的属性。

isPlainObject()

是jquery提供的外部可以直接使用的方法,这个方法是用来判断一个对象是否是纯粹的对象,即对象字面量,而不是一个构造函数的实例。其中涵盖了大部分原型相关的方法。我们先来看一下如何使用。

var log = console.log.bind(console);
log(jQuery.isPlainObject({x:0}));//true
log(jQuery.isPlainObject(new Object({})))//true
log(jQuery.isPlainObject([0]))//false
function Zoom(){
  this.fish = function(){
    return "Im a fish";
  }
}
var zoom = new Zoom();
log(jQuery.isPlainObject(zoom))//false

可以看到只有纯粹的对象才会返回真,typeof类型检测出数组为object,或是构造函数的实例都是返回假。我们看一看在jquery 3.1版本中是如何实现这个方法的。分析过程请见代码:

//传入需要判断的对象
function isPlainObject(obj) {
//没有具体的意义只是保存其他执行的结果。
var proto, Ctor;
if (!obj || toString.call(obj) !== "[object Object]") {
  // toString 在之前的代码中已被定义为 Object.prototype.toString,代码等价于Object.prototype.toString.call( obj ) !== "[object Object]"
  //在任何值上调用Object 原生的toString()方法,都会返回一个[object NativeConstructorName] 格式的字符串,
  //[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性,这个属性中就指定了上述字符串中的构造函数名(NativeConstructorName)
  // 类似的 [object Array] [object Function] [object RegExp]
  //但是这个判断不能排除一个实例对象,因为上[[Class]] 属性默认保存对象的类型,所以也会返回Object;
  //所以除了对象的其他类型,都会返回false
  return false;
}
proto = getProto(obj);
// getProto 之前已经被定义为 getProto = Object.getPrototypeOf 返回对象构造函数的原型
// Objects with no prototype (e.g., `Object.create( null )`) are plain
// 上一行是坐着的注释,即为通过Object.create( null ) 方法,可以创建一个没有原型的对象null
// 所以如果proto 为false,表示对象没有原型,只会是null,而null也是一个纯粹的对象,所以返回真
if (!proto) {
  return true;
}

Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
// Objects with prototype are plain iff they were constructed by a global Object function
// hasOwn之前被定义为,hasOwn = {}.hasOwnProperty 
// hasOwn.call( proto, "constructor" ) 传入对象构造函数的原型,判断原型上面有没有constructor属性(而不是在原型链的其他位置),因为constructor属性只会出现在Object函数的原型上,其他函数原型的constructor属性,都是从Object原型上继承来的
// 所以有constructor属性表示是通过Object对象得到的,但是还不能确定为是Object的实例,或是字面量方式得到。最后保存proto.constructor 即是传入对象的构造函数


return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
// fnToString被定义为hasOwn.toString 即是{}.hasOwnProperty.toString.call(Ctor);
// ObjectFunctionString定义为fnToString.call( Object ) 即是{}.hasOwnProperty.toString.call(Object);
// 所以 typeof Ctor = 'fuction" 为函数就是需要判断对象的原型是函数类型,{}.hasOwnProperty目的是得到一个函数,一个函数的toString方法会以字符串的格式显示函数
// 如果两个字符串相等,表示两个函数相等。也就表示传入对象的构造函数就是Object,所以他是一个纯净的对象。

}

至此就是全文的所有内容,有疑问尽情留言,一定回复。

© 著作权归作者所有

共有 人打赏支持
Say_-no
粉丝 1
博文 6
码字总数 9308
作品 0
昆明
从零实现一个简易的jQuery框架之二—核心思路详解

如何读源码 整体框架甚是复杂,也不易读懂。但是若想要在前端的路上走得更远、更好,研究分析前端的框架无疑是进阶路上必经之路。但是庞大的源码往往让我们不知道从何处开始下手。在很长的时...

余大彬
08/08
0
0
一个后端眼中的jQuery的extend方法

概述 我看的是3.1.0版本的,先上一段代码吧,不要版本都不一样那就尴尬了,这段代码看着没有多少,但我相信这基本上是这个世界上执行的最多的代码了,再不济也是一个之一。 直接看代码有一点...

trayvon
2017/10/20
0
0
通过jQuery源码学习javascript(一)

构造函数(构造器) 2. 扩展原型   javascript为所有函数绑定一个prototype属性,由这个属性指向一个原型对象。我们在原型对象中定义类的继承属性和方法等。 何为原型: 原型是js实现继承的...

stone_
2014/12/02
0
0
自定义jQuery插件Step by Step

自定义jQuery插件Step by Step 1.1.1 摘要 随着前端和后端技术的分离,各大互联网公司对于Mobile First理念都是趋之若鹜的,为了解决网页在不同移动设备上的显示效果,其中一个解决方案就是R...

wolf_su
2013/07/04
0
0
深入理解javascript系列(十八):掌握面向对象(1)

面向对象,一个老生常谈的话题,但你有没有想过面向对象要解决什么问题? 有一位大神说的很直接,”面向对象要解决的问题,并不是封装、继承和多态,而是写代码的套路“。 我觉得有理,所以简...

Panthon
06/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

linux 系统的运行级别

运行级别 运行级别 | 含义 0 关机 1 单用户模式,可以想象为windows 的安全模式,主要用于修复系统 2 不完全的命令模式,不含NFS服务 3 完全的命令行模式,就是标准的字符界面 4 系统保留 5 ...

Linux学习笔记
今天
2
0
学习设计模式——命令模式

任何模式的出现,都是为了解决一些特定的场景的耦合问题,以达到对修改封闭,对扩展开放的效果。命令模式也不例外: 命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。 解决了这...

江左煤郎
今天
3
0
字典树收集(非线程安全,后续做线程安全改进)

将500W个单词放进一个数据结构进行存储,然后进行快速比对,判断一个单词是不是这个500W单词之中的;来了一个单词前缀,给出500w个单词中有多少个单词是该前缀. 1、这个需求首先需要设计好数据结...

算法之名
昨天
14
0
GRASP设计模式

此文参考了这篇博客,建议读者阅读原文。 面向对象(Object-Oriented,OO)是当下软件开发的主流方法。在OO分析与设计中,我们首先从问题领域中抽象出领域模型,在领域模型中以适当的粒度归纳...

克虏伯
昨天
1
0
Coding and Paper Letter(四十)

资源整理。 1 Coding: 1.Tomislav Hengl撰写的非官方作者指南:Michael Gould•Wouter Gerritsma。 UnofficialGuide4Authors 2.R语言包rwrfhydro,社区贡献的工具箱,用于管理,分析和可视化...

胖胖雕
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部