__proto__被认为是过时和不推荐使用的,这意味着JaScript规范规定proto必须仅在浏览器环境中受支持。
现代方法是:
Object.create(proto, [descriptors]) —— 利用给定的 proto 作为 [[Prototype]] 和可选的属性描述来创建一个空对象。Object.getPrototypeOf(obj) —— 返回对象 obj 的 [[Prototype]]。Object.setPrototypeOf(obj, proto) —— 将对象 obj 的 [[Prototype]] 设置为 proto。
应该用这些方法代替__proto__。
例如:
let animal = { eats:true };//新建一个对象,让rabbit = object . Create(animal);alert(rabbit . eats);//true alert(object . getprototypeof(rabbit)= = = animal);//true object . set prototypeof(rabbit,{ });//将rabbit的原型修改为{}Object.create有一个可选的第二个参数:属性描述符。我们可以在这里为新对象提供附加属性,如下所示:
let animal = { eats:true };let rabbit = Object.create(animal,{ jumps:{ value:true } });alert(兔子. jumps);//真实描述符的格式与属性标志和属性描述符一章中的格式相同。
我们可以使用Object.create来实现一种比复制..在循环中:
let clone = object . create(object . getprototypeof(obj),object . getownpropertydescriptors(obj));这个调用可以对obj进行真正精确的复制,包括所有的属性:可枚举和不可枚举,数据属性和setter/getter——包括所有带有正确[[Prototype]]的东西。
原型简史
如果我们数一数有多少种处理[[原型]]的方法,答案是很多!很多方法可以做同样的事情!
为什么会这样?
这是历史原因。
构造函数的 "prototype" 属性自古以来就起作用。之后,在 2012 年,Object.create 出现在标准中。它提供了使用给定原型创建对象的能力。但没有提供 get/set 它的能力。因此,许多浏览器厂商实现了非标准的 __proto__ 访问器,该访问器允许用户随时 get/set 原型。之后,在 2015 年,Object.setPrototypeOf 和 Object.getPrototypeOf 被加入到标准中,执行与 __proto__ 相同的功能。由于 __proto__ 实际上已经在所有地方都得到了实现,但它已过时,所以被加入到该标准的附件 B 中,即:在非浏览器环境下,它的支持是可选的。
到目前为止,我们有所有这些方法。
为什么用函数getPrototypeOf/setPrototypeOf代替__proto__呢?这是一个有趣的问题,我们需要了解__proto__为什么不好。继续读下去,你就会知道答案。
如果速度很重要,请不要修改现有对象的[[原型]]。
技术上,我们可以随时获取/设置[[原型]]。但是通常我们在创建一个对象的时候只设置一次,之后就再也不修改了:rabbit继承了animal,之后就再也不修改了。
此外,JaScript引擎为此进行了高度优化。使用Object.setPrototypeOf或obj更改原型。_ _ proto _ = ” immediately “是一个非常慢的操作,因为它破坏了对象属性访问操作的内部优化。所以,除非你知道自己在做什么,或者JaScript的执行速度对你来说完全不重要,否则请避免使用。
"Very plain" objects
我们知道对象可以作为关联数组来存储键/值对。
…..但是如果我们尝试在其中存储用户提供的键(例如,用户输入的字典),我们可以发现一个有趣的小故障:除了& # 34;_ _ proto _ _ & # 34。
看看这个例子:
设obj = { };let key = prompt(& # 34;什么& # 39;关键是什么?", "_ _ proto _ _ & # 34);obj[key]= & # 34;有些价值& # 34;;alert(obj[key]);// [object Object],而不是& # 34;有些价值& # 34;!如果用户输入__proto__,赋值将被忽略!
我们不应该对此感到惊讶。__proto__属性是特殊的:它必须是对象或null。字符串不能是原型。
但是我们不会实现这种行为,对吗?我们想存储键值对,但是键名是& # 34;_ _ proto _ _ & # 34的键值对存储不正确。所以这是个bug。
在这里,后果不是很严重。但在其他情况下,我们可能会给对象赋值,然后可能会改变原型。结果可能会导致完全意想不到的结果。
最可怕的是——通常开发者根本不考虑这个。这使得这样的bug很难被发现,甚至成为漏洞,尤其是在服务器上使用JaScript的时候。
将值赋给toString(默认情况下是一个函数)和其他内置方法也会产生意外的结果。
怎样才能避免这样的问题?
首先,我们可以用Map代替普通对象进行存储,这样一切都会迎刃而解。
但是Object在这里也能很好的运行,因为JaScript语言的制作者很早就注意到了这个问题。
__proto__不是对象的属性,而是object的访问器属性。prototype:
因此,如果obj。__proto__被读取或赋值,相应的getter/setter将从其原型中调用,它将set/get [[Prototype]]。
正如本部分教程开头所说:__proto__是一种访问[[prototype]]的方法,而不是[[Prototype]]本身。
现在,我们想用一个对象作为关联数组,摆脱这样的问题。我们可以使用一些提示:
let obj = object . create(null);let key = prompt(& # 34;什么& # 39;关键是什么?", "_ _ proto _ _ & # 34);obj[key]= & # 34;有些价值& # 34;;alert(obj[key]);// "有些价值& # 34;Object.create(null)创建一个空对象,该对象没有原型([[Prototype]]为null):
因此,它不继承__proto__的getter/setter方法。现在,它被当作一个普通的数据属性,所以上面的例子可以正常工作。
我们可以称这样的对象为“非常简单”或“纯字典”,因为它们比普通的简单对象更简单…}.
缺点是这样的对象没有任何内置的对象方法,比如toString:
let obj = object . create(null);alert(obj);//错误(nottostring)…但是它们通常对关联数组很友好。
请注意,大多数与对象相关的方法都是Object.something(…),比如object . keys(obj)——它们不在prototype中,所以仍然可以用在“非常普通”的对象中:
let Chinese dictionary = object . create(null);chineseDictionary.hello = & # 34你好& # 34;;chineseDictionary.bye = & # 34再见& # 34;;alert(Object.keys(中文字典));// hello,bye总结设置和直接访问原型的现代方法有:
Object.create(proto, [descriptors]) —— 利用给定的 proto 作为 [[Prototype]](可以是 null)和可选的属性描述来创建一个空对象。Object.getPrototypeOf(obj) —— 返回对象 obj 的 [[Prototype]](与 __proto__ 的 getter 相同)。Object.setPrototypeOf(obj, proto) —— 将对象 obj 的 [[Prototype]] 设置为 proto(与 __proto__ 的 setter 相同)。
如果你想把一个用户生成的键放入一个对象,内置的__proto__ getter/setter是不安全的。因为用户可以输入& # 34;_ _ proto _ _ & # 34作为一个键,这将导致一个错误。虽然我们希望这个问题不会有什么大的影响,但它通常会产生不可预知的后果。
因此,我们可以使用Object.create(null)创建一个“非常普通”的对象,而不需要__proto__,或者我们可以针对这类场景坚持使用Map对象。
此外,Object.create提供了一种简单的方法来轻松复制对象的所有描述符:
let clone = object . create(object . getprototypeof(obj),object . getownpropertydescriptors(obj));此外,我们还明确了__proto__是[[Prototype]]的getter/setter,和其他方法一样,它位于Object.prototype中。
我们可以通过Object.create(null)创建一个没有原型的对象。这样的对象被用作“纯字典”,对于它们来说,& # 34;_ _ proto _ _ & # 34作为一把钥匙没有问题。
其他方法:
Object.keys(obj) / Object.values(obj) / Object.entries(obj) —— 返回一个可枚举的由自身的字符串属性名/值/键值对组成的数组。Object.getOwnPropertySymbols(obj) —— 返回一个由自身所有的 symbol 类型的键组成的数组。Object.getOwnPropertyNames(obj) —— 返回一个由自身所有的字符串键组成的数组。Reflect.ownKeys(obj) —— 返回一个由自身所有键组成的数组。obj.hasOwnProperty(key):如果 obj 拥有名为 key 的自身的属性(非继承而来的),则返回 true。
所有返回对象属性的方法(比如Object.keys等)——都返回“self”属性。如果我们想继承它们,我们可以使用for…英寸
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。