跳转至

JavaScript中的new和原型原型链

new

JavaScript中new操作符用于创建一个给定构造函数的对象实例 :

function Person(name, age){
    this.name = name;
    this.age = age;
}
const person1 = new Person('Tom', 20)
console.log(person1)  // Person {name: "Tom", age: 20}
  • 我们定义了一个构造函数Person, 然后通过new操作符生成Person构造函数的一个实例并将其引用赋值给变量person1,然后控制台打印出person1的内容, 可以看到该实例对象具有nameage属性, 它们的值就是我们在调用构造函数时传入的值

new关键字进行的操作

为了方便描述 obj 用于创建的新对象 用 constrc 表示构造函数

  1. 创建一个对象 obj {}
  2. obj[[prototype]]属性指向构造函数constrc的原型 (即obj.[[prototype]] = constrc.prototype)
  3. 将构造函数constrc内部的this绑定到新建的对象obj, 执行constrc
  4. 若构造函数没有返回非原始值(即不是引用类型的值), 则返回该 新建的对象obj 默认会添加return this),否则,返回 引用类型的值

[[prototype]]属性是隐藏的,不过目前大部分新浏览器实现方式是使用__proto__来表示

19546-w9gqw2sct9.png

原型链

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找

这样 通过__proto__属性一层一层向上查找就会形成一个链式结构,我们称为原型链

function Parent(month){
    this.month = month;
}

var child = new Parent('Ann');

console.log(child.month); // Ann

console.log(child.father); // undefined

98722-dcscpvqtwr.png

14322-oext8r8yvyf.png

原型与原型链的关系

  1. 红色箭头表示__proto__属性指向
  2. 绿色箭头表示prototype属性的指向
  3. 棕色实线箭头表示本身具有的constructor属性的指向
  4. 棕色虚线箭头表示继承而来的constructor属性的指向
  5. 蓝色方块表示对象,浅绿色方块表示函数 ( 这里为了更好看清,Foo()仅代表是函数,并不是指执行函数Foo后得到的结果,图中的其他函数同理 )
  6. 图的中间部分即为它们之间的联系,图的最左边即为例子代码

41653-m4a5jvr57jj.png

__ proto__ 属性

__proto__和constructor属性是对象所独有的

prototype属性是函数所独有的 , 但是由于JS中函数也是一种对象, 所以函数也拥有__proto__和constructor属性

23583-fegubjr95wc.png

  1. 仅留下 __proto__ 属性,它是对象所独有的,可以看到__proto__属性都是由一个对象指向一个对象,即指向它们的原型对象(也可以理解为父对象 )
  2. 它的作用就是当访问一个对象的属性时, 如果该对象内部不存在这个属性 , 那么就会去它的__proto__属性所指向的那个对象(可以理解为父对象)里找, 如果父对象也不存在这个属性, 则继续往父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找…直到原型链顶端null
  3. 平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠__proto__继承而来的

prototype属性

函数所独有的, 它是从一个函数指向一个对象 , 它的含义是函数的原型对象 , 也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象: f1.proto === Foo.prototype , 它们两个完全一样

作用就是包含可以由特定类型的所有实例共享的属性和方法,也就是让该函数所实例化的对象们都可以找到公用的属性和方法。任何函数在创建的时候,其实会默认同时创建该函数的prototype对象

70495-uo47u19vv29.png

constructor属性

constructor属性也是对象才拥有的 , 它是从一个对象指向一个函数, 就是指向该对象的构造函数

48740-j4djcwvd5as.png

每个对象都有构造函数 (本身拥有或继承而来, 继承而来的要结合__proto__属性查看会更清楚点, 如下图 )

上图中可以看出Function这个对象比较特殊, 它的构造函数就是它自己 ( 因为Function可以看成是一个函数, 也可以是一个对象), 所有函数和对象最终都是由Function构造函数得来, 所以constructor属性的终点就是Function这个函数

12437-q0ahgobwv9r.png

每个对象都有构造函数

每个对象都可以找到其对应的constructor, 因为创建对象的前提是需要有constructor, 而这个constructor可能是对象自己本身显式定义的或者通过__proto__在原型链中找到的

单从constructor这个属性来讲, 只有prototype对象才有, 每个函数在创建的时候, JS会同时创建一个该函数对应的prototype对象

函数创建的对象.proto === 该函数.prototype,该函数.prototype.constructor===该函数本身 , 故通过函数创建的对象即使自己没有constructor属性 , 也能通过__proto__找到对应的constructor , 所以任何对象最终都可以找到其构造函数 (null除外)

97694-nknoo5ourbo.png

小结

  1. 我们需要牢记两点: __proto__constructor属性是对象所独有的; prototype属性是函数所独有的,因为函数也是一种对象, 所以函数也拥有__proto__constructor属性
  2. __proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错
  3. prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.__proto__ === Foo.prototype
  4. constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function