• 你不知道的JavaScript-对象篇


    前言

    已知:对于默认的取值操作来说,如果无法在对象本身找到需要的属性,就会继续访问对象的prototype链,如下例

    1. var anotherOnject ={
    2. a:2
    3. }
    4. var myObject = Object.create(anotherOnject)
    5. console.log(myObject.a); //2
    6. 复制代码

    稍后我会介绍object.create()的原理,现在只需要知道它会创建一个对象并把这个对象的prototype关联到指定对象上

    也就是说现在myObject对象的prototype关联到了anotherObject。显然myObject.a并不存在,但尽管如此,属性访问仍然成功地找到了a的值2。如果还没有找到,那就一直在prototype链上持续查找下去,尽管尽头是undefined。

    for...in

    使用for...in遍历对象时的原理和查找prototype链基本类似,任何可以通过原型链访问到的属性都会被枚举出来。使用in操作符来检查属性在对象中是否存在,同样会查找对象的整条原型链(无论是否可枚举)

    1. var anotherObject ={
    2. a:2
    3. }
    4. //创建一个关联到anotherObject的对象
    5. var myObject = Object.create(anotherObject)
    6. for (const k in myObject) {
    7. console.log(k,"k"); //a
    8. }
    9. console.log("a" in myObject); //true
    10. 复制代码

    下面来理解一下prototype

    原型

    我们来验证一下:

    1. function Foo(){
    2. }
    3. var a = new Foo()
    4. Object.getPrototypeOf(a) = Foo.prototype //true
    5. 复制代码

    最直接的解释就是,a这个对象是在调用new Foo()时创建的,其中的会给a一个內部的prototype链接,关联到Foo.prototype指向的那个对象。

    即new Foo()会产生一个新对象a,这个新对象的内部链接关联的是Foo.prototype对象。

    实际上,new Foo()这个函数调用并没有直接的创建关联,这个关联只是意外的副作用,new Foo()只是间接的完成了我们的目标(一个对象关联到其他对象的新对象,也就是关联Foo.prototype的对象a)

    那么有没有更直接一点的呢?当然,功臣就是Object.create(),顺便说一下原型继承。

    1. function Foo(){
    2. this.name=name
    3. }
    4. Foo.prototype.myName = function(){
    5. return this.name
    6. }
    7. function Bar(name,label){
    8. Foo.call(this,name)
    9. this.label= label
    10. }
    11. Bar.prototype=Object.create(Foo.prototype)
    12. Bar.prototype.myLabel = function(){
    13. return this.label
    14. }
    15. var a = new Bar("a","obj a")
    16. a.myName()
    17. a.myLabel()
    18. 复制代码

    以上为典型的原型继承风格

    这段代码核心部分就是Bar.prototype=Object.create(Foo.prototype)。调用object.create()会凭空 创建一个“新对象” 并把新对象内部的prototype关联到你指定的Foo.prototype中(本例子是如此)

    换句话说,这代码的意思是,创建一个新的Bar.prototype对象并把它关联到Foo.prototype

    ES6开始之后可以直接修改现有的Bar.prototype了

    1. Object.setPrototypeOf(Bar.prototype,Foo.prototype)
    2. 复制代码

    检查类关系

    思考下面的代码:

    1. function Foo(){
    2. }
    3. Foo.prototype.blah=...;
    4. var a = new Foo()
    5. 复制代码

    我们如何通过内省找出a的祖先是Foo呢?通过instanceof

    1. a instanceof Foo //true
    2. 复制代码

    Instanceof操作符的左操作数是一个普通的对象,右操作数是一个函数。

    instanceof回答的问题是:在a的整条prototype链中是否有指向Foo.prototype的对象?

    下面是第二种判断prototype反射的方法,它更加简洁:

    1. Foo.prototype.isPrototypeOf(a) //true
    2. 复制代码

    同样也是提问Foo.prototype是否出现在a的prototype中? 是的。

    我们 也可以直接获取一个对象的prototype链:

    1. Object.getPrototypeOf(a)
    2. 复制代码

    获取到原型链后也会与Foo.prototype确定一下是否存在。

    1. Object.getPrototypeOf(a) === Foo.prototype //true
    2. 复制代码

    最后一种,直接使用原型链:

    1. a.__proto__ = Foo.prototype //true
    2. 复制代码

    关于__proto__的实现大致是这样的:

    1. Object.defineProperty(Object.prototype,"__proto__",{
    2. get:function(){
    3. return Object.getPrototypeOf(this)
    4. },
    5. set:function(o){
    6. Object.setPrototypeOf(this,o)
    7. return o;
    8. }
    9. })
    10. 复制代码

    在object.prototype中设置了key: __ proto __ ,value 为 {get: ,set: } 的键值对。

    因此,访问a.__ proto__时,实际上是调用了get函数由于是隐式绑定了this,所以this指向a,所以和Object.getPrototypeOf(a)的结果相同。

    _ proto _是可设置属性,之前的代码中使用ES6中object.setPrototypeOf()进行设置。

    JavaScript对于双斜线有一个非官方的称呼,叫它笨蛋proto

    现在我们知道了prototype机制就是存在于对象中的一个内部链接,它会引用其他对象。通常来说,这个链接的作用是,如果没有找到需要的属性或者方法引用,引擎就会在继续prototype关联的对象上进行查找,以此类推,这一系列对象的链接被称为原型链

    object.create

    它的原理:创建一个新对象,把它关联到我们指定的对象上去。

    1. object.create = function(o){
    2. function F(){} //创建新对象
    3. F.prototype = o //与o对象关联
    4. return new F()
    5. }
    6. 复制代码

    由于Object.create可以被模拟,所以这个应用非常广泛,出于完整性的考虑,还是举个例子更深刻的理解一下:

    1. var anotherObject={
    2. a:2
    3. }
    4. var myObject = Object.create(anotherObject,{
    5. b:{
    6. enumerable:false,
    7. writable:true,
    8. configurable:false,
    9. value:3
    10. },
    11. c:{
    12. enumerable:false,
    13. writable:true,
    14. configurable:false,
    15. value:4
    16. }
    17. })
    18. 复制代码

    使用create将myObject关联到anotherObject上去。

    1. myObject.hasOwnProperty("a") //false
    2. myObject.hasOwnProperty("b") //true
    3. myObject.hasOwnProperty("c") //true
    4. 复制代码

    观察到第一个为什么是false呢?因为它只会分析myObject表面是否有这个对象(如果想更深一步判断的话可以用 “a” in myObject;它可以遍历到原型链中查找是否有a的存在,它会返回true。)

    但仍然不影响从myObject上的三个a,b,c属性,因为即使没有在myObject本身上找到,也会去它的上一层原型链中查找是否存在该属性的,直至原型链尽头。

    myObject.a; myObject.b; myObject.c这三个变量分别都能找到是2,3,4.

    由于a这个变量是去原型链中的anotherObject找到的,这种myObject上本身不存在a变量但是也可以正常工作获取到a变量的场景,从內部来说,我们的实现遵循的就是委托设计模式。

  • 相关阅读:
    websocket使用案例(前后端springboot+vue)
    TS第一讲-----基础类型
    Day33力扣打卡
    Mybatis 常见面试题
    14_墨菲定律书摘
    设计模式学习(十六):责任链模式
    卷王必备学习的MyBatis-Plus用法,不来瞧瞧吗~~
    【华为机试真题 JAVA】最长子字符串的长度-100
    喜报|Authing 入选 CNCF Landscape 云原生技术图谱
    设计模式—结构型模式之代理模式
  • 原文地址:https://blog.csdn.net/m0_68036862/article/details/127752937