[JavaScript]原型链是什么

原型链

函数上属性 prototype【原型】指向一个对象【原型对象】–> 对象上存在一个 [[prototype]] 属性指向这个对象的构造函数的prototype指向的原型对象, 这样就不断构成了一个链条,就是原型链

js

function Parent() {
    this.name = 'wang da'
    this.age = 12
}
Parent.prototype.getName = function () {
    return this.name
}

console.log(Parent.prototype)
console.log(Object.getPrototypeOf(Parent.prototype) === Object.prototype)

从第一个打印中可以看到函数Parent的原型对象上有一个默认的constructor属性指向自身,这个原型对象上有一个[[prototye]] 属性指向Object.prototype

从第二个打印中可以看到 原型对象上的[[prototype]]属性指向了Object.prototype, 验证了图中所述的原型链的关系

可以看到链条中关键的连接是[[prototype]] 对应对象上的属性; prototype 对象函数上的属性,理解这两点就可以理解原型链

[[prototype]] 每一个对象上都会有这样一个属性(除非使用 Object.create(null)创建对象),它指向创建这个对象的构造函数上的prototype所对应的原型对象

prototype: 函数上的prototype属性,指向一个对象,该对象中默认有一个contructor属性指向函数本身

实现继承的方式

  1. 原型继承

优点:原型继承可以方便的继承到父类原型上的方法与属性

缺点:引用类型的数据共享一份内存,容易导致多个实例对象操作同一份数据,导致对象数据紊乱

js

function Parent() {
    this.name = 'wang da'
    this.age = 30
    
    this.subjects = ['math', 'english']
}

Parent.prototype.getName = function () {
    return this.name
}

function Child() {
    this.name = 'wang er'
    this.age = 6
}

// 将父类的原型对象赋值给子类的原型
Child.prototype = new Parent() // -- 创建一个对象,对象 __proto__ 指向父类的原型对象
// 修正子类原型对象的构造函数指向
Child.prototype.constructor = Child

const child_1 = new Child()
const child_2 = new Child()

// chid_1 与 child_2 引用的都是原型对象上的 subjects 会导致引用类型时多个实例操作同一份数据
child_1.subjects.push("child_1")

console.log(child_1.subjects) // ['math', 'english', 'child_1']
console.log(child_2.subjects) // ['math', 'english', 'child_1']

// 从打印可以看出,child_2实例上的subjects属性同样被child_1中修改了
  1. 构造函数继承

在子构造函数内部调用父构造函数,并绑定为子类的this, 从而实现继承父构造函数的属性与方法,但是继承不到父构造函数prototype上的属性与方法

js

fuction Child() {
    Parent.call(this)
    this.lastName = 'child'
}

  1. 组合继承

结合原型链继承与contructor继承

js

// 结合构造函数继承与原型继承两种方式
function Child() {
    Parent.call(this)
}

Child.prototype = new Parent()
Child.prototype.constructor = Child

  1. Object.create()

可以用于创建一个对象,第一个参数为对象或null, 第二个参数是新对象数据描述符【可选】

js

// 用于创建绑定原型,避免多一次创建new
Child.prototype = Object.create(Parent.prototype)

// 第二个参数属性描述符
const obj = Object.create({}, {
    name: {
        value: 'wang',
        writable: false,
        enumerable: false,
    }
})
  1. class 继承

js

class Person {
    firstName = 'wang'
    age = 30
    subjects = ['math', 'english']

    constructor(name) {
        this.father_fullName = this.firstName + '_' + name
    }
}

class Child extends Person {
    constructor(fatherName, childName) {
        super(fatherName) // 调用父类构造函数
        this.child_fullName = this.firstName + '_' + childName
    }
}

const child_1 = new Child('ddasd', 'dasdas')

补充 new 创建对象的过程

  1. 创建this对象
  2. 给创建的this对象绑定构造函数的prototype原型属性作为 new 对象的原型对象
  3. 执行构造函数方法, 将this绑定到新对象上
  4. 默认return this, 除非显式返回一个object

根据以上过程可以,可以自定义一个new的函数

js

function myNew(constructor) {
    // 创建对象
    const obj = {}
    // 绑定 __proto__
    Object.setPrototypeOf(obj, constructor.prototype)
    // 执行构造函数,给构造函数中的this绑定为创建的对象
    const res = constructor.apply(obj, Array.prototype.slice.call(arguments, 1))

    // 如果构造函数显式返回的是对象就返回这个对象,否则就是返回创建的对象
    return res instanceof Object ? res : obj
}

const obj = myNew(Parent, 'dadadasd')

标签

发表评论