一、JavaScript 中的原型与原型链
原型对象的基本概念 | 在 JavaScript 中,每个对象都有一个内置属性 [[Prototype]](在某些实现中被访问为 __proto__),它指向另一个对象,这个对象就是该对象的原型(prototype),当访问对象的属性或方法时,如果该对象本身没有定义该属性或方法,JavaScript 引擎就会沿着原型链去寻找,直到找到为止,原型链是一种机制,它将对象串联起来,形成了一条查找路径。 |
prototype 属性的作用 | 每个 JavaScript 的函数都有一个特殊的属性 prototype,该属性指向一个对象,函数的所有实例都可以继承这个对象上的属性和方法,prototype 属性是 JavaScript 实现继承的基础。 function Person(name) { this.name = name; } Person.prototype.sayHello = function() { console.log('Hello, my name is ' + this.name); }; const person1 = new Person('Alice'); person1.sayHello(); // 输出: Hello, my name is Alice 在这个例子中,sayHello 方法被添加到了 Person.prototype 上,person1 可以通过原型链访问这个方法。 |
原型链详解
原型链的结构 | JavaScript 的对象之间通过原型链联系起来,当你访问对象的属性时,JavaScript 会首先检查对象本身是否有该属性,如果没有,它会顺着原型链逐级向上查找,直到找到该属性或到达 null(原型链的终点),这个查找过程的顺序就是原型链的结构。 console.log(person1.__proto__ === Person.prototype); // true console.log(Person.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__ === null); // true 在上述例子中,person1 的 __proto__ 指向 Person.prototype,而 Person.prototype 的 __proto__ 又指向 Object.prototype,Object.prototype.__proto__ 是 null,表明已经到达原型链的顶端。 |
原型链的层级 | 我们可以理解原型链为一个层级结构: 实例对象的 __proto__ 指向其构造函数的 prototype 对象。 构造函数的 prototype 对象的 __proto__ 通常指向 Object.prototype。 Object.prototype 是所有对象的顶层原型,它的 __proto__ 为 null。 |
构造函数与原型对象
构造函数与 prototype | 每当你使用构造函数创建对象时,该对象会自动获得对构造函数 prototype 属性中方法和属性的访问权限,这意味着你可以通过 prototype 为对象实例添加共享的属性和方法,而无需每次创建新实例时都重新定义这些属性和方法。 function Dog(name) { this.name = name; } Dog.prototype.bark = function() { console.log(this.name + ' is barking!'); } const dog1 = new Dog('Buddy'); const dog2 = new Dog('Charlie'); dog1.bark(); // 输出: Buddy is barking! dog2.bark(); // 输出: Charlie is barking! 在这个例子中,bark 方法只定义了一次,但 dog1 和 dog2 实例都可以通过原型链访问并使用该方法,这不仅节省了内存,还提高了代码的复用性。 |
共享 vs. 实例属性 | 需要注意的是,使用 prototype 定义的属性是共享的,而使用 this 定义的属性是实例独有的。 function Cat(name) { this.name = name; // 实例属性 } Cat.prototype.type = 'feline'; // 原型属性 const cat1 = new Cat('Whiskers'); const cat2 = new Cat('Felix'); console.log(cat1.type); // 输出: feline console.log(cat2.type); // 输出: feline cat1.type = 'domestic'; console.log(cat1.type); // 输出: domestic (实例属性覆盖了原型属性) console.log(cat2.type); // 输出: feline 在这个例子中,cat1 的 type 属性被修改成了实例属性,不再从原型链中获取该值,而 cat2 依然保持原型中的 feline 值。 |
四、使用Object.create()
创建对象
使用Object.create() 创建对象 | 除了使用构造函数,JavaScript 还提供了Object.create() 方法来创建对象。Object.create() 方法接受两个参数:一个原型对象和一个包含属性描述符的对象字面量,通过Object.create() ,你可以创建一个新对象,其原型被设置为第一个参数指定的对象。const obj = Object.create(null); obj.prop = 'value'; console.log(obj.prop); // 输出: value 在这个例子中,我们使用 Object.create(null) 创建了一个新对象,并将它的prop 属性设置为 'value',由于我们没有指定任何原型,所以这个对象没有任何内置的方法或属性。 |
五、ES6 中的class
与原型
class 语法糖 | ES6 引入了class 关键字作为语法糖,使得编写基于原型的继承更加直观和简洁,虽然class 看起来与传统的类继承相似,但本质上仍然是基于原型的继承。class Animal { speak() { console.log('Animal speaks'); } } class Dog extends Animal { bark() { console.log('Dog barks'); } } const dog = new Dog(); dog.speak(); // 输出: Animal speaks dog.bark(); // 输出: Dog barks 在这个例子中,我们定义了一个 Animal 类和一个继承自Animal 的Dog 类,尽管使用了class 关键字,但实际上仍然是基于原型的继承。 |
class 继承 | ES6 的class 语法糖允许我们使用extends 关键字来实现继承,子类可以继承父类的属性和方法,同时还可以添加自己的属性和方法。class Animal { speak() { console.log('Animal speaks'); } } class Dog extends Animal { bark() { console.log('Dog barks'); } } const dog = new Dog(); dog.speak(); // 输出: Animal speaks dog.bark(); // 输出: Dog barks 在这个例子中,我们定义了一个 Animal 类和一个继承自Animal 的Dog 类,尽管使用了class 关键字,但实际上仍然是基于原型的继承。 |
JavaScript 是一门强大的编程语言,其独特的对象模型基于原型链而非传统的类继承,本文详细介绍了 JavaScript 中的原型对象 prototype,帮助你深入理解其原理及应用场景。
相关问题与解答
1、问题:在 JavaScript 中,什么是原型链?
解答:原型链是一种将对象串联起来的机制,形成了一条查找路径,当访问一个对象的属性或方法时,如果该对象本身没有定义该属性或方法,JavaScript 引擎会沿着原型链逐级向上查找,直到找到该属性或方法或到达原型链的终点(null),这种查找过程的顺序就是原型链的结构。
2、问题:如何使用Object.create()
方法创建一个新对象?
解答:Object.create()
方法接受两个参数:一个原型对象和一个包含属性描述符的对象字面量,通过Object.create()
,你可以创建一个新对象,其原型被设置为第一个参数指定的对象。<br>const obj = Object.create(null);<br>obj.prop = 'value';<br>console.log(obj.prop); // 输出: value<br>在这个例子中,我们使用Object.create(null)
创建了一个新对象,并将它的prop
属性设置为 'value',由于我们没有指定任何原型,所以这个对象没有任何内置的方法或属性。
以上内容就是解答有关“javascript学习笔记(九)javascript中的原型(prototype)及原型”的详细内容了,我相信这篇文章可以为您解决一些疑惑,有任何问题欢迎留言反馈,谢谢阅读。