- 1. Object.getOwnPropertyNames()
在学习使用该方法的时候,查阅了《JavaScript高级程序设计》与 MDN 来综合学习。
先来看看MDN对其的表述:
参数
obj:
- 一个对象,其自身的可枚举和不可枚举属性的名称被返回。
返回值
在给定对象上找到的属性对应的字符串数组。
描述
Object.getOwnPropertyNames()
返回一个数组,该数组对元素是 obj
自身拥有的枚举或不可枚举属性名称字符串。 数组中枚举属性的顺序与通过 for...in
循环(或 Object.keys
)迭代该对象属性时一致。数组中不可枚举属性的顺序未定义。
同时,举出了几个例子来表显示,例如:
var arr = ["a", "b", "c"]; console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"] // 类数组对象 var obj = { 0: "a", 1: "b", 2: "c"}; console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"] // 使用Array.forEach输出属性名和属性值 Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) { console.log(val + " -> " + obj[val]); }); // 输出 // 0 -> a // 1 -> b // 2 -> c //不可枚举属性 var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; }, enumerable: false } }); my_obj.foo = 1; console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]
正如:MDN上所述,该方法返回的是对象自身的可枚举与不可枚举属性,即返回的是一个数组。并且在查阅《JavaScript高级程序设计》这本书籍的时候,却发现了一个问题。
function Person(){ }
Person.prototype.name = 'asfd'
Person.prototype.age = 15
Person.prototype.sayName = function () {
console.log(this.name)
}
var keys = Object.getOwnPropertyNames(Person.prototype) console.log(keys)
// 书籍中表述keys返回的是 ['constructor', 'name', 'age', 'sayName']
// 但是当将Person.property设置一个以对象字面量形式创建的对象时,就不再返回constructor. 书籍中已经解释此时的constructor不再指向Person // 于是,我真的操作了一遍才发现,真的如此
// 实例如下:
function Person(){ } Person.prototype = { name: 'zwj', age: 21, say: function () { console.log('say') } }
var keys = Object.getOwnPropertyNames(Person.property)
console.log(keys)
// 此时返回的 ['name','age','sayName']
我根据书中理解如下:设置一个以对象字面量形式创建的对象,虽然结果相同,但是constructor不再指向Person,这就涉及到原型链了
首先,每个构造函数被创建的时候,同时就创建了一个构造函数对应的原型对象,即: 构造函数.prototype
这个对象就会自动获得一个constructor属性,这个属性就是该对象指向构造函数的指针,你也可以试试输出: Person.prototype Person.prototype.constructor Person.prototype.constructor.prototype 等等,就会发现形成了一个闭环...
这里我们使用的语法去接收属性值,本质上默写了prototype对象,这时候,改写的对象默认的constructor指向为Object构造函数,毕竟函数也是对象,在JS中一切皆为对象...
所以如果我们通常为了在原型上进行大量的属性写入,就必须对这部分操作进行注意,需要在使用设置对象字面量方式之后手动为其添加constructor属性的指向,这样做的目的就是为了确保通过该属性能够访问到适当的值...
当然你会觉得这样设置并不是完美,毕竟对于constructor这个属性,他应该是不可枚举的,此时,却是可枚举的,这是就需要我们使用Object.defineProperty()进行相应的设置.相应的代码如下:
function Person(){ } // 此时重写了prototype对象 constructor指向Object Person.prototype = { name: 'Nick', age: 15, sayName: function(){ consolelog(this.name) } } // 对constructor进行设置修改 Object.defineProperty(Person.prototype, 'constructor', { enumerable: false, // 设置是否可枚举 false为不可枚举 value: Person // 设置值为Person }) //可以通过definePrototype进行更改数据属性的共有6个,其余4个分别为: // configurable ----------> 表示是否可以通过delete进行删除 默认为true // writable ----------> 表示是否可以修改属性的值 默认为true
// get ----------> 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined。
// set --------> 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。
总结: 在进行创建构造函数的时候,以前习惯只是将constructor直接写在重写的prototype对象中,但是却忽略了其中最重要的一条信息,那就是constructor属性本身是不可枚举的属性,还需要更深一层的操作去设置constructor.看问题不能只看表面,更应该注重于内在...