javascript 对象 与 prototype 原型用法实例分析

JavaScript 中的对象是非常重要的概念,它是用来封装相关属性和行为的数据类型,JavaScript 对象实际上是一个特殊的键值对集合,每个键值对被称为一个属性或者方法。

JavaScript 对象与 Prototype 原型用法实例分析

JavaScript 中的对象是非常重要的概念,它是用来封装相关属性和行为的数据类型,JavaScript 对象实际上是一个特殊的键值对集合,每个键值对被称为一个属性或者方法。

JavaScript 中对象的创建有很多方式,包括字面量语法、构造函数语法、Object.create() 等,本文主要讲解 JavaScript 中的 Prototype 原型用法,这种方式可以实现灵活的对象继承,方便地复用已有的代码。

JavaScript 原型

在 JavaScript 中,每个对象都有一个原型,原型是一个对象,对象可以通过原型来继承另一个对象的属性和方法,这种机制被称为原型继承,原型可以通过 __proto__ 属性来访问,也可以使用 Object.getPrototypeOf() 方法来访问。

原型的作用是为对象提供一些共享的属性和方法,当对象调用一个属性或者方法时,如果该属性或方法不存在于该对象本身,则会继续在原型链中查找,直到找到该属性或方法或者到达 Object.prototype。

prototype 属性和构造函数

JavaScript 中的函数也是对象,每个函数对象都有一个 prototype 属性,该属性是一个对象,表示该函数的原型对象,当使用该函数作为构造函数创建新的实例对象时,该实例对象会从原型对象中继承属性和方法,这种机制被称为构造函数继承。

构造函数的属性和方法都存在于构造函数的原型对象中,这可以极大地减少每个实例对象所占用的内存空间,并且使得对象间共享属性和方法成为可能。

下面是一个例子,首先定义一个构造函数 Person,然后定义 Person.prototype 对象,并在其上定义了一个 sayHello 方法,该方法用来打印字符串 "Hello, I'm ${this.name}",其中 ${this.name} 是一个占位符,表示实例对象的 name 属性值。

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

Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}.`);
};

然后,我们可以使用 Person 构造函数来创建实例对象,并调用 sayHello 方法,例如:

let p1 = new Person('Alice', 18);
let p2 = new Person('Bob', 20);

p1.sayHello(); // Hello, I'm Alice.
p2.sayHello(); // Hello, I'm Bob.

可以看到,两个实例对象 p1p2 都可以调用 sayHello 方法,因为该方法存在于它们的原型对象 Person.prototype 中。

原型链

JavaScript 中的对象可以通过原型链来继承多个对象的属性和方法,原型链就是多个对象原型的链式链接。

例如,我们可以通过定义一个 Student 构造函数来继承 Person 的属性和方法,该构造函数的原型对象 Student.prototypePerson 的实例对象,这样所有通过 new Student() 创建的对象都会从 Student.prototype 继承 Person 的属性和方法。

function Student(name, age, grade) {
  Person.call(this, name, age); // 调用 Person 构造函数初始化 name 和 age 属性
  this.grade = grade;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.sayGrade = function() {
  console.log(`My grade is ${this.grade}.`);
};

上述代码中,我们首先调用 Person 构造函数来初始化 nameage 属性,然后使用 Object.create() 方法来创建一个新对象作为 Student.prototype,该新对象的原型对象是 Person.prototype,这样就实现了 Student 构造函数对 Person 属性和方法的继承。

注意,我们还需要将 Student.prototypeconstructor 属性设置为 Student,以便后续使用 new 关键词来创建 Student 对象。

最后,我们在 Student.prototype 上定义了一个新的方法 sayGrade,该方法用来打印字符串 "My grade is ${this.grade}",其中 ${this.grade} 是实例对象的 grade 属性值。

现在,我们可以使用 Student 构造函数来创建一个实例对象,并调用它的 sayHellosayGrade 方法:

let s1 = new Student('John', 20, 'A');
s1.sayHello();  // Hello, I'm John.
s1.sayGrade();  // My grade is A.

我们可以在 Chrome 的控制台中输入 s1.__proto__ 来查看该对象的原型链:

s1.__proto__     // => Student.prototype
s1.__proto__.__proto__     // => Person.prototype
s1.__proto__.__proto__.__proto__     // => Object.prototype
s1.__proto__.__proto__.__proto__.__proto__     // => null

注意到 s1.__proto__ 的原型是 Student.prototype,该对象的原型是 Person.prototype,以此类推,最后到达 Object.prototype,该对象是所有对象的默认原型,最后再到达 null,表示原型链的终止。

结语

JavaScript 中的对象和原型继承是非常灵活的,它们能够让我们在开发中轻松地实现代码的复用和组合,同时还可以简化代码,提高代码的可读性和可维护性。我们需要认真学习和掌握 JavaScript 对象和原型继承的知识,以便能够在开发中灵活地使用它们。

示例一:原型链继承的缺陷

function Animal(name) {
  this.name = name;
  this.sleep = function() {
    console.log(`${this.name} is sleeping`);
  };
}
Animal.prototype.eat = function(food) {
  console.log(`${this.name} is eating ${food}`);
};

function Cat(name) {
  this.name = name;
}
Cat.prototype = new Animal("cat");
Cat.prototype.constructor = Cat;

let cat1 = new Cat("cat1");
let cat2 = new Cat("cat2");

cat1.eat("fish");
cat1.sleep();

cat2.eat("meat");
cat2.sleep();

console.log(cat1.sleep === cat2.sleep); //false

我们使用 Cat 构造函数来继承 Animal 的属性和方法,但是,使用 Cat.prototype = new Animal("cat"); 这种方式会导致所有 Cat 实例对象共享原型对象 Animal.prototype,如果我们通过 cat1.sleep === cat2.sleep 来比较两个实例对象的 sleep 方法,我们会发现它们不相等,原因是该方法存在于原型对象而非实例对象上,因此根据原型链继承的规则,它们实际上继承自不同的方法对象。这就是原型链继承的一个常见缺陷。

示例二:使用 Object.create() 来创建原型对象

let animal = {
  name: "",
  sleep: function() {
    console.log(`${this.name} is sleeping`);
  },
  eat: function(food) {
    console.log(`${this.name} is eating ${food}`);
  },
};

function Cat(name) {
  this.name = name;
}

Cat.prototype = Object.create(animal);
Cat.prototype.constructor = Cat;

let cat1 = new Cat("cat1");
let cat2 = new Cat("cat2");

cat1.eat("fish");
cat1.sleep();

cat2.eat("meat");
cat2.sleep();

console.log(cat1.sleep === cat2.sleep); //true

在该例子中,使用 animal 对象作为 Cat 的原型对象,可以避免多实例对象共享原型对象的问题,因为每个实例对象都拥有它自己的原型对象,而该对象是通过 Object.create() 方法来创建的,该方法会返回一个新对象,新对象的原型是参数中传入的对象。

注意,我们需要手动将 Cat.prototypeconstructor 属性设置为 Cat,否则会使用默认的 Object.prototype.constructor

本文标题为:javascript 对象 与 prototype 原型用法实例分析

基础教程推荐