最新文�? 原创 : ubuntu16.04怎样才能安装 wxpython 2.8.12.1 转载 : gerrit安装与配置 原创 : RobotFramework框架系列 – 介绍篇 原创 : RobotFramework框架系列 – 运行参数篇 原创 : RobotFramework框架系列 – TAG选择运行篇
javascript 历史版本:
上次修改时间:
继承与原型链 历史版本:
上次修改时间:
{ b: 3, c: 4 } ---> Object.prototype ---> null\n\nconsole.log(o.a); // 1\n// o 上有自有属性“a”吗?有,且其值为 1。\n\nconsole.log(o.b); // 2\n// o 上有自有属性“b”吗?有,且其值为 2。\n// 原型也有“b”属性,但其没有被访问。\n// 这被称为属性遮蔽(Property Shadowing)\n\nconsole.log(o.c); // 4\n// o 上有自有属性“c”吗?没有,检查其原型。\n// o.[[Prototype]] 上有自有属性“c”吗?有,其值为 4。\n\nconsole.log(o.d); // undefined\n// o 上有自有属性“d”吗?没有,检查其原型。\n// o.[[Prototype]] 上有自有属性“d”吗?没有,检查其原型。\n// o.[[Prototype]].[[Prototype]] ?? Object.prototype 且\n// 其默认没有“d”属性,检查其原型。\n// o.[[Prototype]].[[Prototype]].[[Prototype]] ?? null,停止搜索,\n// 未找到该属性,返回 undefined。\n\n```\n\n给对象设置属性会创建自有属性。获取和设置行为规则的唯一例外是当它被 getter ?? setter 拦截时??
\n\n同理,你可以创建更长的原型链,并在原型链上查找一个属性??
\n\n\n```\nconst o = {\n a: 1,\n b: 2,\n // __proto__ 设置?? [[Prototype]]。它在这里被指定为另一个对象字面量。\n __proto__: {\n b: 3,\n c: 4,\n __proto__: {\n d: 5,\n },\n },\n};\n\n// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> { d: 5 } ---> Object.prototype ---> null\n\nconsole.log(o.d); // 5\n\n```\n\n#### 继承方法\n\nJavaScript 并没有其他基于类的语言所定义的“方法”。在 JavaScript 中,任何函数都被可以添加到对象上作为其属性。函数的继承与其他属性的继承没有差别,包括上面的“属性遮蔽”(这种情况相当于其他语言的方法重写)??
\n\n当继承的函数被调用时,this 值指向的是当前继承的对象,而不是拥有该函数属性的原型对象??
\n\n```\nconst parent = {\n value: 2,\n method() {\n return this.value + 1;\n },\n};\n\nconsole.log(parent.method()); // 3\n// 当调?? parent.method 时,“this”指向了 parent\n\n// child 是一个继承了 parent 的对象\nconst child = {\n __proto__: parent,\n};\nconsole.log(child.method()); // 3\n// 调用 child.method 时,“this”指向了 child。\n// 又因?? child 继承的是 parent 的方法,\n// 首先?? child 上寻找“value”属性。但由于 child 本身\n// 没有名为“value”的自有属性,该属性会在\n// [[Prototype]] 上被找到,即 parent.value。\n\nchild.value = 4; // ?? child,将“value”属性赋值为 4。\n// 这会遮蔽 parent 上的“value”属性。\n// child 对象现在看起来是这样的:\n// { value: 4, __proto__: { value: 2, method: [Function] } }\nconsole.log(child.method()); // 5\n// 因为 child 现在拥有“value”属性,“this.value”现在表示\n// child.value\n\n```\n\n#### 构造函数\n原型的强大之处在于,如果一组属性应该出现在每一个实例上,那我们就可以重用它们——尤其是对于方法。假设我们要创建多个盒子,其中每一个盒子都是一个对象,包含一个可以通过 getValue 函数访问的值。一个简单的实现可能是:
\n\n```\nconst boxes = [\n { value: 1, getValue() { return this.value; } },\n { value: 2, getValue() { return this.value; } },\n { value: 3, getValue() { return this.value; } },\n];\n\n```\n\n这是不够好的,因为每一个实例都有自己的,做相同事情的函数属性,这是冗余且不必要的。相反,我们可以?? getValue 移动到所有盒子的 [[Prototype]] 上:
\n\n```\nconst boxPrototype = {\n getValue() {\n return this.value;\n },\n};\n\nconst boxes = [\n { value: 1, __proto__: boxPrototype },\n { value: 2, __proto__: boxPrototype },\n { value: 3, __proto__: boxPrototype },\n];\n\n```\n这样,所有盒子的 getValue 方法都会引用相同的函数,降低了内存使用率。但是,手动绑定每个对象创建?? __proto__ 仍旧非常不方便。这时,我们就可以使用构造函数,它会自动为每个构造的对象设置 [[Prototype]]。构造函数是使用 new 调用的函数。\n
\n\n```\n// 一个构造函数\nfunction Box(value) {\n this.value = value;\n}\n\n// 使用 Box() 构造函数创建的所有盒子都将具有的属性\nBox.prototype.getValue = function () {\n return this.value;\n};\n\nconst boxes = [new Box(1), new Box(2), new Box(3)];\n\n```\n\n我们?? new Box(1) 是通过 Box 构造函数创建的一个实例。Box.prototype 与我们之前创建的 boxPrototype 并无太大区别——它只是一个普通的对象。通过构造函数创建的每一个实例都会自动将构造函数的 prototype 属性作为其 [[Prototype]]。即,Object.getPrototypeOf(new Box()) === Box.prototype。Constructor.prototype 默认具有一个自有属性:constructor,它引用了构造函数本身。即,Box.prototype.constructor === Box。这允许我们在任何实例中访问原始构造函数。\n上面的构造函数可以重写为类:
\n\n\n```\nclass Box {\n constructor(value) {\n this.value = value;\n }\n\n // ?? Box.prototype 上创建方法\n getValue() {\n return this.value;\n }\n}\n\n```\n类是构造函数的语法糖,这意味着你仍然可以修?? Box.prototype 来改变所有实例的行为。然而,由于类被设计为对底层原型机制的抽象,我们将在本教程中使用更轻量级的构造函数语法,以充分展示原型的工作原理。\n
\n因为 Box.prototype 引用了(作为所有实例的 [[Prototype]] 的)相同的对象,所以我们可以通过改变 Box.prototype 来改变所有实例的行为??
\n\n\n```\nfunction Box(value) {\n this.value = value;\n}\nBox.prototype.getValue = function () {\n return this.value;\n};\nconst box = new Box(1);\n\n// 在创建实例后修改 Box.prototype\nBox.prototype.getValue = function () {\n return this.value + 1;\n};\nbox.getValue(); // 2\n\n```\n\n有个推论是:
\n重新赋?? Constructor.prototype(Constructor.prototype = ...)是一个不好的主意,原因有两点??
\n\n- 在重新赋值之前创建的实例?? [[Prototype]] 现在引用的是与重新赋值之后创建的实例?? [[Prototype]] 不同的对象——改变一个的 [[Prototype]] 不再改变另一个的 [[Prototype]]。\n- 除非你手动重新设?? constructor 属性,否则无法再通过 instance.constructor 追踪到构造函数,这可能会破坏用户期望的行为。一些内置操作也会读?? constructor 属性,如果没有设置,它们可能无法按预期工作。\n\n\nConstructor.prototype 仅在构造实例时有用。它?? Constructor.[[Prototype]] 无关,后者是构造函数的自有原型,即 Function.prototype。也就是说,Object.getPrototypeOf(Constructor) === Function.prototype。\n\n字面量的隐式构造函数JavaScript 中的一些字面量语法会创建隐式设?? [[Prototype]] 的实例。例如:
\n\n\n```\n// 对象字面量(没有 `__proto__` 键)自动将\n// `Object.prototype` 作为它们?? `[[Prototype]]`\nconst object = { a: 1 };\nObject.getPrototypeOf(object) === Object.prototype; // true\n\n// 数组字面量自动将 `Array.prototype` 作为它们?? `[[Prototype]]`\nconst array = [1, 2, 3];\nObject.getPrototypeOf(array) === Array.prototype; // true\n\n// 正则表达式字面量自动?? `RegExp.prototype` 作为它们?? `[[Prototype]]`\nconst regexp = /abc/;\nObject.getPrototypeOf(regexp) === RegExp.prototype; // true\n\n```\n\n我们可以将它们“解糖(de-sugar)”为构造函数形式。\n\n```\nconst array = new Array(1, 2, 3);\nconst regexp = new RegExp(\"abc\");\n\n```\n\n例如,像 map() 这样的“数组方法”只是在 Array.prototype 上定义的方法,而它们又自动在所有数组实例上可用,就是因为这个原因??
\n\n\n有一个常见的错误实践(misfeature):扩展 Object.prototype 或其它内置原型。这种不良特性例子是,定?? Array.prototype.myMethod = function () {...},然后在所有数组实例上使用 myMethod。\n
\n这种错误实践被称为猴子修补(monkey patching)。使用猴子修补存在向前兼容的风险,因为如果语言在未来添加了此方法但具有不同的签名,你的代码将会出错。它已经导致了类似于 SmooshGate 这样的事件,并且由于 JavaScript 致力于“不破坏 web”,因此这可能会对语言的发展造成极大的麻烦。\n
\n扩展内置原型的唯一理由是向后移植新?? JavaScript 引擎的特性,比如 Array.prototype.forEach??
\n\n有趣的是,由于历史原因,一些内置构造函数的 prototype 属性本身就是其自身的实例。例如,Number.prototype 是数?? 0,Array.prototype 是一个空数组,RegExp.prototype ?? /(?:)/。\n
\n\n```\nNumber.prototype + 1; // 1\nArray.prototype.map((x) => x + 1); // []\nString.prototype + \"a\"; // \"a\"\nRegExp.prototype.source; // \"(?:)\"\nFunction.prototype(); // Function.prototype 本身就是一个无操作函数\n\n```\n\n然而,对于用户定义的构造函数,以及 Map 等现代的构造函数,则并非如此。\n\n```\nMap.prototype.get(1);\n// Uncaught TypeError: get method called on incompatible Map.prototype\n\n```\n\n#### 构建更长的继承链\nConstructor.prototype 属性将成为构造函数实例的 [[Prototype]],包?? Constructor.prototype 自身?? [[Prototype]]。默认情况下,Constructor.prototype 是一个普通对象——即 Object.getPrototypeOf(Constructor.prototype) === Object.prototype。唯一的例外是 Object.prototype 本身,其 [[Prototype]] ?? null——即 Object.getPrototypeOf(Object.prototype) === null。因此,一个典型的构造函数将构建以下原型链:\n
\n\n```\nfunction Constructor() {}\n\nconst obj = new Constructor();\n// obj ---> Constructor.prototype ---> Object.prototype ---> null\n\n```\n\n要构建更长的原型链,我们可用通过 Object.setPrototypeOf() 函数设置 Constructor.prototype ?? [[Prototype]]。\n\n```\nfunction Base() {}\nfunction Derived() {}\n// ?? `Derived.prototype` ?? `[[Prototype]]`\n// 设置?? `Base.prototype`\nObject.setPrototypeOf(Derived.prototype, Base.prototype);\n\nconst obj = new Derived();\n// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null\n\n```\n在类的术语中,这等同于使?? extends 语法。\n\n```\nclass Base {}\nclass Derived extends Base {}\n\nconst obj = new Derived();\n// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null\n```\n你可能还会看到一些使?? Object.create() 来构建继承链的旧代码。然而,因为这会重新?? prototype 属性赋值并删除 constructor 属性,所以更容易出错,而且如果构造函数还没有创建任何实例,性能提升可能并不明显。\n\n```\nfunction Base() {}\nfunction Derived() {}\n// ?? `Derived.prototype` 重新赋值为 `Base.prototype`,\n// 以作为其 `[[Prototype]]` 的新对象\n// 请不要这样做——使?? Object.setPrototypeOf 来修改它\nDerived.prototype = Object.create(Base.prototype);\n\n```\n\n#### 检查原型:更深入的研究\n让我们来仔细看看幕后发生了什么。\n\n如上所述,?? JavaScript 中,函数可以拥有属性。所有函数都有一个名?? prototype 的特殊属性。请注意,下面的代码是独立的(出于严谨,假设页面没有其他?? JavaScript 代码)。为获得最佳的学习体验,强烈建议你打开控制台,进入“console”标签页,复制并粘贴以下 JavaScript 代码,然后按回车键运行。(大多?? web 浏览器的开发者工具中都包含控制台。请参阅 Firefox 开发者工具、Chrome 开发者工具和 Edge 开发者工具,以了解详情。)
\n\n```\nfunction doSomething() {}\nconsole.log(doSomething.prototype);\n// 你如何声明函数并不重要;\n// JavaScript 中的函数总有一个默认的\n// 原型属性——有一个例外:\n// 箭头函数没有默认的原型属性:\nconst doSomethingFromArrowFunction = () => {};\nconsole.log(doSomethingFromArrowFunction.prototype);\n\n```\n如上所示,doSomething() 有一个默认的 prototype 属性(正如控制台所示)。运行这段代码后,控制台应该显示一个类似于下面的对象??
\n\n```\n{\n constructor: ? doSomething(),\n [[Prototype]]: {\n constructor: ? Object(),\n hasOwnProperty: ? hasOwnProperty(),\n isPrototypeOf: ? isPrototypeOf(),\n propertyIsEnumerable: ? propertyIsEnumerable(),\n toLocaleString: ? toLocaleString(),\n toString: ? toString(),\n valueOf: ? valueOf()\n }\n}\n```\n\n我们可以像下面这样,?? doSomething() 的原型添加属性。\n\n\n```\nfunction doSomething() {}\ndoSomething.prototype.foo = \"bar\";\nconsole.log(doSomething.prototype);\n\n```\n其结果为:\n\n\n```\n{\n foo: \"bar\",\n constructor: ? doSomething(),\n [[Prototype]]: {\n constructor: ? Object(),\n hasOwnProperty: ? hasOwnProperty(),\n isPrototypeOf: ? isPrototypeOf(),\n propertyIsEnumerable: ? propertyIsEnumerable(),\n toLocaleString: ? toLocaleString(),\n toString: ? toString(),\n valueOf: ? valueOf()\n }\n}\n```\n\n我们现在可以使用 new 运算符来创建基于该原型的 doSomething() 的实例。要使用 new 运算符,只需像往常一样调用函数,只是要在前面加上 new。使?? new 运算符调用函数会返回一个函数的实例对象。然后可以在该对象上添加属性。\n\n尝试以下代码:\n\n```\nfunction doSomething() {}\ndoSomething.prototype.foo = \"bar\"; // 向原型上添加一个属性\nconst doSomeInstancing = new doSomething();\ndoSomeInstancing.prop = \"some value\"; // 向该对象添加一个属性\nconsole.log(doSomeInstancing);\n\n```\n这会产生类似于下面的输出:\n\n\n```\n{\n prop: \"some value\",\n [[Prototype]]: {\n foo: \"bar\",\n constructor: ? doSomething(),\n [[Prototype]]: {\n constructor: ? Object(),\n hasOwnProperty: ? hasOwnProperty(),\n isPrototypeOf: ? isPrototypeOf(),\n propertyIsEnumerable: ? propertyIsEnumerable(),\n toLocaleString: ? toLocaleString(),\n toString: ? toString(),\n valueOf: ? valueOf()\n }\n }\n}\n\n```\n\n如上所示,doSomeInstancing ?? [[Prototype]] ?? doSomething.prototype。但是,这是做什么的呢?当你访问 doSomeInstancing 的属性时,运行时首先会查?? doSomeInstancing 是否有该属性??
\n\n如果 doSomeInstancing 没有该属性,那么运行时会?? doSomeInstancing.[[Prototype]](也就是 doSomething.prototype)中查找该属性。如?? doSomeInstancing.[[Prototype]] 有该属性,那么就会使用 doSomeInstancing.[[Prototype]] 上的该属性。\n
\n否则,如?? doSomeInstancing.[[Prototype]] 没有该属性,那么就会?? doSomeInstancing.[[Prototype]].[[Prototype]] 中查找该属性。默认情况下,任何函数的 prototype 属性的 [[Prototype]] 都是 Object.prototype。因此会?? doSomeInstancing.[[Prototype]].[[Prototype]](也就是 doSomething.prototype.[[Prototype]](也就是 Object.prototype))上查找该属性??
\n\n如果?? doSomeInstancing.[[Prototype]].[[Prototype]] 中没有找到该属性,那么就会?? doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]] 中查找该属性。但是,这里有一个问题:doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]] 不存在,因为 Object.prototype.[[Prototype]] ?? null。然后,只有在查找完整个 [[Prototype]] 链之后,运行时才会断言该属性不存在,并得出该属性的值为 undefined。\n
\n让我们在控制台中输入更多的代码:
\n\n```\nfunction doSomething() {}\ndoSomething.prototype.foo = \"bar\";\nconst doSomeInstancing = new doSomething();\ndoSomeInstancing.prop = \"some value\";\nconsole.log(\"doSomeInstancing.prop: \", doSomeInstancing.prop);\nconsole.log(\"doSomeInstancing.foo: \", doSomeInstancing.foo);\nconsole.log(\"doSomething.prop: \", doSomething.prop);\nconsole.log(\"doSomething.foo: \", doSomething.foo);\nconsole.log(\"doSomething.prototype.prop:\", doSomething.prototype.prop);\nconsole.log(\"doSomething.prototype.foo: \", doSomething.prototype.foo);\n\n```\n\n其结果如下:\n```\ndoSomeInstancing.prop: some value\ndoSomeInstancing.foo: bar\ndoSomething.prop: undefined\ndoSomething.foo: undefined\ndoSomething.prototype.prop: undefined\ndoSomething.prototype.foo: bar\n```\n\n#### 使用不同的方法来创建对象和改变原型链\n我们碰到过很多创建对象和改变其原型链的方法。我们将系统地总结不同的方法,并比较每种方法的优缺点。\n\n##### 使用语法结构创建对象\n```\nJS\nCopy to Clipboard\nconst o = { a: 1 };\n// 新创建的对象 o ?? Object.prototype 作为它的 [[Prototype]]\n// Object.prototype 的原型为 null。\n// o ---> Object.prototype ---> null\n\nconst b = [\"yo\", \"whadup\", \"?\"];\n// 数组继承?? Array.prototype(具?? indexOf、forEach 等方法)\n// 其原型链如下所示:\n// b ---> Array.prototype ---> Object.prototype ---> null\n\nfunction f() {\n return 2;\n}\n// 函数继承?? Function.prototype(具?? call、bind 等方法)\n// f ---> Function.prototype ---> Object.prototype ---> null\n\nconst p = { b: 2, __proto__: o };\n// 可以通过 __proto__ 字面量属性将新创建对象的\n// [[Prototype]] 指向另一个对象。\n// (不要与 Object.prototype.__proto__ 访问器混淆)\n// p ---> o ---> Object.prototype ---> null\n\n```\n\n在对象初始化器中使用 __proto__ 键的优缺点\n- 优点 被所有的现代引擎所支持。将 __proto__ 属性指向非对象的值只会被忽略,而非抛出异常。与 Object.prototype.__proto__ setter 相反,对象字面量初始化器中的 __proto__ 是标准化,被优化的。甚至可以比 Object.create 更高效。在创建对象时声明额外的自有属性比 Object.create 更符合习惯。\n- 缺点 不支?? IE10 及以下的版本。对于不了解其与 Object.prototype.__proto__ 访问器差异的人可能会将两者混淆。\n\n##### 使用构造函数\n```\nJS\nCopy to Clipboard\nfunction Graph() {\n this.vertices = [];\n this.edges = [];\n}\n\nGraph.prototype.addVertex = function (v) {\n this.vertices.push(v);\n};\n\nconst g = new Graph();\n// g 是一个带有自有属性“vertices”和“edges”的对象。\n// 在执?? new Graph() 时,g.[[Prototype]] ?? Graph.prototype 的值。\n```\n\n使用构造函数的优缺点\n- 优点 所有引擎都支持——一直到 IE 5.5。此外,其速度很快、非常标准,且极易被 JIT 优化。\n- 缺点 要使用这个方法,必须初始化该函数。在初始化过程中,构造函数可能会存储每一个对象都必须生成的唯一信息。这些唯一信息只会生成一次,可能会导致问题。\n构造函数的初始化过程可能会将不需要的方法放到对象上。\n这两者在实践中通常都不是问题。\n\n\n##### 使用 Object.create()\n调用 Object.create() 来创建一个新对象。该对象?? [[Prototype]] 是该函数的第一个参数:\n```\nJS\nCopy to Clipboard\nconst a = { a: 1 };\n// a ---> Object.prototype ---> null\n\nconst b = Object.create(a);\n// b ---> a ---> Object.prototype ---> null\nconsole.log(b.a); // 1 (inherited)\n\nconst c = Object.create(b);\n// c ---> b ---> a ---> Object.prototype ---> null\n\nconst d = Object.create(null);\n// d ---> null(d 是一个直接以 null 为原型的对象)\nconsole.log(d.hasOwnProperty);\n// undefined,因?? d 没有继承 Object.prototype\n```\nObject.create 的优缺点\n- 优点 被所有现代引擎所支持。允许在创建时直接设置对象的 [[Prototype]],这允许运行时进一步优化对象。还允许使用 Object.create(null) 创建没有原型的对象。\n- 缺点 不支?? IE8 及以下版本。但是,由于微软已经停止了对运行 IE8 及以下版本的系统的扩展支持,这对大多数应用程序而言应该不是问题。此外,如果使用了第二个参数,慢对象的初始化可能会成为性能瓶颈,因为每个对象描述符属性都有自己单独的描述符对象。当处理上万个对象描述符时,这种延时可能会成为一个严重的问题。\n\n\n##### 使用类\n```\nJS\nCopy to Clipboard\nclass Polygon {\n constructor(height, width) {\n this.height = height;\n this.width = width;\n }\n}\n\nclass Square extends Polygon {\n constructor(sideLength) {\n super(sideLength, sideLength);\n }\n\n get area() {\n return this.height * this.width;\n }\n\n set sideLength(newLength) {\n this.height = newLength;\n this.width = newLength;\n }\n}\n\nconst square = new Square(2);\n// square ---> Square.prototype ---> Polygon.prototype ---> Object.prototype ---> null\n```\n类的优缺点\n- 优点 被所有现代引擎所支持。非常高的可读性和可维护性。私有属性是原型继承中没有简单替代方案的特性。\n- 缺点 类,尤其是带有私有属性的类,比传统的类的性能要差(尽管引擎实现者正在努力改进这一点)。不支持旧环境,通常需要转译器才能在生产中使用类。\n\n##### 使用 Object.setPrototypeOf()\n虽然上面的所有方法都会在对象创建时设置原型链,但?? Object.setPrototypeOf() 允许修改现有对象?? [[Prototype]] 内部属性。\n```\nJS\nCopy to Clipboard\nconst obj = { a: 1 };\nconst anotherObj = { b: 2 };\nObject.setPrototypeOf(obj, anotherObj);\n// obj ---> anotherObj ---> Object.prototype ---> null\n```\nObject.setPrototypeOf 的优缺点\n- 优点 被所有现代引擎所支持。允许动态地修改对象的原型,甚至可以强制为使?? Object.create(null) 创建的无原型对象设置原型。\n- 缺点 性能不佳。如果可以在创建对象时设置原型,则应避免此方法。许多引擎会优化原型,并在调用实例时会尝试提前猜测方法在内存中的位置;但是动态设置原型会破坏这些优化。它可能会导致某些引擎重新编译你的代码以进行反优化,以使其按照规范工作。不支持 IE8 及以下版本。\n\n\n##### 使用 __proto__ 访问器\n所有对象都继承?? Object.prototype.__proto__ 访问器,它可以用来设置现有对象的 [[Prototype]](如果对象没有覆?? __proto__ 属性)。\n\n警告?? Object.prototype.__proto__ 访问器是非标准的,且已被弃用。你几乎总是应该使用 Object.setPrototypeOf 来代替。\n```\nJS\nCopy to Clipboard\nconst obj = {};\n// 请不要使用该方法:仅作为示例。\nobj.__proto__ = { barProp: \"bar val\" };\nobj.__proto__.__proto__ = { fooProp: \"foo val\" };\nconsole.log(obj.fooProp);\nconsole.log(obj.barProp);\n```\n设置 __proto__ 属性的优缺点\n- 优点 被所有现代引擎所支持。将 __proto__ 设置为非对象的值只会被忽略,而非抛出异常。\n- 缺点 性能不佳且已被弃用。许多引擎会优化原型,并在调用实例时会尝试提前猜测方法在内存中的位置;但是动态设置原型会破坏这些优化,甚至可能会导致某些引擎重新编译你的代码以进行反优化,以使其按照规范工作。不支持 IE10 及以下版本。__proto__ 访问器是规范中可选的特性,因此可能无法在所有平台上使用。你几乎总是应该使用 Object.setPrototypeOf 代替。\n\n\n##### 性能\n原型链上较深层的属性的查找时间可能会对性能产生负面影响,这在性能至关重要的代码中可能会非常明显。此外,尝试访问不存在的属性始终会遍历整个原型链。\n
\n此外,在遍历对象的属性时,原型链中的每个可枚举属性都将被枚举。要检查对象是否具有在其自身上定义的属性,而不是在其原型链上的某个地方,则有必要使?? hasOwnProperty ?? Object.hasOwn 方法。除 [[Prototype]] ?? null 的对象外,所有对象都?? Object.prototype 继承 hasOwnProperty——除非它已经在原型链的更深处被覆盖。我们将使用上面的图示例代码来说明它,具体如下:\n\n```\nJS\nCopy to Clipboard\nfunction Graph() {\n this.vertices = [];\n this.edges = [];\n}\n\nGraph.prototype.addVertex = function (v) {\n this.vertices.push(v);\n};\n\nconst g = new Graph();\n// g ---> Graph.prototype ---> Object.prototype ---> null\n\ng.hasOwnProperty(\"vertices\"); // true\nObject.hasOwn(g, \"vertices\"); // true\n\ng.hasOwnProperty(\"nope\"); // false\nObject.hasOwn(g, \"nope\"); // false\n\ng.hasOwnProperty(\"addVertex\"); // false\nObject.hasOwn(g, \"addVertex\"); // false\n\nObject.getPrototypeOf(g).hasOwnProperty(\"addVertex\"); // true\n注意:仅检查属性是否为 undefined 是不够的。该属性很可能存在,但其值恰好设置为 undefined。\n```\n\n#### 结论\n对于 Java ?? C++ 的开发者来说,JavaScript 可能有点令人困惑,因为它是完全动态、完全是在执行期间确定的,而且根本没有静态类型。一切都是对象(实例)或函数(构造函数),甚至函数本身也?? Function 构造函数的实例。即使是语法结构中的“类”也只是运行时的构造函数??
\n\nJavaScript 中的所有构造函数都有一个被称为 prototype 的特殊属性,它与 new 运算符一起使用。对原型对象的引用被复制到新实例的内部属?? [[Prototype]] 中。例如,当你执行 const a1 = new A() 时,JavaScript(在内存中创建对象之后,为其定义 this 并执?? A() 之前)设?? a1.[[Prototype]] = A.prototype。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接存在于该对象上,如果不存在,则?? [[Prototype]] 中查找。会递归查询 [[Prototype]],即 a1.doSomething、Object.getPrototypeOf(a1).doSomething、Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething,以此类推,直至找到?? Object.getPrototypeOf 返回 null。这意味着?? prototype 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 prototype 的部分内容,使得更改被应用到所有现有的实例中??
\n\n\n在上面的示例中,如果你执?? const a1 = new A(); const a2 = new A();,那?? a1.doSomething 实际上会引用 Object.getPrototypeOf(a1).doSomething——这与你定义?? A.prototype.doSomething 相同,即 Object.getPrototypeOf(a1).doSomething === Object.getPrototypeOf(a2).doSomething === A.prototype.doSomething??
\n\n了解原型继承模型是使用它编写复杂代码的重要基础。此外,要注意代码中原型链的长度,在必要时可以将其分解,以避免潜在的性能问题。此外,除非是为了与新的 JavaScript 特性兼容,否则永远不应扩展原生原型??
\n -->
内存管理 历史版本:
上次修改时间:
Javascript中判断对象是否具有属性的5种方?? 历史版本:
上次修改时间:
0条评�?
全部评论

关于博主

an actually real engineer

通信工程专业毕业,7年开发经验

技术栈:

精通c/c++

精通golang

熟悉常见的脚本,js,lua,python,php

熟悉电路基础,嵌入式,单片机

耕耘领域:

服务端开发

嵌入式开发

git

>

gin接口代码CURD生成工具

sql ddl to struct and markdown,将sql表自动化生成代码内对应的结构体和markdown表格格式,节省宝贵的时间。

输入ddl:
输出代码:

qt .ui文件转css文件

duilib xml 自动生成绑定控件代码

协议调试器

基于lua虚拟机的的协议调试器软件 支持的协议有:

串口

tcp客户端/服务端

udp 组播/udp节点

tcp websocket 客户端/服务端

软件界面

使用例子: 通过脚本来获得接收到的数据并写入文件和展示在界面上

下载地址和源码

duilib版本源码 qt qml版本源码 二进制包

webrtc easy demo

webrtc c++ native 库 demo 实现功能:

基于QT

webrtc摄像头/桌面捕获功能

opengl渲染/多播放窗格管理

janus meeting room

下载地址和源码

源码 二进制包

wifi,蓝牙 - 无线开关

实现功能:

通过wifi/蓝牙实现远程开关电器或者其他电子设备

电路原理图:

实物图:

深度学习验证工具

vtk+pcl 点云编辑工具

实现功能:

1. 点云文件加载显示(.pcd obj stl)

2. 点云重建

3. 点云三角化

4. 点云缩放

下载地址:

源码 二进制包

虚拟示波器

硬件实物图:

实现原理

基本性能

采集频率: 取决于外部adc模块和ebaz4205矿板的以太网接口速率,最高可以达到100M/8 约为12.5MPS

上位机实现功能: 采集,显示波形,存储wave文件。

参数可运行时配置

上位机:

显示缓冲区大小可调

刷新率可调节

触发显示刷新可调节

进程守护工具

基本功能:

1. 守护进程,被守护程序崩溃后自动重启。

2. 进程输出获取,显示在编辑框中。

二进制包

openblt 烧录工具

基本功能:

1. 加载openblt 文件,下载到具有openblt bootloader 运行的单片机中。

二进制包

opencv 功能验证工具(开源项目二次开发)

基本功能:

1. 插件化图像处理流程,支持自定义图像处理流程。 2. 完善的日志和权限管理

二进制包

又一个modbus调试工具

最近混迹物联网企业,发现目前缺少一个简易可用的modbus调试工具,本软件旨在为开发者提供一个简单modbus测试工具。
主打一个代码简单易修改。
特点:

1. 基于QT5

2. 基于libmodbus

3. 三方库完全跨平台,linux/windows。

二进制包

屏幕录制工具

1. 基于QT5

2. 基于ffmpeg

3. 支持自定义录屏

源代码

开源plutosdr 板卡

1. 完全开源

2. 提高固件定制服务

3. 硬件售价450 手焊产量有线

测试数据

内部DDS回环测试

接收测试

外部发送500MHZ FM波形

硬件原理图

matlab测试

2TRX版本

大部分plutosdr应用场景都是讲plutosdr板卡作为射频收发器来使用。
实际上plutosdr板卡本身运行linux 操作系统。是具有一定脱机运算的能力。 对于一些微型频谱检测,简单射频信号收发等应用完全可以将应用层直接实现在板卡上
相较于通过网卡或者USB口传输具有更稳定,带宽更高等优点。
本开源板卡由于了SD卡启动,较原版pluto支持了自定义启动应用的功能。
提供了应用层开发SDK(编译器,buildroot文件系统)。
通过usb连接电脑,经过RNDIS驱动可以近似为通过网卡连接
(支持固件的开发定制)。

二次开发例子

``` all:
arm-linux-gnueabihf-gcc -mfloat-abi=hard --sysroot=/root/v0.32_2trx/buildroot/output/staging -std=gnu99 -g -o pluto_stream ad9361-iiostream.c -lpthread -liio -lm -Wall -Wextra -lrt
clean:
rm pluto_stream

bsdiff算法补丁生成器

1. 官方bsdiff算法例子自带bzip压缩方式

2. 本例子没有压缩,直接生成补丁文件

3. 图形化界面基于DUILIB

二进制文件

版面分析即分析出图片内的具体文件元素,如文档标题,文档内容,文档页码等,本工具基于cnstd模型

Base64 Image

. 闽ICP备19002644号