原型链的树形结构

  总所周知,js有七种数据类型,其中一种引用类型Object,其他六种基本类型:Number、String、null、undefined、symbol、Boolean。

在讨论原型的时候,我们讨论的还是这个引用类型Object。

  我们先看一下这段代码

var a = 20;
var b = 'abc';
var c = true;
var d = { m: 20 }

  通过这段代码我们可以看一看JS的栈内存和堆内存

JS栈内存与堆内存

 

  对于引用类型的变量d,我们可以在栈中记录的是其堆中的引用地址。我们可以看到 d 的属性以及值存储在堆中,我们可以通过 d.m 获取20这个值,但我们调用 d.toSting() 时我们可以发现,这个方法是存在的。

var d = { m: 20 }
console.log(d.toString())  // '[object Object]'

  为什么有toString 属性

  根据执行结果可以知道toSting是一个函数,也就是属于对象,属于引用类型。

  可以确定,toString 属于引用类型,而对于我们生成的引用变量d来说,它有一种特殊的方式,通过这一方式可以访问toString。同样的这一方式还可以访问 valueOf、isPrototypeOf等方法。

  而每个生成的一个新的对象,我们知道,它们都具有这么一个方式,可以以这个方式访问我们未曾定义的方法集合。

  在我们先将这个方式称之为 ‘共’  ,如图所示。

  我们定义一个对象obj,在chrome浏览器里

  var obj = {
    name:'he',
  }
  console.dir(obj)

  我们可以看到打印出来的结果

  1. obj 本身有一个属性 name(这是我们给它加的)
  2. obj 还有一个属性叫做 __proto__(它是一个对象)
  3. obj.__proto__ 有很多属性,包括 valueOf、toString、constructor、hasOwnProperty 等

  也就是说这个的属性名的真正名字就是 __proto__ 也就是我们平时说的原型。
  而obj.__proto__ 指向的集合,也就称之为 Object.prototype

  现在回到我们的问题:obj 为什么会拥有 valueOf / toString / constructor等属性?

  答案:这跟 __proto__ 有关。

  对于 obj 来说,本身不具有toString / valueOf 方法,通过 __prototype__ 以后可以 以 obj.toString 进行访问

  当我们读取obj.toString 时,JS引擎是这样的

  1. 查询 obj 本身是否有toString属性,没有就到下一步
  2. 查询obj.__proto__ 上是否有toString属性,没有就到下一步
  3. 查询obj.__proto__.__proto__ (此时为null)上是否有toString属性,没有就继续查找

     对于obj来说,在obj.__proto__ 找到toSting,所以调用 obj.__proto.toString 方法
  而在这一过程中,也就是 [读取属性] 的过程中,就像摸着链子一步一步寻找目的地一样。

  这条链子,我们就称之为原型链

  

  当然,这只是对 new Object() 生成的对象,当我们把整个原型链可以放置到所有的数据结构时, 每个不同类型的对象也同样会拥有他们不同的集合。

  所以,数组有一个 Array.prototype、函数有一个Function.prototype、Number类型对象有一个Number.prototype 。。。

  JS一切皆对象,其实Array.prototype 、Function.prototype ... 都属于 Object类型。

var arr = [1,2,3]
console.log(arr.toString())   // 1,2,3

  我们可以画图

  注:原型链的部分树形结构

  • [p] 表示的是 __proto__
  •  console.log方法,Array.prototype 打印出来为数组,Function.prototype无法打印结果
  • 建议使用console.dir()打印,以便查看结果
  • 。。。 表示包括String、Symbol、Date、Number、Boolean、Error....

  总结:

  1. JS中为了方便区分,生成了许多如Array、Function、String...不同的数据类型
  2. 每个数据类型的通用属性集合被放置在一个对象中,用户定义的变量根据其数据类型可以获取不同的方法,同时其顶端都属于Object.prototype

  

  延伸:obj 的由 Object所构造,但Array,Function又是由谁构造出来的呢。也就是说构造函数是什么情况呢?

  参考资料:JavaScript万物诞生记

  这篇文章通过比较诙谐的方式,以现有的证据来说明了JS不同数据类型是怎么出现的

 

转载于:https://www.cnblogs.com/h246802/p/9794264.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用递归函数遍历整个树形结构,对每个节点进行属性名修改。具体实现可以参考下面的代码: ```javascript function renameProp(node, oldProp, newProp) { if (node.hasOwnProperty(oldProp)) { Object.defineProperty(node, newProp, Object.getOwnPropertyDescriptor(node, oldProp)); delete node[oldProp]; } for (let key in node) { if (typeof node[key] === 'object' && node[key] !== null) { renameProp(node[key], oldProp, newProp); } } } // 示例数据 const data = { name: 'root', children: [ { name: 'child1', children: [ { name: 'grandchild1' }, { name: 'grandchild2' } ] }, { name: 'child2', children: [ { name: 'grandchild3' }, { name: 'grandchild4' } ] } ] }; // 修改属性名 renameProp(data, 'name', 'label'); // 输出修改后的数据 console.log(data); ``` 在上面的代码中,`renameProp` 函数接受三个参数:`node` 表示当前节点,`oldProp` 表示要修改的属性名,`newProp` 表示修改后的属性名。首先,函数会判断当前节点是否包含要修改的属性名,如果包含,则使用 `Object.defineProperty` 方法将属性名修改为新的属性名,然后删除旧的属性名。接下来,函数会遍历当前节点的所有子节点,如果子节点是对象,则递归调用 `renameProp` 函数进行属性名修改。 需要注意的是,上面的代码只会修改对象自身的属性名,不会修改原型链上的属性名。如果需要修改原型链上的属性名,可以使用 `Object.getOwnPropertyNames` 和 `Object.getPrototypeOf` 方法递归遍历原型链。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值