JS高级–对象原型
前言
通过原型的机制,js中的对象从其他对象中继承功能特性;这种继承机制与经典的继承机制不同,本文将探讨这些差别,解释原型链的功能,并了解如何让通过prototype属性向已有的构造器添加方法。
一、JS?基于原型的语言?
js通常被描述为一种基于原型的解释性语言—即每个对象都拥有一个原型对象。原型对象也可能拥有原型,并从中继承方法和属性,一层一层以此类推。这种关系被称之为原型链,他解释了为什么一个对象为什么会拥有定义在其他对象中的方法和属性。(准确的说这些属性和方法是定义在Object的构造函数之上的prototype属性上的,而不是对象实例本身)
在传统的OOP中(class继承),首先定义类,此后在实例化对象时类中的属性和方法都被赋值到实例中。但是在js中并不是如此复制-----而是在对象实例和它的构造器之间建立一个连接(_proto_属性)之后通过上溯原型链,在构造器中找到这些属性和方法。
二、使用JS中的原型
function my_function(){}
console.log(my_function.prototype);
var my_function=function(){};
console.log(my_function.prototype);
运行后可以看到,my_function函数有一个默认的原型属性。它是这样的:
{constructor: ƒ}
constructor: ƒ my_function()
arguments: null
caller: null
length: 0
name: "my_function"
prototype: {constructor: ƒ}
[[FunctionLocation]]: VM273:1
[[Prototype]]: ƒ ()
[[Scopes]]: Scopes[1]
[[Prototype]]: Object
现在可以添加一些属性到my_function的原型上面,例如:
function my_function(){}
console.log(my_function.prototype);
var my_function=function(){};
my_function.prototype.foo="js";
console.log(my_function.prototype);
结果:
{foo: "js", constructor: ƒ}
foo: "js"
constructor: ƒ ()
[[Prototype]]: Object
使用new在这个原型基础上实例化一个对象,然后就可以在这个新的对象上添加属性了
var my_function=function(){};
my_function.prototype.foo="js";
var sub_function=new my_function();
sub_function.prop="sub_js"
console.log(sub_function);
结果:
prop: "sub_js"
[[Prototype]]: Object
foo: "js"
constructor: ƒ ()
[[Prototype]]: Object
默认情况下,所有函数的原型属性的_proto_就是window.Object,prototype.所以sub_function的_proto_的_proto_(也就是my_function.prototype的_proto_(也即是Object.protoptype))会被查找是否有这个属性,如果没有找到,然后就会在my_function的_proto_的_proto_的_proto_里面查找。从这个例子来看,事实上my_function的_proto_的_proto_的_proto_是不存在的,此时会得出结论:这个属性是undefined
三、理解原型对象
为了更好的理解原型对象,使用简单的例子加以说明:
function person(first,last,age,gender,interest){
//定义属性与方法
}
var person1 = new person('lili','wang',21,'male',['music','soccer']);//创建对象实例
当使用***person1.***时会发现含有两类成员,它们是:
person()构造器的成员—name,gender,age,inerest.
定义在person()构造器原型对象(object)上的成员—watch,valueOf,等
此时:对应的原型链的运行机制就应该是:
person1——(inherits from prototype)——>person——(inherits from prototype)——>Object
值得注意的是,如果调用person1的实际定义在object上的方法时,会发生什么?比如:
person1.valurOf()
这个方法仅仅返回了被调用对象的值。浏览器首先检查person1对象是否具有可用的valueOf方法,再沿着原型链的方向去寻找,直到Object构造函数的prototype属性所指的对象。
四、prototype属性:继承成员被定义的地方
(1)继承的属性和方法是定义在prototype属性之上的,prototype属性的值是一个对象,被原型链下游的对象继承的属性和方法,都被储存在其中。举例来说:
Object.prototype.watch(),适用于任何继承自Object的对象类型,包括new出来的对象实例。
而Object.watch(),仅能被Object()构造器本身使用。
注意!!!
访问原型对象应当使用_proto_.而不是this关键字
(2)一般情况,很少将属性定义在prototype属性中,比如:
person.prototype.full_name=this.name.first+' '+this.name.last;
但是,这种方法必须放在函数内部,这里涉及到this指向的问题,例子中的this指向的全局范围,结果返回的是两个***undefined***所以在函数体内部直接定义属性是一般做法。而方法一般定义在prototype属性上,代码更具有可读性。例如:
//定义方法一
person.prototype.x=function(){...}
//定义方法二
person.prototype.y=function(){...}
另外,每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。
五、如何修改原型
function person(first,last,gender,interest){
//基于函数构建对象
};
var person1 = new person('lili','wang',21,'male',['music','soccer']);
person.prototype.farewell=function(){
//定义新方法
}
此时person1{}也可以使用farewell()方法,以及后面沿着此条原型链实例化之后的对象。
总结
本次介绍了JS对象原型,包括原型链如何允许对象之间继承特性,prototype属性,如何通过它来向构造器中添加方法以及其他相关主题。
1885

被折叠的 条评论
为什么被折叠?



