js中的原型和原型链

2023-05-21 17:32:44 浏览数 (1)

一、原型

1、prototype和constructor

在js中每个函数(非箭头函数,一般关于原型的有关知识我们都只考虑构造函数)都会拥有一个 prototype 属性,该属性值是一个对象,我们称之为原型对象。原型对象上默认会有 constructor 属性,指向该构造函数。创建原型的主要目的是为了对象实例共享属性和方法。

代码语言:javascript复制
function Person() {
	this.name = 'zs'
}
Person.prototype.age = 12;
Person.prototype.getSome = function() {return false}
let p = new Person()
console.log(Person.prototype.constructor === Person) // true
console.log(p) // Person {name: 'zs'}
console.log(p.age) // 12
console.log(p.getSome()) // false

上面代码中实例对象 p 继承了 Person 原型对象上的方法和属性、如果在创建一个 实例对象同样可以继承该构造函数原型上的属性和方法,实现了数据共享。

2、__proto__

每次调用构造函数创造一个新实例,这个实例内部的 [[Prototype]] 指针就会被赋值为构造函数的原型对象。我们可以通过 __proto__ 属性(隐式原型,每个对象都有该属性),访问对象的原型(上面代码有展示出来)。从而实例对象域构造函数之间有了直接的联系。

编辑

代码语言:javascript复制
function Person() {
	this.name = 'zs'
}
Person.prototype.age = 12;
Person.prototype.getSome = function() {return false}
let p = new Person()
console.log(Person.prototype === p.__proto__) // true

二、原型链

原型链:《JavaScript高级程序设计》中的描述是:每个构造函数都有一个原型对象,如果该原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。这就是原型链的基本构想。

代码语言:javascript复制
function Person() {}
console.log(Person.prototype)

在控制台中分析打印出的上面的代码:最外层的对象是 Person.prototype

​编辑

上文中我们说到每个对象都有 __proto__,那么原型对象也不例外。

原型链中的查找机制:实例对象上找不到指定属性,就从该原型对象上找,如果还是找不到就到该原型对象上的原型上去找,。逐层查找,直至查找到原型链的顶端 Object.prototype 它的 __proto 值为 null。

代码语言:javascript复制
// 依次注释掉下面三点下的代码进行调试不难理解原型链
function Person() {
	// 1.先从实例对象自身查找name属性
	// this.name = 'zs'
}
// 2.实例对象没有name属性的话查找该原型对象上的属性
// Person.prototype.name = 'lisi';
// 3.如果还没有就继续查找,直至查找到原型链的最顶端Object.Prototype
Person.prototype.__proto__.name = 'wangwu';
const p = new Person()
console.log(p.__proto__.__proto__.name) // wangwu
console.log(Person.prototype.__proto__ === Object.prototype) // true

三、__proto__扩展

不再推荐使用该特性。虽然一些浏览器仍然支持它,但也许已从相关的 web 标准中移除,也许正准备移除或出于兼容性而保留。请尽量不要使用该特性。

当 Object.prototype.__proto__ 已被大多数浏览器厂商所支持的今天,其存在和确切行为仅在 ECMAScript 2015 规范中被标准化为传统功能,以确保 Web 浏览器的兼容性。为了更好的支持,建议只使用 Object.getPrototypeOf()。

注意点:通过现代浏览器的操作属性的便利性,可以改变一个对象的 [[Prototype]] 属性,这种行为在每一个 JavaScript 引擎和浏览器中都是一个非常慢且影响性能的操作,使用这种方式来改变和继承属性是对性能影响非常严重的,并且性能消耗的时间也不是简单的花费在 obj.__proto__ = ... 语句上,它还会影响到所有继承来自该 [[Prototype]] 的对象,如果你关心性能,你就不应该在一个对象中修改它的 [[Prototype]]。

关于Object.getPrototypeOf()

返回指定对象的原型(内部[[Prototype]]属性的值),参数为要返回其原型的对象。

代码语言:javascript复制
const prototype1 = {};
const object1 = Object.create(prototype1); // 该方法下面有描述

console.log(Object.getPrototypeOf(object1) === prototype1); // true

关于Object.setPrototypeOf()

Object.setPrototypeOf() 方法设置一个指定的对象的原型(即,内部 [[Prototype]] 属性)到另一个对象或 null。

第一个参数是:要设置其原型的对象;第二个参数是:该对象的新原型(一个对象或 null)。

代码语言:javascript复制
function Human(name, level) {
  this.name = name;
  this.level = level;
}

function SuperHero(name, level) {
  Human.call(this, name, level);
}
Human.prototype.getName = function() {
	return this.name;
}
Object.setPrototypeOf(SuperHero.prototype, Human.prototype);
let hero = new SuperHero('zs', 2);
console.log(hero.getName()) // zs

// 注意:如果使用类创建的话可以直接使用 extend 关键字实现继承

Object.create()

Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype),返回一个带着指定原型对象的新对象。

代码语言:javascript复制
let objPrototype = {}
let obj = Object.create(objPrototype)
console.log(obj) // {}

三、关于原型中各种指向问题总结

前置知识:所有构造函数其实里面都有 prototype 和 __proto__ 这两个属性,所以 Object 和 Function 就是一个构造函数(我的理解)可能不严谨但是对于我们理解这里面的指向问题会很有帮助,为什们会有这句观点?因为之前我在看其他相关的文章解释的时候,一会儿是 Object.__proto__,一会儿又是 Object.prototype,有点绕脑子,所以总结出来了上面的观点。

1.关于构造函数的 __proto__属性

任何构造函数的 __proto__ 都是 Function.prototype ,所以 构造函数.__proto__ === Function.prototype

代码语言:javascript复制
function Person() {}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true

2.关于实例对象的 __proto__

实例对象的 __proto__ 指向创建这个实例对象的构造函数,这个知识点大家都应该没得问题

代码语言:javascript复制
let p = new Person()
console.log(p.__proto__ === Person.prototype) // true

3.任何对象都是Object的实例

所以 xxx.prototype.__proto__ === Object.prototype,但是这里要注意Object.prototype 这个最顶级的原型对象,不由任何构造函数创建,其 Object.prototype.__proto__ === null

代码语言:javascript复制
function Person() {}
console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__ === null) // true

以上就是关于原型及原型链的各种问题,希望能够帮助到大家,还有不明白的可以私信我,或者留言,加油!

0 人点赞