• 前端基础(三十六):你不知道的JavaScript - 原型


    [[Prototype]]

    定义一个对象,当访问对象内属性时,可以找到就可以相当于调用的是对象[[GET]]方法,找到属性对应的值,当找不到属性时,这个时候就会去寻找[[Prototype]]链上的属性了,如果还是找不到那就会继续在链上找[[Prototype]],从而继续查找,直到查找完整个链,最终如果还是未曾找到,那么就会返回undefined。如:

    function Person(){
        this.name = 'Lee';
    }
    Person.prototype.age = 18;
    
    var person = new Person();
    
    console.log(person); // Person { name: "Lee", [[Prototype]]: { age: 18 } }
    
    console.log(person.name);   // Lee
    console.log(person.age);    // 18
    console.log(person.sex);    // undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Object.prototype

    内置[[Prototype]]链最终都会指向Object.prototype,它包含了js中许多通用的功能(如:toString、hasOwnProperty等)
    在这里插入图片描述

    属性设置和屏蔽

    • 属性设置并不是在原型上进行修改或设置的,而是在对象上进行设置的(属性的默认参数下)
      function Person(){}
      Person.prototype.name = 'Tom';
      
      var person = new Person();
      
      console.log(person.name); // Tom
      
      person.name = 'Lee';
      
      console.log(person); // Person { name: "Lee", [[Prototype]]: { name: "Tom" } }
      console.log(person.name); // Lee
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 定义的name属性屏蔽了原型上的name属性
      function Person(){
          this.name = 'Lee';
      }
      Person.prototype.name = 'Tom';
      
      var person = new Person();
      
      console.log(person.name); // Lee
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    针对person.name = 'Lee';的三种情况

    • 属性设置时,当原型上的属性writable值为truefalse
      1. 【1】writable = true
        function Person(){}
        Object.defineProperty(Person.prototype, 'name', {
            value: 'Tom',
            writable: true
        })
        var person = new Person();
        person.name = 'Lee';
        console.log(person); // Person { name: "Lee", [[Prototype]]: { name: "Tom" } }
        console.log(person.name); // Lee
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
      2. 【2】writable = false
        function Person(){}
        Object.defineProperty(Person.prototype, 'name', {
            value: 'Tom',
            writable: false
        })
        var person = new Person();
        person.name = 'Lee';
        console.log(person); // Person { [[Prototype]]: { name: "Tom" } }
        console.log(person.name); // Tom
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
    1. 【3】属性设置时,当原型上的属性配置了setter属性时
      function Person() { }
      var _name = 'Tom';
      Object.defineProperty(Person.prototype, 'name', {
          get: () => _name,
          set: value => _name = value
      })
      var person = new Person();
      console.log(person.name); // Tom
      person.name = 'Lee';
      console.log(person); // Person { name: "Lee", [[Prototype]]: { name: "Lee" } }
      console.log(person.name); // Lee
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 产生隐式屏蔽的另一种写法
      在这里插入图片描述

    “类”

    "类"函数

    调用 new Person() 时会创建 person,其中一部就是给person一个内部的[[Prototype]]链接,关联到Person.prototype指向的那个对象。

    function Person() { this.name = 'Lee'; }
    var person = new Person();
    console.log(Person.prototype); // { constructor: ƒ Person() }
    console.log(Object.getPrototypeOf(person)); // { constructor: ƒ Person() }
    console.log(Object.getPrototypeOf(person) === Person.prototype); // true
    
    • 1
    • 2
    • 3
    • 4
    • 5

    一个类可以实例化多个对象,但多个对象的[[Prototype]]关联的实际上是同一个对象,因此这些对象并不是完全失去联系的,他们是相互关联着的。

    function Person() { this.name = 'Lee'; }
    var lee = new Person();
    var tom = new Person();
    console.log(Object.getPrototypeOf(lee) === Object.getPrototypeOf(tom)); // true
    
    • 1
    • 2
    • 3
    • 4
    • 关于名称
      • 关联对象(“类”)—> 这个机制被称为原型继承
        function Foo(){ this.name = "Foo"; }
        function Bar(){ this.age = 18; }
        Bar.prototype = Foo; // Bar 继承 Foo
        var foo = new Foo();
        var bar = new Bar();
        console.log(bar.name); // Foo
        console.log(Object.getPrototypeOf(bar)); // ƒ Foo(){ this.name = "Foo"; }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
      • 这种继承机制实际上并不是复制操作,而是在两个对象间创建的关联操作,这样就可以让一个对象委托访问另一个对象的的属性和函数。

    “构造函数”

    我们认为Person是一个类的原因是因为我们使用了关键词new,实际上我们的构造函数指向的是原型属性.constructor上的值。如下示例:

    function Foo() { }
    console.log(Foo.prototype.constructor); // ƒ Foo() { }
    var foo = new Foo();
    console.log(foo.constructor); // ƒ Foo() { }
    console.log(Object.getPrototypeOf(foo)); // {constructor: ƒ Foo()}
    
    function Person() { }
    Person.prototype.constructor = null;
    var person = new Person();
    console.log(person.constructor); // null
    console.log(Object.getPrototypeOf(person)); // {constructor: null}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 构造函数还是调用
      • 函数不是构造函数,但是当使用new修饰时,函数调用就会变成构造函数调用

    技术

    1. this.name = name;为每个对象添加了一个name属性
    2. foo1.say()没有在但前对象上找到函数,就会通过委托去Foo原型上去寻找
      function Foo(name) { this.name = name; }
      Foo.prototype.say = function(){
          return this.name;
      }
      var foo1 = new Foo('Lee');
      var foo2 = new Foo('Tom');
      console.log(foo1, foo1.say()); // Lee
      console.log(foo2, foo2.say()); // Tom
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    (原型)继承 - 四种继承方式

    function FunA(a) {
        this.a = a;
    }
    FunA.prototype.logA = function () {
        console.log(this.a);
    }
    function FunB(b) {
        this.b = b;
    }
    FunB.prototype.logB = function () {
        console.log(this.b);
    }
    
    FunB.prototype = Object.create(FunA.prototype);
    // FunB.prototype = FunA.prototype;
    // FunB.prototype = new FunA('aaa');
    // Object.setPrototypeOf(FunB.prototype, FunA.prototype);
    
    console.log(FunB.prototype);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • FunB.prototype = Object.create(FunA.prototype);
      • 创建一个新的 FunB.prototype 对象并把它关联到 FunA.prototype
    • FunB.prototype = FunA.prototype;
      • 不会创建一个关联到 FunB.prototype 的新对象,他只是让 FunB.prototype 直接引用 FunA.prototype 本身
    • FunB.prototype = new FunA();
      • 会创建一个关联到 FunB.prototype 的新对象,但是它使用的是 FunA构造函数调用,如果 FunA 本身存在一些 副作用(如写日志、修改状态、注册到其他对象、给this添加数据属性,等等) 的话,就会影响到 FunB 的后代,后果不堪设想
    • ES6 Object.setPrototypeOf(FunB.prototype, FunA.prototype);
      • Object.create 会抛弃之前的 FunB.prototype 创建一个新的对象
      • Object.setPrototypeOf 会直接修改现有的 FunB.prototype

    检查"类"关系

    • instanceof
      • 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
    • isPrototypeOf
      • 方法用于测试一个对象是否存在于另一个对象的原型链上
    • Object.getPrototypeOf
      • 方法返回指定对象的原型(内部[[Prototype]]属性的值)
    • __proto__(不推荐)
      • 暴露了通过它访问的对象的内部[[Prototype]] (一个对象或 null)

    对象关联(原型链)

    如果在对象上没有找到需要的属性或者方法引用,引擎就会继续在 [[Prototype]] 关联的对象上进行查找。同理,如果在后者中也没有找到需要的 引用就会继续查找它的 [[Prototype]],以此类推。这一系列对象的链接被称为“原型链”。

    • Object.create()polyfill 代码(兼容写法)
      // Object.create() 的 polyfill 代码
      if (!Object.create) {
          Object.create = function (o) {
              function F() { }
              F.prototype = o;
              return new F();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
  • 相关阅读:
    IntelliJ IDEA安装教程
    基于java雪花算法生成long类型无序ID实现
    14.mybatis拦截器原理
    信息安全技术实验:软件的动态分析和破解
    延时队列DelayQueue的使用
    红黑树树插入后自平衡
    怎么把录音转文字?快把这些方法收好
    nginx配置二级域名
    力扣 -- 44. 通配符匹配
    jfreechart后台生成图片采样完美解决方案以及样式美化
  • 原文地址:https://blog.csdn.net/weixin_43526371/article/details/125374088