conselo.log:😊😊😊|

CodeForBetter

园龄:3年4个月粉丝:11关注:1

2023-02-23 18:11阅读: 365评论: 0推荐: 3

浅谈JS原型

前言

JavaScript 原型是该语言中一个非常重要的概念。理解原型是理解 JavaScript 的关键。在本篇技术博客中,我们将深入探讨 JavaScript 的原型概念,并介绍常用的操作对象原型的方法。(欢迎点评,欢迎指正!)

什么是原型?

在 JavaScript 中,每个对象都有一个原型(prototype)对象。原型可以看做是对象的“父类”,包含了一些共有的属性和方法。当我们试图访问对象的属性时,JavaScript 首先查找该对象本身是否有该属性,如果没有,就会在该对象的原型中查找,如果还没有,就会一直沿着原型链(也可理解为原型对象的原型对象)向上查找,直到找到该属性或者查找到原型链的顶端为止。

在 JavaScript 中,我们可以使用 Object.getPrototypeOf 方法来获取一个对象的原型,例如:

const obj = {};
console.log(Object.getPrototypeOf(obj)); // 输出:{}

这里的 {} 就是 obj 对象的原型。

原型链

每个对象的原型也可以有自己的原型,这样就形成了一个链式结构,我们称之为“原型链”。

在 JavaScript 中,原型链的顶端是 Object.prototype,它是所有对象的默认原型。Object.prototype 对象中包含了一些常用的属性和方法,例如 toString、valueOf 等。

const obj = {};
console.log(Object.getPrototypeOf(obj) === Object.prototype); // 输出:true
console.log(obj.toString()); // 输出:[object Object]

在 JavaScript 中,我们可以通过 Object.create 方法来创建一个新对象,并将其原型指向指定对象。例如:

const obj1 = { name: 'Jack' };
const obj2 = Object.create(obj1);
console.log(obj2.name); // 输出:'Jack'

在上面的代码中,我们创建了一个对象 obj1,然后通过 Object.create 方法创建了一个新对象 obj2,并将其原型指向 obj1。由于 obj1 中包含了一个属性 name,所以在 obj2 中也能访问到该属性。

构造函数创建对象

在JavaScript中,通过构造函数创建对象时,这些对象都会有一个内部属性Prototype,指向构造函数的原型对象。例如:

function Person(name, age) {
this.name = name;
this.age = age;
}
const person = new Person('Tom', 20);
console.log(person.__proto__ === Person.prototype); // true

这里的person.__proto__就是指向Person构造函数的原型对象Person.prototype。通过new关键字创建的实例对象就是构造函数的一个实例,因此它继承了构造函数的原型对象中定义的属性和方法。

如果我们想要给Person构造函数的原型对象添加一个方法,可以通过给Person.prototype赋值来实现:

Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
}

现在,Person构造函数的所有实例对象都可以访问这个新定义的方法:

const person = new Person('Tom', 20);
person.sayHello(); // Hello, my name is Tom

当我们访问一个对象的属性或方法时,JavaScript引擎会首先查找对象本身是否有该属性或方法。如果对象本身没有定义,就会沿着它的原型链向上查找,直到找到为止。如果一直查找到最顶层的原型对象还没有找到,则返回undefined。

__proto__Prototype 区别

在JavaScript中,每个对象都有一个__proto__ 属性和一个构造函数的 prototype 属性。它们两者都与对象原型有关,但是它们的作用和用法是不同的。
__proto__ 是一个对象的内部属性,用于指向该对象的原型。当我们访问对象的某个属性时,JavaScript引擎会先查找该对象本身是否有该属性,如果没有则会去该对象的原型链中查找。因此,我们可以通过修改 __proto__ 来修改对象的原型链,但是不建议这样做,因为 __proto__ 是一个非标准的属性,在一些浏览器中可能会出现兼容性问题。

相比之下,prototype 是一个构造函数特有的属性,它是一个对象,包含了该构造函数的所有实例共享的属性和方法。当我们使用 new 运算符创建一个实例时,实例对象会继承其构造函数的 prototype 属性。因此,我们可以通过在构造函数的 prototype 上定义属性和方法来实现所有实例共享这些属性和方法的效果。

下面是一个简单的例子,演示了 __proto__prototype 的使用方法和区别:

function Animal(name) {
this.name = name;
}
// 通过 prototype 定义方法
Animal.prototype.sayName = function() {
console.log('My name is', this.name);
};
// 创建实例
const cat = new Animal('Tom');
// 访问实例属性和方法
console.log(cat.name); // 'Tom'
cat.sayName(); // 'My name is Tom'
// 修改实例的 __proto__
const dog = {};
dog.__proto__ = Animal.prototype;
dog.name = 'Spike';
dog.sayName(); // 'My name is Spike'

上述例子中,我们首先定义了一个 Animal 构造函数,并通过 prototype 定义了一个 sayName 方法。然后,我们创建了一个 cat 实例,访问了实例的 name 属性和 sayName 方法。接着,我们创建了一个 dog 对象,并将其 __proto__ 属性设置为 Animal.prototype,这样 dog 对象就可以继承 Animal 的 prototype 上定义的方法和属性。最后,我们访问了 dog 对象的 name 属性和 sayName 方法,验证了 __proto__prototype 的区别。

操作对象原型的方法

在 JavaScript 中,我们可以使用一些方法来操作对象原型,下面是一些常用的操作对象原型的方法:

  • Object.getPrototypeOf(obj):获取对象的原型。
  • Object.setPrototypeOf(obj, prototype):设置对象的原型。
  • obj.hasOwnProperty(prop):判断对象是否有自有属性prop。
  • obj.isPrototypeOf(obj2):判断对象是否是另一个对象的原型。

需要注意的是,修改原型对象会对继承自它的所有实例对象生效。因此,在修改原型对象时需要特别小心,确保不会对其他对象产生影响。

Object.create()

Object.create():创建一个新对象,使用现有对象作为新对象的原型。

const parent = {
name: "parent",
sayHello: function () {
console.log(`Hello, I'm ${this.name}.`);
},
};
const child = Object.create(parent);
child.name = "child";
child.sayHello(); // Hello, I'm child.

Object.getPrototypeOf()

Object.getPrototypeOf():获取对象的原型。

const parent = {
name: "parent",
};
const child = Object.create(parent);
console.log(Object.getPrototypeOf(child) === parent); // true

Object.setPrototypeOf()

Object.setPrototypeOf():设置对象的原型。

const parent = {
name: "parent",
};
const child = {};
Object.setPrototypeOf(child, parent);
console.log(Object.getPrototypeOf(child) === parent); // true

Object.prototype.hasOwnProperty()

Object.prototype.hasOwnProperty():判断对象是否拥有某个属性,不包括原型链上的属性。

const obj = { a: 1 };
console.log(obj.hasOwnProperty("a")); // true
console.log(obj.hasOwnProperty("toString")); // false

Object.prototype.isPrototypeOf()

Object.prototype.isPrototypeOf():判断对象是否是另一个对象的原型。

const parent = {};
const child = Object.create(parent);
console.log(parent.isPrototypeOf(child)); // true
console.log(child.isPrototypeOf(parent)); // false

Object.prototype.constructor

Object.prototype.constructor:获取对象的构造函数。

function Person(name) {
this.name = name;
}
const person = new Person("Tom");
console.log(person.constructor === Person); // true

使用场景

JS的原型在实际开发中有许多使用场景,其中一些常见的包括:

  • 原型继承:使用原型链实现继承,可以减少代码的冗余,提高代码的可重用性。
  • 动态添加方法:在原型对象上动态添加方法,可以使得所有实例都能共享该方法,避免每个实例都单独创建方法,节省内存。
  • 实现多态:利用原型链的特性,可以实现多态,即同一个方法可以有多种不同的实现方式,根据实际对象类型进行调用。
  • 扩展和覆盖原型属性和方法:可以通过修改原型对象来实现对所有实例属性和方法的扩展或覆盖,具有很大的灵活性。

举例说明几个使用场景

创建对象

在JavaScript中,可以使用原型来创建对象,这种方式可以避免多次创建相同的对象,提高代码的性能。例如:

function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
}
const person1 = new Person('John');
person1.sayHello(); // 输出:Hello, my name is John
const person2 = new Person('Jane');
person2.sayHello(); // 输出:Hello, my name is Jane

继承

JavaScript中没有类的概念,继承是通过原型来实现的。子类通过原型链继承父类的属性和方法。例如:

function Animal(name) {
this.name = name;
}
Animal.prototype.sayName = function() {
console.log(`My name is ${this.name}`);
}
function Dog(name, breed) {
Animal.call(this, name); // 继承父类的属性
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype); // 继承父类的方法
Dog.prototype.constructor = Dog;
Dog.prototype.sayBreed = function() {
console.log(`My breed is ${this.breed}`);
}
const dog = new Dog('Buddy', 'Golden Retriever');
dog.sayName(); // 输出:My name is Buddy
dog.sayBreed(); // 输出:My breed is Golden Retriever

修改对象

通过原型,可以方便地对现有对象进行修改或添加新的属性和方法。例如:

const person = { name: 'John' };
Object.getPrototypeOf(person).sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
}
person.sayHello(); // 输出:Hello, my name is John

多态

在 JavaScript 中,可以使用原型实现多态。多态是指在不同的对象实例中,同一个方法可以有不同的实现方式。通过原型,可以在不改变对象实例自身的情况下,实现方法的多态性。

举个例子,假设有一个名为 Animal 的构造函数,其原型对象有一个方法 speak,并且有两个子构造函数 Cat 和 Dog,它们继承了 Animal 的原型对象。现在我们希望在 Cat 和 Dog 的实例中,分别实现 speak 方法的不同实现方式。

function Animal() {}
Animal.prototype.speak = function() {
console.log('Animal speaks');
}
function Cat() {}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.speak = function() {
console.log('Meow');
}
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function() {
console.log('Woof');
}
const cat = new Cat();
const dog = new Dog();
cat.speak(); // Meow
dog.speak(); // Woof

在上述例子中,我们通过将 Cat 和 Dog 的原型对象设置为 Animal 的原型对象,并分别实现了它们自己的 speak 方法,使得它们的 speak 方法在不同的实例中有不同的实现方式。这就是使用 JS 原型实现多态的一种方法。
说明:在JavaScript中,每个函数都有一个默认的 prototype 属性,它指向一个对象,即该函数的原型对象。该原型对象是包含构造函数所有实例共享的属性和方法。

而 constructor 是原型对象上的一个属性,它指向创建当前对象的构造函数。因此 Cat.prototype.constructor = Cat 的作用是将 Cat 函数的原型对象上的 constructor 属性指向 Cat 函数本身,以确保 Cat 函数作为构造函数创建的对象,其 constructor 属性正确地指向 Cat 函数本身。

在上述代码中,由于将 Animal 函数的原型对象替换为 Cat 函数的实例,因此也需要将 constructor 属性指向 Cat 函数本身,以确保 constructor 属性的正确性。

总结

JavaScript 中的所有对象都有一个内置的属性 __proto__,用于指向其原型对象。每个构造函数都有一个名为 Prototype 的属性,该属性指向一个对象,该对象是由该构造函数创建的所有实例对象的原型。因此,可以通过构造函数的 Prototype 属性来扩展对象的原型。对象可以访问其原型中的属性和方法,就像它们是对象自身的属性和方法一样。当对象尝试访问其自身不存在的属性或方法时,它会沿着原型链向上查找,直到找到匹配的属性或方法为止。

在 JavaScript 中,原型可以用于实现继承和共享属性和方法。通过创建一个新对象,并将其原型设置为另一个对象,可以实现原型继承。此外,可以使用原型将属性和方法添加到对象中,这些属性和方法可以被该对象的所有实例共享。

总之,原型是JavaScript中一个非常重要的概念,掌握原型的原理和使用方法对于编写高效、优雅的JavaScript代码至关重要。

本文作者:CodeForBetter

本文链接:https://www.cnblogs.com/CodeForBetter/p/17147587.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   CodeForBetter  阅读(365)  评论(0编辑  收藏  举报
   
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 第三人称 买辣椒也用券
  2. 2 依然爱你 yihuik苡慧
  3. 3 唯一 告五人
  4. 4 那么骄傲 孟静
依然爱你 - yihuik苡慧
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 王力宏

作曲 : 王力宏

原唱 : 王力宏

一闪一闪亮晶晶 留下岁月的痕迹

我的世界的中心 依然还是你

一年一年又一年 飞逝仅在一转眼

唯一永远不改变 是不停地改变

我不像从前的自己 你也有点不像你

但在我眼中你的笑 依然的美丽

日子只能往前走 一个方向顺时钟

不知道爱有多久 所以要让你懂

我依然爱你 就是唯一的退路

我依然珍惜 时时刻刻的幸福

你每个呼吸 每个动作 每个表情

到最后 一定会 依然爱你

我不像从前的自己 你也有点不像你

但在我眼中你的笑 依然的美丽

日子只能往前走 一个方向顺时钟

不知道爱有多久 所以要让你懂

我依然爱你 就是唯一的退路

我依然珍惜 时时刻刻的幸福

你每个呼吸 每个动作 每个表情

到最后 一定会 依然爱你

我依然爱你 或许是命中注定

多年之后任何人 都无法代替

那些时光 是我这一辈子最美好的

那些回忆 依然无法忘记

我依然爱你 就是唯一的退路

我依然珍惜 时时刻刻的幸福

你每个呼吸 每个动作 每个表情

到最后 一定会 依然爱你

你每个呼吸 每个动作 每个表情

到永远 一定会 依然爱你