1。在原型上
保存方法 例如,不使用原型进行Javascript编码是完全可行的:
功能的
用户(姓名、passwordhash){
this.name =名称;
this.passwordhash = passwordhash;
this.tostring =
函数(){
返回{用户+ this.name +};
};
this.checkpassword =功能(
密码){
返回哈希(密码)= this.passwordhash;
};
}
VaR U1 =新(用户 / *…**;
var =新的(用户…**;
VaR U3 =新(用户 / *…**;
创建多种用户
类型的实例时,有一个问题:不仅
名字和passwordhash
属性存在于每一个实例,但ToString和checkPassword方法有
复制在每一个实例,如下面的图所示:
但当ToString和checkPassword定义原型,上面的图像就变成下面:
toString方法,现在user.prototype checkPassword定义的对象,这意味着只有一个拷贝,这两种方法的存在,它是由所有用户共享实例。
也许您会认为将每个实例作为副本保存一个副本可以节省方法
查询的时间(当该方法在原型上定义时,该方法首先在实例本身上找到,如果没有找到,它将在原型上继续)。
但在现代Javascript
执行引擎中,该方法的查询已经进行了大量
优化。因此查询时间几乎不需要考虑。因此,在原型对象上保存方法可以节省大量
内存。
两。使用闭包保存私有数据
Javascript的对象
系统不鼓励使用信息隐藏(隐藏)从其语法。因为使用this.name,this.passwordhash时,这些属性的
默认访问级别是公开的,可以通过obj.name和obj.passwordhash在任何
位置。
在ES5的环境,提供更方便的访问一个对象的所有属性,也有一些方法,如Object.keys()、Object.getOwnPropertyNames(),因此,一些开发商利用某些协议的定义例如Javascript对象的私有属性,最典型的是使用下划线属性告诉其他
开发者和用户,这个属性不应直接访问前缀。
但这并不能从根本上
解决问题,其他开发人员和用户仍然可以直接使用带有下划线的属性访问。
从某种意义上说,在Javascript中,对变量的访问
策略和对象的访问策略的
关闭是两个极端。闭包中的任何变量
都是私有的,只有在函数中才能访问变量:
功能的用户(姓名、passwordhash){
this.tostring =函数(){
返回{用户+名称;
};
this.checkpassword =功能(密码){
返回哈希(密码)= passwordhash;
};
}
在这一点上,名称和passwordhash都没有保存的实例的属性,而是由局部变量保存。然后,根据关闭的访问规则,在实例中的方法可以访问,但不能在其他地方。
采用这种
模式的一个缺点是,使用局部变量的方法需要对实例本身的定义,而这些方法不能对原型对象的定义,讨论item34,这样做的问题是增加内存消耗。但在一些特殊的
情况下,在一个实例定义的方法可行是也。
三。实例状态仅保存在实例对象上。
一种原型是一对与这种类型的实例多关系。因此,需要确保数据相关的例子将不被保存在原型的
错误。例如,一个树型结构,保存它的子节点上的原型是不
正确的。
函数树(x){
this.value = x;
}
tree.prototype = { {
子:{,应该是实例状态!
给:函数(x){
This.children.push(X);
}
};
var =新树(2);
(1)left.addchild;
(3)left.addchild;
var =新树(6);
(5)right.addchild;
(7)right.addchild;
新树(4);
top.addchild(左);
top.addchild(右);
top.children; / / { 1, 3, 5,7 },左,右
当状态被保存到原型时,所有实例的状态将被集中保存,这在上面的场景中显然是不正确的:属于每个实例的状态被错误地共享:
正确的实现应该是:
函数树(x){
this.value = x;
this.children = {}; / /实例的状态
}
tree.prototype = { {
给:函数(x){
This.children.push(X);
}
};
此时,实例状态的存储如下所示:
可以看出,当实例的状态在原型上共享时,这可能是一个问题。在您需要在原型上保存状态属性之前,必须确保属性可以共享。
一般来说,当一个属性是不可变的(状态)的属性,它可以保存在原型对象(例如,该方法可以保存在原型对象)。当然,状态属性也可以放在原型对象,这取决于特定的应用场景,如用来记录的类型实例变量数。使用java
语言作为一个比喻,这类变量可以存储在原型对象在java类变量(用static关键字修饰)。
四。避免标准继承类型
ECMAscript标准库不是很大,但它提供了一些重要的类型如数组、函数和
日期。在某些情况下,你可以考虑继承的类型之一,以实现特定的功能,但这并不是鼓励。
例如,为了
操作目录,可以让目录类型继承数组类型如下所示:
函数Dir(
路径,条目){
this.path =路径;
对于(var i = 0,n = entries.length;i < n;i++){
此{ = } =条目{ };
}
}
dir.prototype = object.create(阵列。原型);
扩展数组
var目录=新目录( / / TMP的首页
dir.length / / 0;
但可以
发现,dir.length值是0,不是3的期望。
这样做的
原因是,当对象是真正的数组类型时,长度属性才会起
作用。
在ECMAscript标准,一个看不见的内部属性定义为{ {类} }。这个属性的值是一个字符串,所以不要被误导认为Javascript也实现了它自己的类型系统,为数组类型,此属性的值是数组的函数式;此属性的值的函数。下表是所有{ {类} }值定义的ECMAscript:
当对象的类型是数组的长度属性是特殊的:长度的值将在对象索引属性的数量一致。例如,一个对象数组arr数组{ 0 }和{ 1 }表明数组的对象有两个索引属性,那么长值是2。当ARR { 2 }增加长度的值会自动
同步到3。同样,当长度的值
设置为2,ARR { 2 }自动设置为未定义。
但是,当数组类型被继承并创建一个实例时,实例的{ } }属性不是数组,而是对象,所以长度属性不能正常
工作。
在Javascript中,一种方法查询{ {类} }特性还提供,即使用object.prototype.tostring方法:
新目录()
object.prototype.tostring.call(DIR); / / {物}
object.prototype.tostring.call({ }); / / } {对象数组
因此,更好的实现方法是使用组合而不是继承:
函数Dir(路径,条目){
this.path =路径;
this.entries =条目; / /数组属性
}
dir.prototype.foreach =函数(F,thisarg){
如果(typeof thisarg =未定义){
这thisarg =;
}
this.entries.foreach(F,thisarg);
};
上面的代码将不再使用继承,而是将函数
代理的一部分给内部条目属性,这是一个数组类型对象。
在ECMAscript标准库,大多数的建设者依靠内部的属性值,如{ {类} },达到正确的行为。对于亚型,继承这些标准类型,也不可能保证自己的行为是正确的。因此,不继承类型在ECMAscript标准库,这样作为:
阵列、布尔值、日期、数量、功能、正则表达式、字符串
以上就是使用原型来总结几点注意事项,希望能帮助您正确使用原型。