new 构造函数都做了一些什么?

2020/01/04 JavaScript

构造函数改变了 this 的指向

首先你要明白构造函数的 prototype 指向实例原型,实例原型的 constructor 指向构造函数。由构造函数创建的实例,这个实例的 __proto__ 指向实例原型。因为一个构造函数可以 new 很多个实例,所以实例和构造函数之间就没有指向关系了

对象和函数

每一个函数都有一个 prototype 他指向了实例原型( prototype 是函数才会有的属性)。

每一个实例原型都有一个 constructor 属性,这个实现指向函数本身。

每一个实例都有一个 __proto__ 属性,指向了实例原型。

根据对应关系进行对比

function Person() {

}
var person = new Person();

person.__proto__===Person.prototype
true

person.__proto__.constructor===Person
true

person.constructor===Person
true

Person.prototype===person.__proto__
true

注意:后文中提到的 函数的原型 其实就是实例原型。 当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。

如果直接是对象的,他就没有 prototype 属性,他的 __proto__ 属性,指向他的原型(即对象),他的 constructor 属性,这个实现指向函数本身

到这里我们理清楚了函数和对象的关系,prototype__proto__ 还有 constructor 分别是谁的属性,干什么的应该也差不多了。

function Person() {

}
var person = new Person();
console.log(person.constructor === Person); // true

当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到 constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以:

person.constructor === Person.prototype.constructor

说一说 new 构造函数都发生些什么

理清楚 prototype__proto__ 还有 constructor 是为了更好的理解构造函数准备。在一次模拟面试中,朋友问我通过 new 关键字调用函数都发生了一些什么?

关于构造函数的概念,简单介绍,函数声明的时候首字母大写(为了区分普通函数和构造函数),调用的时候通过 new 关键字,否则和普通函数一样,没有区别。

prototype__proto__ 还有 constructor

如果是构造函数调用的话

通过打印 this 发现他指向了函数的原型,推导出 this.__proto__=Persion.prototype

在顺便说一句 构造函数,实例,和原型对象的关系

new 的模拟实现

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

模拟实现:

  1. 创建一个新的对象

  2. 把新对象的原型指向传进来的参数

  3. 执行函数

  4. 根据条件返回结果

// 实现方式1
function myNew(constructor, name) {
  let obj = Object.create(constructor.prototype)
  let res = constructor.call(obj, name)
  return (typeof res === 'object' || typeof res === 'function') ? res : obj
}
// 实现方式2
function myNew(constructor, name) {
  let obj = {}
  Object.setPrototypeOf(obj, constructor)
  let res = constructor.call(obj, name)
  return (typeof res === 'object' || typeof res === 'function') ? res : obj
}
// 实现方式3
function myNew(constructor, name) {
  let obj = {}
  obj.__proto__ = constructor.prototype
  let res = constructor.call(obj, name)
  return (typeof res === 'object' || typeof res === 'function') ? res : obj
}

function Person(name) {
  this.name = name
}
let n = myNew(Person, 'thj')
console.log(n)

Object.create()

class 实现原理

classes6 的语法糖,他的底层还是通过函数实现的

class Person {
  constructor() {
    this.name="sunseekers"
  }
  say(){
    return `${this.name} say: ever weeping, ever youthful`
  }
  static info(age,address){
    return `${age} and ${address}`
  }
}

// 等价于
function Person(name){
  this.name=name
}
Person.prototype.say=function(){
  return `${this.name} say: ever weeping, ever youthful`
}
Person.info=(age,address)=>`${age} and ${address}`

类的数据类型就是函数,类本身就指向构造函数

class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true

原型方法和工具方法区别

instanceof 运算符的第一个参数是一个对象,第二个参数一般是一个函数

instanceof 的判断规则是: 沿着对象的 __proto__ 这条链来向上查找找,如果能找到函数的prototype 则返回 true ,否则 返回 false

function Person(name, age) {
  this.name = name;
  this.age = age;
  // 构造函数方法,每声明一个实例,都会重新创建一次,属于实例独有
  this.getName = function() {
    return this.name;
  }
}

// 原型方法,仅在原型创建时声明一次,属于所有实例共享
Person.prototype.getAge = function() {
  return this.age;
}

// 工具方法,直接挂载在构造函数名上,仅声明一次,无法直接访问实例内部属性与方法
Person.each = function() {
 console.log(7887)
}
// new Person.each()或者Person.each()可以执行这个方法

class 和 new 混用

es6 普遍之后,大部分的时候都是使用 class 创建一个类,几乎很少使用构造函数去创建一个类。因为使用起来方便简洁,具体对比不详细介绍

问一个问题: class 创建的类能在构造函数创建的类中使用调用吗?反之成了吗?

function UserInfo() {
  this.name = 'sunseekers'
  this.color = 'red'
}

class Persion {
  constructor() {
    this.name = 'kitty'
    this.color = 'yellow'
    this.info = new UserInfo()
  }
  getNames() {
    console.log('name:' + this.name)
    console.log(new UserInfo())
  }
}

答案是,调用恰当,是可以互相调用的。

参考文章

JavaScript 深入之从原型到原型链

JavaScript 深入之 new 的模拟实现

Class 的基本语法


有时候,虽然素未谋面。却已相识很久,很微妙也很知足。在逝去的岁月里,我们在某一刻,共同经历着一样的情愫。

如果喜欢我的话,我们可以互相关注,相互学习的哟!互相鼓励,你的关注是我最大的动力来源

sunseekers

Search

    Table of Contents