理解原型链和new

455 阅读2分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战

原型链

function Person(){
this.name = 'tom'
}
Person.prototype.age = 18
let person = new Person()
console.log(person.name)
console.log(person.age)

如以上的代码中,Person就是构造函数,Person.prototype就是Person的原型,person则是由Person构建出来的一个实例,它能够获取到Person里的变量,也能获取到Person原型上的变量。

prototype

每一个函数都有prototype属性,也只是函数拥有这个属性。函数的prototype属性指向了一个对象,这个对象就是因调用构造函数而创建的实例的原型。他们的关系如下图:

image.png

那实例与原型是通过什么来联系的呢?

__ proto __

每一个js对象都有(除了null和undefined)都有一个__ proto __属性指向原型,该属性并非标准属性,但是大部分浏览器都有内置该属性指向了构造函数的原型,但是还可以使用Object.getprototypeOf(obj)来获取

function Person(){}
let person = new Person()
person.__proto__ = Person.prototype // true

image.png

那么有没有属性是原型对象指向构造函数的呢?

constructor

constructor是原型对象上的一个属性,其指向构造函数,实例对象上是没有这个属性的,但是由于其找不到该属性,会再其原型上查找,因此实例对象也能够访问该属性。

image.png

实例与原型

当实例对象读取属性时,若是读取不到,便会去对象的原型上查找,原型其实也是一个对象,在其身上查找不到,其会继续向它的原型查找,知道原型链的尽头为止。

function Person(){}
Person.prototype.name = 'Tom'

let person = new Person()
person.name // Tom
person.name = 'Jack'
person.name // Jack
delete person.name
person.name // Tom

image.png

new

new 运算符可以创建一个对象实例,该实例可以访问构造函数内的属性和方法,也可以访问构造函数原型上的属性和方法

下面我们来实现一个new方法:

function myNew(){
    let obj = new Object()
    let Constructor = [].shift.call(arguments)
    obj.__proto__ = Constructor.prototype
    Constructor.apply(obj,arguments)
    return obj
}
function Person(name){
this.name = name || 'person'
}
Person.age = 18
let person = myNew(Person,'tom')
console.log(person.name) // tom
person.__proto__ === Person.prototype // true

来看看new运算符到底做了什么

  1. 创建一个空对象并将他的原型指向了构造函数的原型对象
  2. 调用构造函数
  3. 返回obj对象

但是这样还是不够的,如果构造函数有返回值呢?让我们来看个例子:

function Person (name, age) {
    this.strength = 60;
    this.age = age;

    return {
        name: name,
        habit: 'Games'
    }
}

var person = new Person('Kevin', '18');

console.log(person.name) // Kevin
console.log(person.habit) // Games
console.log(person.strength) // undefined
console.log(person.age) // undefined
function Person(name, age) {
    this.strength = 60;
    this.age = age;

    return 'handsome boy';
}

var person = new Person('Kevin', '18');

console.log(person.name) // undefined
console.log(person.habit) // undefined
console.log(person.strength) // 60
console.log(person.age) // 18

可以看到上述结果中,若是构造函数返回一个对象,则我们是不能访问构造函数内部的属性的,而返回其他类型即可

因此,对于刚才的new方法还要再进行一次改写:

function myNew(){
  let obj = new Object()
  let Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  let res = Constructor.apply(obj,arguments)
  return typeof res === 'object' ? res : obj
}