在java,php等一些后端语言中,都有特定的方法获取到类中的所有属性和方法,但是在js中却没有这样的API,随着es的发展预计会在未来出现.但是目前我们也可以自己实现这样的需求,题目如下:
class One {
constructor() {
this.nameOne = 'One'
}
validateOne() {
console.log("one")
}
}
class Two extends One {
constructor() {
super()
this.nameTwo = 'Two'
}
validateTwo() {
console.log("two")
}
}
class Three extends Two {
constructor() {
super()
this.nameThree = 'three'
}
validateThree() {
console.log("three")
}
}
类Three继承类Two,而类Two又继承类One,现在实例化一个对象 ,let three = new Three(),如何输出three上面的所有自定义的属性和方法呢?
此时我们可以直接在控制台打印出three对象,看下该实例上面具体含有哪些属性:
从上面打印出的three对象可知,类Three继承类Two,而类Two又继承类One,此时实例化的three对象含有三个属性nameOne,nameTwo和nameThree.其中nameOne,nameTwo这两个属性全部在three对象上,并不在原型链上,但是validateOne,validateTwo,validateThree这三个自定义方法全部在原型链上面.
另外我们会想到使用in循环遍历对象three,希望能输出原型链上的方法名,伪代码如下:
for(let key in three){
console.log(key);
}
输出结果:'nameOne','nameTwo','nameThree';
in循环能够遍历到对象以及其原型链上面的所有可枚举属性,但是它并不能输出方法名,所以我们需要自定义一个工具函数来实现我们的需求,具体调用形式如下,函数名如下findProperties,第一个参数传入要查询的对象实例,后面的“name”和“validate”是我们想要输出的自定义属性和方法的前缀.
const data = findProperties(three, 'name', 'validate');
console.log(data)
findProperties实现如下:
function findProperties(obj,...arg){
function getProperty(new_obj){
if(new_obj.__proto__ === null){ //说明该对象已经是最顶层的对象
return [];
}
let properties = Object.getOwnPropertyNames(new_obj);
let arr = [];
arg.forEach((v)=>{
const newValue = properties.filter((property)=>{
return property.startsWith(v);
})
if(newValue.length>0){
arr = arr.concat(newValue);
}
})
return [...arr,...getProperty(new_obj.__proto__)];
}
return getProperty(obj);
}
解析:这里需要格外注意的便是 Object.getOwnPropertyNames() ,该方法返回对象的所有自身属性的属性名(包括不可枚举的属性)组成的数组,但不会获取原型链上的属性.每一次嵌套函数的执行都是获取该层对象上面的所有属性和方法,紧接着将该对象.__proto__作为参数继续传入嵌套函数作为参数,以继续获取该对象的父对象上面的所有属性和方法.
我们可以看一下上面这几行在控制台输入的测试代码,从这几行代码我们可得出一些结论,所有自定义对象的原型链的顶层都是Object的原型对象,即Object.prototype,而Object.prototype.__proto__则为null,我们可以利用此特征作为嵌套函数的中止条件.
完整代码如下:
class One {
constructor() {
this.nameOne = 'One'
}
validateOne() {
console.log("one")
}
}
class Two extends One {
constructor() {
super()
this.nameTwo = 'Two'
}
validateTwo() {
console.log("two")
}
}
class Three extends Two {
constructor() {
super()
this.nameThree = 'three'
}
validateThree() {
console.log("three")
}
}
function findProperties(obj,...arg){
function getProperty(new_obj){
if(new_obj.__proto__ === null){ //说明该对象已经是最顶层的对象
return [];
}
let properties = Object.getOwnPropertyNames(new_obj);
let arr = [];
arg.forEach((v)=>{
const newValue = properties.filter((property)=>{
return property.startsWith(v);
})
if(newValue.length>0){
arr = arr.concat(newValue);
}
})
return [...arr,...getProperty(new_obj.__proto__)];
}
return getProperty(obj);
}
let three = new Three()
const data = findProperties(three, 'name', 'validate');
console.log(data)
nodejs运行的效果如下: