开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情
前言
JavaScript
在设计之初只是一个提供给非专业的开发人员使用的工具,所以为了尽可能的让这个工具
简单易学,并没有直接引入类
的概念,而是借鉴了Self
和 Smalltalk
这两门基于原型
的语言。
因此,原型和原型链成为 JavaScript
这门语言最大的一个特点。
概述
- 所有的函数都是对象
- 函数中可以拥有自定义属性
- 所有的对象都是通过
new 函数
创建的
prototype
- 所有函数都有一个属性:
prototype
,叫做函数原型 - 默认情况下,
prototype
是一个普通的Object
对象 - 默认情况下,
prototype
中有一个属性,constructor
,它也是一个对象,它指向构造函数本身
代码示例:
function Person() {}
console.log(Person.prototype); // {constructor: f}
console.log(Person.prototype.constructor === Person); // true
图示:
__proto__
已弃用: 不再推荐使用该特性 --- MDN
该特性可以通过 `Object.prototype` 身上的一些方法实现,这里记录一下,便于理解原型及原型链的概念。
- 所有对象都有一个属性:
__proto__
,叫做:隐式原型
- 默认情况下,对象的
__proto__
属性指向创建该对象的函数的原型对象
代码示例:
function Person() {}
const p = new Person();
console.log(p.__proto__); // {constructor: f}
console.log(p.__proto__.constructor === Person); // true
// 实例 p 的 __proto__ 属性 等于 构造函数 Person 的 prototype(函数原型对象)
console.log(p.__proto__ === Person.prototype); // true
图例:
原型链
当访问对象的某个属性时:
- 先查看对象本身是否存在该属性,存在则直接使用
- 对象本身不存在,则依次查找对象的原型上是否存在该属性
- 若对象的原型都不存在该属性,则报错
注:这里的依次查看对象原型,指的是顺着对象的原型对象一直找,并不是只查找[对象].proptotype或者[对象].__proto__(看代码和图例会比较好理解)
function Person() {}
Person.prototype.say = function () {
console.log('Hello World!');
}
// 在 Function 的原型上定义 func 方法,根据原型链全貌,访问对象 p 时是不会访问到 Function.prototype 的
Function.prototype.func = function() {
console.log('Function prototype func');
}
const p = new Person();
console.log(p); // Person {}
// 访问 say 函数时:
// 先查找 p 自身,不存在 say 方法
// 再查看 p.__proto__,即:Person.prototype, 存在 say 方法,执行
p.say(); // Hello World!
// 访问 func 函数时:
// 先查找 p 自身,不存在 func 方法
// 再查看 p.__proto__,即:Person.prototype, 不存在 func 方法
// 继续查找 p.__proto__.__proto__,即 Object.prototype,不存在 func 方法
// 再查找 p.__proto__.__proto__.__proto__ , 为 null,依旧找不到 func 方法,报错
p.func(); // p.func is not a function
原型链全貌:
测试题:
function User() {}
User.prototype.sayHello = function() {}
var u1 = new User();
var u2 = new User();
console.log('1. ', User.prototype.constructor); // 1. User {}
console.log('2. ', u1.sayHello === u2.sayHello); // 2. true
// Object.prototype === Object.prototype true
console.log('3. ', Function.prototype.__proto__ === Object.prototype); // 3. true
// Function.prototype === Function.prototype true
console.log('4. ', User.__proto__ === Function.__proto__); // 4. true
// User.prototype === Object.prototype false
console.log('5. ', u1.__proto__ === User.__proto__); // 5. false
// Function.prototype === Function.prototype true
console.log('6. ', User.__proto__ === Function.prototype); // 6. true
// User.prototype === Function.prototype false
console.log('7. ', User.prototype === Function.prototype); // 7. false
// User.prototype === User.prototype true
console.log('8. ', u1.__proto__ === u2.__proto__); // 8. true
// Object.prototype === null false
console.log('9. ', Function.prototype.__proto__ === Object.prototype.__proto__); // 9. false
// Function.prototype === Function.prototype true
console.log('10. ', Function.__proto__ === Object.__proto__); // 10. true
原型及原型链相关知识
Object.setPrototypeOf()
方法设置一个指定的对象的原型(即,内部[[Prototype]]
属性)到另一个对象或null
。可以替代[object].__proto__ = xxx
。
建议使用
Object.create
创建一个新对象,而不是直接修改某个对象的原型。如果你担心性能问题,则应该避免设置对象的
[[Prototype]]
。相反,你应该使用Object.create()
来创建带有你想要的[[Prototype]]
的新对象。 --- MDN
-
Object.create()
方法用于创建一个新对象
,使用现有的对象
来作为新创建对象的原型
, -
Object.getPrototypeOf()
方法返回指定对象的原型(内部[[Prototype]]
属性的值),可以替代[object].__proto__