介绍原型的概念,和相关属性,以及jquery判断纯净对象的实现,不当心点进来的直接 ctrl+f 搜你想找的属性。jquery
什么是原型数组
isPrototypeOf() || Object.getPrototypeOf()函数
hasOwnProperty() || inthis
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
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() 方法会返回一个布尔值,指示对象是否具备指定的属性做为自身(不继承)属性。
若是判断在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
是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,因此他是一个纯净的对象。 }
至此就是全文的全部内容,有疑问尽情留言,必定回复。