JS原型相关知识
什么是原型
在JS中,每个构造函数都有一个prototype属性,这个属性的值是一个对象,这个对象包含了构造函数所有实例共享的属性和方法,这个属性就是原型。
下面的图片深层剖析了当你new一个对象时所发生的4个步骤
图中的[[ProtoType]]这里加双括号是EMA262规范里面规定的,用来表示特性的一种方法,这里就是指,对象实例具有原型这个特性
在调用时,我们一般把他写作__proto__
了解完原型,我们来了解原型链
原型链
原型链其实就是通过原型链组成的一条查找链
当我们用new来创建一个对象实例后,对象有一个prototype指向原型。 访问该对象的方法和属性时,先在该实例上寻找,如果找不到,就在原型上找,还找不到就返回undefined。
function Animal() {this.legNum = 4
}
Animal.prototype.run = function () {console.log('prototype, i am running')
}
const a1 = new Animal()
// 打印a1原型的值,是个对象,包含run方法
console.log(Object.getPrototypeOf(a1)) //{ run: [Function (anonymous)] }
a1.run()//打印出prototype, i am running
其实看完上面的内容,如果你有其他语言比如Java基础的话,你就会知道这个很像继承,子类继承父类的方法和属性,在JS里就是对象实例继承原型的属性和原型。这样就很方便我们去理解了。
你可能会想,不对啊,我在ES6里面明明就有写过类啊,可以实现类之间的继承啊,你说错了吧。
其实ES6实现类与类之间的继承的底层原理还是原型:
JS没有像 Java 那样的传统继承机制,主要是因为它的设计哲学和语言模型是基于原型的,而不是基于类的。这种设计使得 JavaScript 更灵活、更动态,适合 Web 开发的需求。虽然 ES6 引入了 class 语法,但它只是对原型继承的一种语法封装,底层仍然是基于原型链的。
显式原型、隐式原型
显式原型:prototype
隐式原型:proto
在js中万物皆对象,方法(Function)是对象,方法的原型(Function.prototype)是对象,对象具有属性(proto)称为隐式原型,对象的隐式原型指向构造该对象的构造函数的显式原型。
方法(Function)是一个特殊的对象,除了和其他对象一样具有__proto__属性以外,它还有一个自己特有的原型属性(prototype),这个属性是一个指针,指向原型对象。原型对象也有一个属性叫constructor,这个属性包含一个指针,指向原构造函数。
如何获取对象的原型值
在浏览器上可以使用__proto__,例如a.proto,但是不推荐
推荐使用Object.getPrototypeOf(a)
因为:
__proto__是早期浏览器引入的非标准属性(尽管后来被 ECMAScript 2015 附录规范收录为可选特性),但它并非语言核心标准的一部分,现代代码应优先使用标准 API。
如果对象原型被恶意修改(例如通过用户输入动态设置 proto),可能导致原型污染攻击(Prototype Pollution),破坏对象预期行为等等
原型的好处
1、节省内存
所有实例共享原型上的方法,而不是每个实例都创建一份副本,如果方法定义在构造函数内部,每个实例都会创建自己的方法副本,而原型方法只需存储一次。
2、动态修改
所有实例都能立即访问新增的方法或属性。
3、实现继承
对象可以继承另一个对象的属性和方法,形成原型链(Prototype Chain)。