js-原型和创建对象的几种方式

163 阅读3分钟

1、定义

在JavaScript中,无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构造函数。

image.png

fn = function() {};
fn.prototype; // {constructor: ƒ}
fn.prototype.construct === fn; // true

2、创建对象的几种方式

1.使用 Object 构造函数或对象字面量

虽然使用 Object 构造函数或对象字面量可以方便地创建对象,但这些方式也有明显不足:创建具 有同样接口的多个对象需要重复编写很多代码。

2.工厂模式

工厂模式是一种众所周知的设计模式,广泛应用于软件工程领域,用于抽象创建特定对象的过程。

function createPerson(name, age, job) { 
  let o = new Object(); 
  o.name = name; 
  o.age = age; 
  o.job = job; 
  o.sayName = function() { 
    console.log(this.name); 
  }; 
  return o; 
} 
let person1 = createPerson("张三", 29, "前端"); 
let person2 = createPerson("李四", 27, "后端"); 

这里,函数 createPerson()接收 3 个参数,根据这几个参数构建了一个包含 Person 信息的对象。 可以用不同的参数多次调用这个函数,每次都会返回包含 3 个属性和 1 个方法的对象。这种工厂模式虽然可以解决创建多个类似对象的问题,但没有解决对象标识问题(即新创建的对象是什么类型)。

3.构造函数模式

ECMAScript 中的构造函数是用于创建特定类型对象的。像 Object 和 Array 这样的原生构造函数,运行时可以直接在执行环境中使用。当然也可以自定义构造函数,以函数的形式为自己的对象类型定义属性和方法。

本质是利用 构造函数在 new 的过程中 调用prototype.constructor,修改this指针指向实例 将属性、方法挂载在实例上

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function() { console.log(this.name) }
}
let person1 = new Person("张三", 29, "前端"); 
let person2 = new Person("李四", 27, "后端"); 

构造函数的问题

构造函数虽然有用,但也不是没有问题。构造函数的主要问题在于,其定义的方法会在每个实例上 都创建一遍。因此对前面的例子而言,person1 和 person2 都有名为 sayName()的方法,但这两个方 法不是同一个 Function 实例。我们知道,ECMAScript 中的函数是对象,因此每次定义函数时,都会 初始化一个对象。

console.log(person1.sayName === person2.sayName); // false

4.原型模式

每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型,

function Person() {};
Person.prototype.contry = '中国'
Person.prototype.city = '北京';

let person1 = new Person(); 
let person2 = new Person(); 

实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有 image.png

3、new 的过程做了什么

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.city = '北京';
const person1 = new Person('张三', 18);

要创建 Person 的实例,应使用 new 操作符。以这种方式调用构造函数会执行如下操作。

  1. 在内存中创建一个新对象。
  2. 这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性 - 原型模式
  3. 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)
  4. 执行构造函数内部的代码(给新对象添加属性)- 构造函数模式
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象

需要注意的是第5点,如果构造函数内部存在非空对象,则会返回该对象;

function Person(name, age) {
    this.name = name;
    this.age = age;
    return {
      contry: '中国'
    }
}
Person.prototype.city = '北京';
const person1 = new Person('张三', 18);
const person2 = new Person('李四', 18);

image.png