let proto = {
whoami() { console.log('I am proto'); }
};
let obj = {
whoami() {
Object.getPrototypeOf( obj ).whoami.call( this ); // super.whoami();
console.log('I am obj');
}
};
Object.setPrototypeOf( obj, proto );
此代碼讓obj繼承proto。我對這段代碼感到困惑Object.getPrototypeOf( obj ).whoami.call( this );,它等同于super.whoami(). 誰能解釋這段代碼是如何作業的?
uj5u.com熱心網友回復:
在跳到解釋之前,您可能需要先閱讀下面我的回答中的以下主題:
- 原型
- (原型鏈)
- 屬性陰影
- 職能
- (純函式和非純函式)
- 閉包
this語境
原型
原型鏈
在 JavaScript 中,物件可以有原型。物件繼承其原型的所有屬性。由于原型本身就是物件,所以它們也可以有原型。這稱為原型鏈。
訪問物件的屬性將導致 JS 首先嘗試在物件本身上找到它,然后依次在其每個原型上查找。
例子
讓我們定義一個人和他們的原型human:
const human = { isSelfAware: true };
const person = { name: "john" };
Object.setPrototypeOf(person, human);
如果我們嘗試訪問person.name,那么 JS 將回傳 的屬性,person因為它已經定義了這個屬性。
但是,如果我們嘗試訪問person.isSelfAware,JS 將回傳 的屬性,human因為person沒有定義屬性,但它的原型有。
屬性陰影
如果一個物件具有與其原型之一類似名稱的屬性,則原型的屬性將被遮蔽,因為 JS 將回傳最早定義的屬性。
陰影很有用,因為物件可以在不影響原型的情況下定義不同的屬性值。
例子
const lightBulbProto = { serialNumber: 12345, isFunctional: true };
const oldLightBulb = { isFunctional: false };
const newLightBulb = {};
Object.setPrototypeOf(oldLightBulb , lightBulbProto);
Object.setPrototypeOf(newLightBulb, lightBulbProto);
默認情況下,所有燈泡都正常作業。但現在我們將舊燈泡定義為無功能 ( isFunctional: false)。
This doesn't affect newLightBulb because we didn't define the functionality on the prototype, but on the specific object oldLightBulb.
Functions
First off: By "function" I only mean functions and function expressions function() {}. Method definitions are syntactic sugar for function expressions, so they are considered the same. Arrow function expressions () => {} are excluded.
Types of functions
If a function is deterministic (same input results in same output) and self-enclosed (no side-effects), it is a pure function; otherwise it is an impure function.
Pure functions are usually easy to understand and debug, since all relevant code is contained in its definition.
const array = [];
function addToArray(o) { // Impure function
array.push(o);
}
function add(a, b) { // Pure function
return a b;
}
Scope and closures
JS tries to bundle your function with the smallest possible scope. If your function uses variables outside of its own scope, a closure is created at function creation during runtime. This means, the surrounding scope is kept alive for as long as the dependent function is accessible.
Closures aren't inherently bad, but may be confusing or cause of a memory-leak.
Example
createGet initializes object in its scope, and then returns a getter for the object:
function createGet() {
const object = {};
return function() {
return object;
}
}
let get1 = createGet();
let get2 = createGet();
// They are independent!
get1().name = "get1";
get2().number = 2;
console.log(get1()); // { "name": "get1" }
console.log(get2()); // { "number": 2 }
// Release the closures!
get1 = undefined;
get2 = undefined;
The code looks as if each getter function returns the same object. This is not true, because every call of createGet initializes a different object each time. Because of this, each getter function refers to a different object, making each getter independent.
Since closures may bundle a different scope at every creation, they may be a memory-leak. Only when no further reference to a closure exists, it may be garbage-collected. In our example we do this with these lines:
get1 = undefined;
get2 = undefined;
Strict-mode
Apart from the default "sloppy mode", JS also features a strict mode.
this context
The current context is effectively the value of this in the current scope.
The context of a function is determined by how the function is called. A function may be called standalone (e.g. aFunc()) or as a method (e.g. obj.aMethod()).
If a function is called on its own, it inherits the surrounding context. If there is no surrounding function context, the surrounding context will either be globalThis in "sloppy mode", or be undefined in strict mode.
If the function is called as a method obj.aMethod(), this will refer to obj.
Additionally, the first argument of the functions Function.prototype.bind, Function.prototype.call and Function.prototype.apply can specify the context of the underlying function.
Show code snippet
console.log("Global context:");
(function() {
console.log("- Sloppy: this == globalThis? " (this === globalThis));
// In browsers: globalThis == window
})();
(function() {
"use strict";
console.log("- Strict: this == undefined? " (this === undefined));
})();
const compareThis = function(o) {
return this === o;
};
const obj = { compareThis };
console.log("As function:");
console.log("- Default: this == obj? " compareThis(obj));
console.log("- With call(obj): this == obj? " compareThis.call(obj, obj));
console.log("As method:");
console.log("- Default: this == obj? " obj.compareThis(obj));
console.log("- With call(undefined): this == obj? " obj.compareThis.call(undefined, obj));
.as-console-wrapper{max-height:unset !important}
Finally: The explanation
First, two objects (obj and proto) are initialized. proto will be the prototype of obj.
Because both objects have the property whoami defined, the prototype's property will be shadowed. Since we still want to access the prototype's property, we have to access it directly. To do this, we first get the prototype of obj (Object.getPrototypeOf(obj), or "super"), and then access its property (.whoami, or "super.whoami").
Since whoami was originally called on obj, it would make sense to use obj as the context. For this reason, we call whoami.call(this) (with this equal to obj; effectively "super.whoami()") on the prototype's whoami method, because if we didn't the method would use the prototype as its context.
Because obj is part of the surrounding lexical scope instead of the scope of obj.whoami, whoami creates a closure because of it using the identifier obj in Object.getPrototypeOf(obj). Ideally we would get the prototype using this instead, to not have an unnecessary closure: Object.getPrototypeOf(this).
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/424211.html
標籤:javascript 目的 遗产 极好的
上一篇:如何強制執行嵌套類屬性?
