Skip to content

原型链

js对象的创建

js
const foo = Object.create({});
const bar = {};

foo.__proto__.__proto__=== Object.prototype;
bar. __proto__=== Object.prototype;

Object.create(null) => 指向的是一个完全的空对象

prototype和__proto__的联系与区别

区别:

prototype

  1. prototype 是函数对象特有的属性,用于指向一个对象,这个对象包含由该构造函数创建的所有的实例对象所共享属性和方法。
  2. 当你使用 new 关键字创建一个实例时,这个实例的 __proto__ 属性会指向构造函数的 prototype 对象。
  3. 通过修改构造函数的 prototype 对象,可以实现对所有实例对象共享方法和属性的添加或修改。

__proto__

  1. __proto__ 是每个 JavaScript 对象都有的属性,用于指向该对象的原型。
  2. 通过 __proto__ 属性可以访问对象的原型,即该对象的构造函数的 prototype 对象。
  3. 在原型链中,每个对象通过 __proto__ 属性链接到其构造函数的 prototype 对象,形成了原型链。

联系: __proto__ 属性是实例对象所拥有的,用来寻找原型链中的下一个原型。 prototype 属性是构造函数所拥有的,用来指定实例对象的原型。

new 关键字

js

function Person(name) {
    this.name = name
}
Person.prototype.getName = function () {
    console.log(this.name)
} 
const p = new Person("菜鸡")
// p对象的构造函数是Person
// 1. new 创建一个对象,指向构造函数的原型
p.__proto__ === Person.prototype;
// 2.构造函数的原型(是个对象),有constructor函数,他指向创建实例对象的构造函数。
Person.prototype.constructor === Person;
// 3.p的构造函数就是Person
p.constructor === Person;

function newFunc(Father) {
    if (typeof Father !== 'function') {
        throw new Error('new operator function the frist param must be a function');
    }
    var obj = Object.create(Father.prototype);  // 解释了1
    var result = Father.apply(obj, Array.prototype.slice.call(arguments, 1));  // 构造函数返回的对象
    // 如果构造函数有返回值,就返回构造函数的结果,否则就返回new出来的对象
    return (result && typeof result === 'object' && result !== null) ? result : obj;
}
const p = newFunc(Person, name);

new关键字到底干了什么

  • 1.创建了一个对象
  • 2.该对象的原型,指向了这个构造函数 的 prototype
  • 3.该对象实现(执行)了这个构造函数的方法,根据一些特定情况,返回对象:
    • 如果没有返回值,则返回我创建的这个对象;
    • 如果有返回值,是一个对象,则返回该对象;
    • 如果有返回值,不是一个对象,则返回我创建的这个对象;

继承

两个部分:

  • 使用父类的构造方法以及原型函数
  • 让对象的原型链指向父类

原型链继承 -> 构造函数继承 -> 组合式继承 -> 组合寄生继承

js
function Parent(actions, name) {
    this.actions = actions;
    this.name = name;
}
Parent.prototype.getName = function () {
    console.log(this.name)
}

// 1.原型链
function Child() {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
// 问题:如果有引用的属性,那么实例一单修改,所有都会修改;创建child不能传参


// 2.构造函数继承
function Child(id) {
    Parent.apply(this, Array.prototype.slice.call(arguments, 1));
    this.id = id;
}
// 可以访问父类的属性和方法
// 问题:需要使用call或apply手动传递参数,且无法继承父类的原型方法


// 3.组合式继承
function Child(id) {
    Parent.apply(this, Array.prototype.slice.call(arguments, 1));
    this.id = id;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
// 问题:构造函数执行了两次


// 4.组合寄生继承
function Child(id) {
    Parent.apply(this, Array.prototype.slice.call(arguments, 1));
    this.id = id;
}
Child.prototype = Object.create(Parent.prototype)
// Child.prototype = inherit(Parent.prototype)
Child.prototype.constructor = Child;

// 创建一个仅用于封装父类构造函数的临时函数,通过调用父类构造函数来继承属性,然后将其原型赋值给子类原型。
function inherit(p) {
    if (p == null) throw TypeError();
    if (Object.create) return Object.create(p)
    var t = typeof p;
    if (t !== "object" && t !== "function") throw TypeError()
    function f() { }
    f.prototype = p;
    return new f();
}

组合寄生继承 和 class 继承有什么区别

  • class继承,会继承静态属性
  • class继承子类的constructor,super()必须被调用,且必须在this之前调用,因为this的初始化依赖于super()

构造函数继承 - 为了解决静态属性的问题,将以下的代码插入到 继承 中的 2.构造函数继承 ,类 Child

js
for (var k in Parent) {
    if (Parent.hasOwnProperty(k) && !(k in child)) {
        Child[k] = Parent[k]
    }
}

结论

new方法的本质:

js
const a = new A();  // a.__proto__ = A.prototype
// 模拟实现new
{
    a = Object.create(A.prototype)
}

const b = Object.create(B);   // b.__proto__ = B
// 模拟实现create
{
    function f(){};
    f.prototype = p;
    return new f();
}

鄂ICP备2024055897号