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

前言

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

  1. 什么是原型数组

  2. isPrototypeOf() || Object.getPrototypeOf()函数

  3. hasOwnProperty() || inthis

  4. jQuery.isPlainObject() 源码解读spa

什么是原型

prototype(原型,雏形,蓝本) 说新上市的一部手机的原型机,就能够用这个单词。
每个函数默认都有一个prototype(原型)属性,这个属性指向函数的原型对象。就是说函数的原型是一个对象。先来打印一个函数的这个prototype属性,来看看他是什么样的。prototype

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

图片描述

眼见为实,这就是Zoom函数的原型对象,其中还有一个constructor 属性,咱们并未对prototype原型对象进行修改,但却有一个constructor属性。默认状况下,函数的原型对象都会获取到一个constructor属性。
constructor(构造器)英文中的解释为构造器。图中constructor的属性值为Zoom函数。便于记忆,也能够理解为,函数的原型是由函数产生的,那构造出原型的东西,就是函数自己。也就是:code

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__链接的是实例对象与构造函数原型对象,而不是,实例对象和构造函数。blog

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

虽然原型能够共享,可是不能经过实例对象修改原型:

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

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,因此他是一个纯净的对象。

}

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

相关文章
相关标签/搜索