前端基础(三十六):你不知道的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

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
    
  • 定义的name属性屏蔽了原型上的name属性
    function Person(){
          
          
        this.name = 'Lee';
    }
    Person.prototype.name = 'Tom';
    
    var person = new Person();
    
    console.log(person.name); // Lee
    

针对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
      
    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. 【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
    
  • 产生隐式屏蔽的另一种写法
    在这里插入图片描述

“类”

"类"函数

调用 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

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

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

“构造函数”

我们认为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}
  • 构造函数还是调用
    • 函数不是构造函数,但是当使用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
    

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

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);
  • 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();
        }
    }
    

猜你喜欢

转载自blog.csdn.net/weixin_43526371/article/details/125374088