完全弄懂js函数中的属性特点
for in 能够循环出对象中的所有可枚举属性,不论该属性是在原型上面还是本身所有
而Object.keys(),和JSON.stringify()只能找出对象本身的属性而且是要可以枚举的
function Test(){ this.age=1; }; Test.prototype.name="djl" var test=new Test(); for(var i in test){ console.log(i) } //age name console.log(JSON.stringify(test)) //{age:1} console.log(Object.keys(test)) //[age]
Object.hasOwnProperty 就是拿到自身的属性,不是通过原型继承来的,如果这个属性是不可枚举的,但是是对象自有的即没有定义在原型上,那么也会返回true,但是呢,propertyIsEnumerable会返回false,所以propertyIsEnumerable是hasOwnProperty的加强版,要返回true,就必须是自有属性而且是可以枚举的。
function Test(){ this.age=1; }; var test=new Test(); Object.defineProperty(test,"sex",{ enumerable:false }) console.log(test.propertyIsEnumerable("sex")) //false console.log(test.hasOwnProperty("sex")) //true
这个实例test 和 构造函数Test没有直接关系,但是有间接关系
function Test(){ } Test.prototype.name="DJL"; Test.prototype.age=22 var test=new Test();
即在不改变原型对象的情况下,构造函数的prototype属性所指向的原型对象和实例[[prototype]]指针(实例的内部属性)是同一个,当然前提是构造函数的prototype属性没有被更改。所以我们判断这种关系是否还存在或者说构造函数的prototype属性有没有被更改可以用下面这种方法
isPrototypeOf Test.prototype.isPrototypeOf(test) 返回的应该是true。当然ES6增加了一个方法:Object.getPrototypeOf(test) 用来获得实例的这个[[prototype]]
当我们有时在写Test.prototype={} 时,实际上已经切断了a这条线路,但是c这条线路还保留,所以有的代码会手动改写constructor属性
我们创建任何一个函数,该函数就会自动创建以下5个属性:["length", "name", "arguments", "caller", "prototype"],默认情况下这5个属性都是不可枚举的,这里插一句我们如何获得这些不可枚举的属性呢,很简单:Object.getOwnPropertyNames() 这个方法方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组。详见链接 。当然我们现在可以用Array.prototype.filter来实现获得不可枚举元素的素组:
var target = myObject; var enum_and_nonenum = Object.getOwnPropertyNames(target); var enum_only = Object.keys(target); var nonenum_only = enum_and_nonenum.filter(function(key) { var indexInEnum = enum_only.indexOf(key); if (indexInEnum == -1) { // not found in enum_only keys mean the key is non-enumerable, // so return true so we keep this in the filter return true; } else { return false; } }); console.log(nonenum_only);
在这些自动被创建的属性中有些是可以被更改的比如prototype,但是有些是不能的,但是我们如何知道呢
我们知道属性的类型分为数据属性和访问器属性,这里我们只讨论数据属性,数据属性有4个特征来表述一个属性分别是
[[Configurable]] :表示该属性是否能够通过delete删除(delete只能删除自有属性,不能删除继承属性),true表示可以,false不行,在严格模式下会报错,松散模式下会忽略这一操作什么也不做。直接定义在对象上的属性默认都是true
[[Enumerable]]:表示属性是否可以被枚举,一般直接定义在对象上的属性默认都是true
[[Writable]]:表示属性是否可以被更改 true表示可以,false不行,在严格模式下会报错,松散模式下会忽略这一操作什么也不做。直接定义在对象上的属性默认都是true
[[Value]]:表示属性的属性值,默认为undefined,现在你知道为什么打印定义但是未赋值的变量是undefined的原因了吧。
那么首先我们来看看这些固有的特殊属性的这些属性描述符是什么值呢!这里我们要通过Object.getOwnPropertyDescriptor(obj,name)这个方法来获取
function Test(){ } Object.getOwnPropertyNames(Test).forEach(function(value,index){ console.log("----------"+value+"--------------") var descriptor=Object.getOwnPropertyDescriptor(Test,value); console.log(descriptor.configurable+"---configurable") console.log(descriptor.writable+"----writable") console.log(descriptor.enumerable+"----enumerable") console.log(descriptor.value+"----value") })
返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性。详细链接
我们可以看到除了prototype可以修改以外,其他的都不能修改,当然所有的都不能枚举。name的值就是这个构造函数Test的名称。这也就是为什么prototype可以重写的原因。
今天为什么要写这些比较晦涩难懂而且又不经常用到的东西呢,主要是因为今天碰到了这样一段代码。
function Test(){ this.name="djl"; } Test.name="箫氏"; var test=new Test(); console.log(Test.name) //Test console.log(test.name) //djl
说实话当时看这段代码,给我一种简单但又不敢回答的感觉,果然最后答错了,为什么Test.name会为Test,构造函数Test和实例test究竟之间有什么联系,于是下来看了看高程,网上看了看博客,陆陆续续找出了这么多东西,总的来说还是挺有价值的。