学习js的过程中我们绕不开两链一包的学习,尤其是在面试中,关于作用域链,前些时间分析过了,有兴趣的话可以关注一下关于JS中的作用域中的沉思,这篇文章主要是关注原型链性质特点
常言道他山之石可以攻玉,原型的概念在其他的语言中可能不存在,但相似的原理是存在的,比如python
中的基类的老爹——元类
MyClass = MetaClass() # 使用元类创建出一个对象,这个对象称为“类”
my_object = MyClass() # 使用“类”来创建出实例对象
之前学习他的时候,Tim Peters这老头这么说到,“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。”
哦,对于python来说元类是需要用到时候一定是已经理解了,用不到的时候说明是不理解,唔,我愿称之为薛定谔の学习,但不影响我们互相印证学习。
我们创建的每个函数都有一个prototype(原型)属性,prototype
属性指向原型对象。通过该函数创建的实例对象会共享原型对象上的所有属性和方法。
构造如图所示
new
方法来获得实例对象.prototype
属性来获得原型对象constructor
属性来获得构造函数.__proto__
和object.getPrototypeOf()
来获得原型对象constructor
属性来获得构造函数当我们通过一系列的继承并实例化,在JavaScript中我们如果需要进行对象属性的检索,通过在这个继承链上一步步追溯寻找原型的过程称之为原型链
且看原型链的工作过程:
Object.prototype
还是没有,那么返回undefined
其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。然后层层递进,就构成了实例与原型的链条,这就是所谓原型链的基本概念。
talk is cheap,show the code
不妨设计一套代码
function Person(){
}
Person.prototype.name = 'naug';
Person.prototype.age = 18;
Person.prototype.sayName = function(){
console.log(this.name);
}
const person1 = new Person();
person1.sayName(); //'naug'
const person2 = new Person();
person2.sayName(); //'naug'
console.log(person1.sayName === person2.sayName); //true
要理解原型链我们必须理解三个名词:
__proto__
属性,例如arr.__proto__
prototype
属性,例如:func.prototype
prototype
属性的对象,在定义函数时被创建基于这三个关键词我们再展开之后的原型链之间的递归关系可以如下图所示
在JavaScript中只要创建了一个函数,就会为该函数生成一个prototype属性(指向函数的原型对象)。默认情况下,原型对象会自动获得一个constructor属性(构造函数属性),这个属性包含一个指向prototype属性所在函数的指针。当调用该函数创建一个实例后,该实例内部将包含一个指针Prototype
(内部属性),指向构造函数的原型对象。虽然在脚本中没有标准的方式访问Prototype
,但Firefox、Safari和Chrome在每个对象上都支持一个属性__proto__
。而在其他实现中,这个属性对脚本则是完全不可见的。
虽然在所有实现中都无法访问到Prototype
,但可以通过isPrototypeOf方法来确定对象之间是否存在这种关系。如果Prototype
指向调用isPrototypeOf()
方法的对象(Person.prototype),那么这个方法就返回true。
Object.getPrototypeOf方法可以返回Prototype
的值。例如:
每当读取对象的某个属性时,都会首先从对象实例本身开始搜索,如果没有则继续搜索原型对象中的属性。虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。
person2.name = "yerik";
console.log(person1.name); // "yerik" 来自实例本身
console.log(person2.name); // "naug" 来自原型对象
hasOwnProperty方法可以检测一个属性是存在于实例中,还是存在于原型中(从Object继承来的),只在给定属性存在于对象实例中时,才会返回true。
当一个变量在调用某方法或属性时,如果当前变量并没有该方法或属性,就会在该变量所在的原型链中依次向上查找是否存在该方法或属性,如果有则调用,否则返回undefined
在开发中,常常会用到toString()
、valueOf()
等方法,array
类型的变量拥有更多的方法,例如forEach()
、map()
、includes()
等等。例如声明了一个arr
数组类型的变量,arr
变量却可以调用如下图中并未定义的方法和属性。
通过变量的隐式原型可以查看到,数组类型变量的原型中已经定义了这些方法。例如某变量的类型是Array
,那么它就可以基于原型链查找机制,调用相应的方法或属性。
原型中所有属性是被很多实例共享的。对于包含引用类型值的属性的原型对象,会有特别的问题。在实例中修改了原型上的引用类型的属性,会在所有实例上反映出来
这个其实也就会引出原型链污染的问题,具体的案例实践,不妨看这篇文章利用原型链漏洞污染拿下服务器权限
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。